package cleanup import ( "context" "time" "github.com/go-logr/logr" kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1" kyvernov2alpha1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v2alpha1" "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/config" enginecontext "github.com/kyverno/kyverno/pkg/engine/context" controllerutils "github.com/kyverno/kyverno/pkg/utils/controller" "github.com/kyverno/kyverno/pkg/utils/match" "go.uber.org/multierr" "k8s.io/apimachinery/pkg/util/sets" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" ) type handlers struct { client dclient.Interface cpolLister kyvernov2alpha1listers.ClusterCleanupPolicyLister polLister kyvernov2alpha1listers.CleanupPolicyLister nsLister corev1listers.NamespaceLister } func New( client dclient.Interface, cpolLister kyvernov2alpha1listers.ClusterCleanupPolicyLister, polLister kyvernov2alpha1listers.CleanupPolicyLister, nsLister corev1listers.NamespaceLister, ) *handlers { return &handlers{ client: client, cpolLister: cpolLister, polLister: polLister, nsLister: nsLister, } } func (h *handlers) Cleanup(ctx context.Context, logger logr.Logger, name string, _ time.Time, cfg config.Configuration) error { logger.Info("cleaning up...") defer logger.Info("done") namespace, name, err := cache.SplitMetaNamespaceKey(name) if err != nil { return err } policy, err := h.lookupPolicy(namespace, name) if err != nil { return err } return h.executePolicy(ctx, logger, policy, cfg) } func (h *handlers) lookupPolicy(namespace, name string) (kyvernov2alpha1.CleanupPolicyInterface, error) { if namespace == "" { return h.cpolLister.Get(name) } else { return h.polLister.CleanupPolicies(namespace).Get(name) } } func (h *handlers) executePolicy(ctx context.Context, logger logr.Logger, policy kyvernov2alpha1.CleanupPolicyInterface, cfg config.Configuration) error { spec := policy.GetSpec() kinds := sets.New(spec.MatchResources.GetKinds()...) debug := logger.V(4) var errs []error for kind := range kinds { debug := debug.WithValues("kind", kind) debug.Info("processing...") list, err := h.client.ListResource(ctx, "", kind, policy.GetNamespace(), nil) if err != nil { debug.Error(err, "failed to list resources") errs = append(errs, err) } else { for i := range list.Items { resource := list.Items[i] namespace := resource.GetNamespace() name := resource.GetName() debug := debug.WithValues("name", name, "namespace", namespace) if !controllerutils.IsManagedByKyverno(&resource) { var nsLabels map[string]string if namespace != "" { ns, err := h.nsLister.Get(namespace) if err != nil { debug.Error(err, "failed to get namespace labels") errs = append(errs, err) } nsLabels = ns.GetLabels() } // match namespaces if err := match.CheckNamespace(policy.GetNamespace(), resource); err != nil { debug.Info("resource namespace didn't match policy namespace", "result", err) } // match resource with match/exclude clause matched := match.CheckMatchesResources(resource, spec.MatchResources, nsLabels, nil, "") if matched != nil { debug.Info("resource/match didn't match", "result", matched) continue } if spec.ExcludeResources != nil { excluded := match.CheckMatchesResources(resource, *spec.ExcludeResources, nsLabels, nil, "") if excluded == nil { debug.Info("resource/exclude matched") continue } else { debug.Info("resource/exclude didn't match", "result", excluded) } } // check conditions if spec.Conditions != nil { enginectx := enginecontext.NewContext() if err := enginectx.AddResource(resource.Object); err != nil { debug.Error(err, "failed to add resource in context") errs = append(errs, err) continue } if err := enginectx.AddNamespace(resource.GetNamespace()); err != nil { debug.Error(err, "failed to add namespace in context") errs = append(errs, err) continue } if err := enginectx.AddImageInfos(&resource, cfg); err != nil { debug.Error(err, "failed to add image infos in context") errs = append(errs, err) continue } passed, err := checkAnyAllConditions(logger, enginectx, *spec.Conditions) if err != nil { debug.Error(err, "failed to check condition") errs = append(errs, err) continue } if !passed { debug.Info("conditions did not pass") continue } } debug.Info("resource matched, it will be deleted...") if err := h.client.DeleteResource(ctx, resource.GetAPIVersion(), resource.GetKind(), namespace, name, false); err != nil { debug.Error(err, "failed to delete resource") errs = append(errs, err) } else { debug.Info("deleted") } } } } } return multierr.Combine(errs...) }