def prune(client, keep_resources:, skip_forbidden: true)
      # using skip_forbidden: assume we can't create resource types that we are forbidden to list, so we don't need to prune them either
      client.list_resources(labelSelector: { @label => name }, skip_forbidden: skip_forbidden).sort do |a, b|
        # Sort resources so that namespaced objects are deleted first
        if a.metadata.namespace == b.metadata.namespace
          0
        elsif a.metadata.namespace.nil? && !b.metadata.namespace.nil?
          1
        else
          -1
        end
      end.each do |resource|
        next if PRUNE_IGNORE.include? "#{resource.apiVersion}:#{resource.kind}"

        resource_label = resource.metadata.labels ? resource.metadata.labels[@label] : nil
        resource_checksum = resource.metadata.annotations ? resource.metadata.annotations[@checksum_annotation] : nil

        logger.debug { "List resource #{resource.apiVersion}:#{resource.kind}/#{resource.metadata.name} in namespace #{resource.metadata.namespace} with checksum=#{resource_checksum}" }

        if resource_label != name
          # apiserver did not respect labelSelector
        elsif keep_resources && keep_resource?(resource)
          # resource is up-to-date
        else
          logger.info "Delete resource #{resource.apiVersion}:#{resource.kind}/#{resource.metadata.name} in namespace #{resource.metadata.namespace}"
          begin
            client.delete_resource(resource, propagationPolicy: 'Background')
          rescue K8s::Error::NotFound => e
            # assume aliased objects in multiple API groups, like for Deployments
            # alternatively, a custom resource whose definition was already deleted earlier
            logger.debug { "Ignoring #{e} : #{e.message}" }
          end
        end
      end
    end
def prune(client, keep_resources:, skip_forbidden: true)
      # using skip_forbidden: assume we can't create resource types that we are forbidden to list, so we don't need to prune them either
      client.list_resources(labelSelector: { @label => name }, skip_forbidden: skip_forbidden).sort do |a, b|
        # Sort resources so that namespaced objects are deleted first
        if a.metadata.namespace == b.metadata.namespace
          0
        elsif a.metadata.namespace.nil? && !b.metadata.namespace.nil?
          1
        else
          -1
        end
      end.each do |resource|
        next if PRUNE_IGNORE.include? "#{resource.apiVersion}:#{resource.kind}"

        resource_label = resource.metadata.labels ? resource.metadata.labels[@label] : nil
        resource_checksum = resource.metadata.annotations ? resource.metadata.annotations[@checksum_annotation] : nil

        logger.debug { "List resource #{resource.apiVersion}:#{resource.kind}/#{resource.metadata.name} in namespace #{resource.metadata.namespace} with checksum=#{resource_checksum}" }

        if resource_label != name
          # apiserver did not respect labelSelector
        elsif keep_resources && keep_resource?(resource)
          # resource is up-to-date
        else
          logger.info "Delete resource #{resource.apiVersion}:#{resource.kind}/#{resource.metadata.name} in namespace #{resource.metadata.namespace}"
          begin
            client.delete_resource(resource, propagationPolicy: 'Background')
          rescue K8s::Error::NotFound => e
            # assume aliased objects in multiple API groups, like for Deployments
            # alternatively, a custom resource whose definition was already deleted earlier
            logger.debug { "Ignoring #{e} : #{e.message}" }
          end
        end
      end
    end
Delete all stack resources that were not applied
 @param client [K8s::Client]
 @param keep_resources [NilClass, Boolean]
 @param skip_forbidden [Boolean]