2022-12-07 11:30:47 +01:00
|
|
|
package cleanup
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-logr/logr"
|
2023-06-26 16:57:23 +02:00
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
2023-01-08 01:48:44 +01:00
|
|
|
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
2022-12-08 12:45:47 +01:00
|
|
|
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
|
|
|
|
kyvernov2alpha1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v2alpha1"
|
2022-12-07 11:30:47 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
2023-01-02 18:14:40 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/config"
|
2023-04-20 12:36:13 +05:30
|
|
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
2022-12-09 11:24:04 +01:00
|
|
|
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
2023-06-26 16:57:23 +02:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine/factories"
|
2023-04-13 13:29:40 +02:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
2023-01-12 16:25:25 +08:00
|
|
|
"github.com/kyverno/kyverno/pkg/event"
|
2023-08-07 01:24:52 +05:30
|
|
|
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
2023-02-27 14:02:20 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/metrics"
|
2022-12-07 11:30:47 +01:00
|
|
|
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
|
2023-01-05 11:53:44 +05:30
|
|
|
"github.com/kyverno/kyverno/pkg/utils/match"
|
2023-06-19 11:09:08 +02:00
|
|
|
"go.opentelemetry.io/otel"
|
2023-02-27 14:02:20 +01:00
|
|
|
"go.opentelemetry.io/otel/attribute"
|
2023-05-11 12:16:48 +02:00
|
|
|
"go.opentelemetry.io/otel/metric"
|
2022-12-08 11:31:28 +01:00
|
|
|
"go.uber.org/multierr"
|
2022-12-07 11:30:47 +01:00
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
2022-12-08 11:31:28 +01:00
|
|
|
corev1listers "k8s.io/client-go/listers/core/v1"
|
2022-12-07 11:30:47 +01:00
|
|
|
"k8s.io/client-go/tools/cache"
|
|
|
|
)
|
|
|
|
|
|
|
|
type handlers struct {
|
|
|
|
client dclient.Interface
|
2022-12-08 12:45:47 +01:00
|
|
|
cpolLister kyvernov2alpha1listers.ClusterCleanupPolicyLister
|
|
|
|
polLister kyvernov2alpha1listers.CleanupPolicyLister
|
2022-12-08 11:31:28 +01:00
|
|
|
nsLister corev1listers.NamespaceLister
|
2023-06-26 16:57:23 +02:00
|
|
|
cmResolver engineapi.ConfigmapResolver
|
2023-07-18 13:01:09 +03:00
|
|
|
eventGen event.Interface
|
2023-04-13 13:29:40 +02:00
|
|
|
jp jmespath.Interface
|
2023-02-27 14:02:20 +01:00
|
|
|
metrics cleanupMetrics
|
|
|
|
}
|
|
|
|
|
|
|
|
type cleanupMetrics struct {
|
2023-05-11 12:16:48 +02:00
|
|
|
deletedObjectsTotal metric.Int64Counter
|
|
|
|
cleanupFailuresTotal metric.Int64Counter
|
2023-02-27 14:02:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func newCleanupMetrics(logger logr.Logger) cleanupMetrics {
|
2023-06-19 11:09:08 +02:00
|
|
|
meter := otel.GetMeterProvider().Meter(metrics.MeterName)
|
2023-02-27 14:02:20 +01:00
|
|
|
deletedObjectsTotal, err := meter.Int64Counter(
|
2023-05-15 17:54:16 +02:00
|
|
|
"kyverno_cleanup_controller_deletedobjects",
|
2023-05-11 12:16:48 +02:00
|
|
|
metric.WithDescription("can be used to track number of deleted objects."),
|
2023-02-27 14:02:20 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err, "Failed to create instrument, cleanup_controller_deletedobjects_total")
|
|
|
|
}
|
|
|
|
cleanupFailuresTotal, err := meter.Int64Counter(
|
2023-05-15 17:54:16 +02:00
|
|
|
"kyverno_cleanup_controller_errors",
|
2023-05-11 12:16:48 +02:00
|
|
|
metric.WithDescription("can be used to track number of cleanup failures."),
|
2023-02-27 14:02:20 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err, "Failed to create instrument, cleanup_controller_errors_total")
|
|
|
|
}
|
|
|
|
return cleanupMetrics{
|
|
|
|
deletedObjectsTotal: deletedObjectsTotal,
|
|
|
|
cleanupFailuresTotal: cleanupFailuresTotal,
|
|
|
|
}
|
2022-12-07 11:30:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func New(
|
2023-04-13 13:29:40 +02:00
|
|
|
logger logr.Logger,
|
2022-12-07 11:30:47 +01:00
|
|
|
client dclient.Interface,
|
2022-12-08 12:45:47 +01:00
|
|
|
cpolLister kyvernov2alpha1listers.ClusterCleanupPolicyLister,
|
|
|
|
polLister kyvernov2alpha1listers.CleanupPolicyLister,
|
2022-12-08 11:31:28 +01:00
|
|
|
nsLister corev1listers.NamespaceLister,
|
2023-06-26 16:57:23 +02:00
|
|
|
cmResolver engineapi.ConfigmapResolver,
|
2023-04-13 13:29:40 +02:00
|
|
|
jp jmespath.Interface,
|
2023-07-18 13:01:09 +03:00
|
|
|
eventGen event.Interface,
|
2022-12-07 11:30:47 +01:00
|
|
|
) *handlers {
|
|
|
|
return &handlers{
|
|
|
|
client: client,
|
|
|
|
cpolLister: cpolLister,
|
|
|
|
polLister: polLister,
|
2022-12-08 11:31:28 +01:00
|
|
|
nsLister: nsLister,
|
2023-06-26 16:57:23 +02:00
|
|
|
cmResolver: cmResolver,
|
2023-07-18 13:01:09 +03:00
|
|
|
eventGen: eventGen,
|
2023-02-27 14:02:20 +01:00
|
|
|
metrics: newCleanupMetrics(logger),
|
2023-04-13 13:29:40 +02:00
|
|
|
jp: jp,
|
2022-12-07 11:30:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-02 18:14:40 +01:00
|
|
|
func (h *handlers) Cleanup(ctx context.Context, logger logr.Logger, name string, _ time.Time, cfg config.Configuration) error {
|
2022-12-07 11:30:47 +01:00
|
|
|
logger.Info("cleaning up...")
|
2022-12-09 11:24:04 +01:00
|
|
|
defer logger.Info("done")
|
2022-12-07 11:30:47 +01:00
|
|
|
namespace, name, err := cache.SplitMetaNamespaceKey(name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
policy, err := h.lookupPolicy(namespace, name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-01-02 18:14:40 +01:00
|
|
|
return h.executePolicy(ctx, logger, policy, cfg)
|
2022-12-07 11:30:47 +01:00
|
|
|
}
|
|
|
|
|
2022-12-08 12:45:47 +01:00
|
|
|
func (h *handlers) lookupPolicy(namespace, name string) (kyvernov2alpha1.CleanupPolicyInterface, error) {
|
2022-12-07 11:30:47 +01:00
|
|
|
if namespace == "" {
|
|
|
|
return h.cpolLister.Get(name)
|
|
|
|
} else {
|
|
|
|
return h.polLister.CleanupPolicies(namespace).Get(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-27 09:58:50 -07:00
|
|
|
func (h *handlers) executePolicy(
|
|
|
|
ctx context.Context,
|
|
|
|
logger logr.Logger,
|
|
|
|
policy kyvernov2alpha1.CleanupPolicyInterface,
|
|
|
|
cfg config.Configuration,
|
|
|
|
) error {
|
2022-12-07 11:30:47 +01:00
|
|
|
spec := policy.GetSpec()
|
2022-12-21 23:33:51 +01:00
|
|
|
kinds := sets.New(spec.MatchResources.GetKinds()...)
|
2023-01-04 17:03:56 +08:00
|
|
|
debug := logger.V(4)
|
2022-12-08 11:31:28 +01:00
|
|
|
var errs []error
|
2023-06-27 09:58:50 -07:00
|
|
|
|
2023-04-20 12:36:13 +05:30
|
|
|
enginectx := enginecontext.NewContext(h.jp)
|
2023-06-27 09:58:50 -07:00
|
|
|
ctxFactory := factories.DefaultContextLoaderFactory(h.cmResolver)
|
|
|
|
|
|
|
|
loader := ctxFactory(nil, kyvernov1.Rule{})
|
|
|
|
if err := loader.Load(
|
|
|
|
ctx,
|
|
|
|
h.jp,
|
|
|
|
h.client,
|
|
|
|
nil,
|
2023-08-07 01:24:52 +05:30
|
|
|
imageverifycache.DisabledImageVerifyCache(),
|
2023-06-27 09:58:50 -07:00
|
|
|
spec.Context,
|
|
|
|
enginectx,
|
|
|
|
); err != nil {
|
2023-06-26 16:57:23 +02:00
|
|
|
return err
|
2023-04-20 12:36:13 +05:30
|
|
|
}
|
2023-06-27 09:58:50 -07:00
|
|
|
|
2022-12-07 11:30:47 +01:00
|
|
|
for kind := range kinds {
|
2023-02-27 14:02:20 +01:00
|
|
|
commonLabels := []attribute.KeyValue{
|
|
|
|
attribute.String("policy_type", policy.GetKind()),
|
|
|
|
attribute.String("policy_namespace", policy.GetNamespace()),
|
|
|
|
attribute.String("policy_name", policy.GetName()),
|
|
|
|
attribute.String("resource_kind", kind),
|
|
|
|
}
|
2022-12-09 11:24:04 +01:00
|
|
|
debug := debug.WithValues("kind", kind)
|
|
|
|
debug.Info("processing...")
|
2022-12-07 11:30:47 +01:00
|
|
|
list, err := h.client.ListResource(ctx, "", kind, policy.GetNamespace(), nil)
|
|
|
|
if err != nil {
|
2022-12-09 11:24:04 +01:00
|
|
|
debug.Error(err, "failed to list resources")
|
2022-12-08 11:31:28 +01:00
|
|
|
errs = append(errs, err)
|
2023-02-27 14:02:20 +01:00
|
|
|
if h.metrics.cleanupFailuresTotal != nil {
|
2023-05-11 12:16:48 +02:00
|
|
|
h.metrics.cleanupFailuresTotal.Add(ctx, 1, metric.WithAttributes(commonLabels...))
|
2023-02-27 14:02:20 +01:00
|
|
|
}
|
2022-12-08 11:31:28 +01:00
|
|
|
} else {
|
|
|
|
for i := range list.Items {
|
|
|
|
resource := list.Items[i]
|
|
|
|
namespace := resource.GetNamespace()
|
|
|
|
name := resource.GetName()
|
2022-12-09 11:24:04 +01:00
|
|
|
debug := debug.WithValues("name", name, "namespace", namespace)
|
2022-12-08 11:31:28 +01:00
|
|
|
if !controllerutils.IsManagedByKyverno(&resource) {
|
|
|
|
var nsLabels map[string]string
|
|
|
|
if namespace != "" {
|
|
|
|
ns, err := h.nsLister.Get(namespace)
|
|
|
|
if err != nil {
|
2022-12-09 11:24:04 +01:00
|
|
|
debug.Error(err, "failed to get namespace labels")
|
2022-12-08 11:31:28 +01:00
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
|
|
|
nsLabels = ns.GetLabels()
|
|
|
|
}
|
|
|
|
// match namespaces
|
2022-12-16 04:13:14 -05:00
|
|
|
if err := match.CheckNamespace(policy.GetNamespace(), resource); err != nil {
|
2022-12-09 11:24:04 +01:00
|
|
|
debug.Info("resource namespace didn't match policy namespace", "result", err)
|
2022-12-08 11:31:28 +01:00
|
|
|
}
|
|
|
|
// match resource with match/exclude clause
|
2023-01-08 01:48:44 +01:00
|
|
|
matched := match.CheckMatchesResources(
|
|
|
|
resource,
|
|
|
|
spec.MatchResources,
|
|
|
|
nsLabels,
|
|
|
|
// TODO(eddycharly): we don't have user info here, we should check that
|
|
|
|
// we don't have user conditions in the policy rule
|
|
|
|
kyvernov1beta1.RequestInfo{},
|
2023-03-22 11:18:11 +01:00
|
|
|
resource.GroupVersionKind(),
|
|
|
|
"",
|
2023-01-08 01:48:44 +01:00
|
|
|
)
|
2022-12-08 11:31:28 +01:00
|
|
|
if matched != nil {
|
2022-12-09 11:24:04 +01:00
|
|
|
debug.Info("resource/match didn't match", "result", matched)
|
2022-12-08 11:31:28 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if spec.ExcludeResources != nil {
|
2023-01-08 01:48:44 +01:00
|
|
|
excluded := match.CheckMatchesResources(
|
|
|
|
resource,
|
|
|
|
*spec.ExcludeResources,
|
|
|
|
nsLabels,
|
|
|
|
// TODO(eddycharly): we don't have user info here, we should check that
|
|
|
|
// we don't have user conditions in the policy rule
|
|
|
|
kyvernov1beta1.RequestInfo{},
|
2023-03-22 11:18:11 +01:00
|
|
|
resource.GroupVersionKind(),
|
|
|
|
"",
|
2023-01-08 01:48:44 +01:00
|
|
|
)
|
2022-12-08 11:31:28 +01:00
|
|
|
if excluded == nil {
|
2022-12-09 11:24:04 +01:00
|
|
|
debug.Info("resource/exclude matched")
|
2022-12-08 11:31:28 +01:00
|
|
|
continue
|
|
|
|
} else {
|
2022-12-09 11:24:04 +01:00
|
|
|
debug.Info("resource/exclude didn't match", "result", excluded)
|
2022-12-08 11:31:28 +01:00
|
|
|
}
|
|
|
|
}
|
2022-12-09 11:24:04 +01:00
|
|
|
// check conditions
|
|
|
|
if spec.Conditions != nil {
|
2023-04-20 12:36:13 +05:30
|
|
|
enginectx.Reset()
|
2023-05-12 17:43:36 +02:00
|
|
|
if err := enginectx.SetTargetResource(resource.Object); err != nil {
|
2022-12-09 11:24:04 +01:00
|
|
|
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
|
|
|
|
}
|
2023-01-02 18:14:40 +01:00
|
|
|
if err := enginectx.AddImageInfos(&resource, cfg); err != nil {
|
2022-12-09 11:24:04 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2023-02-27 14:02:20 +01:00
|
|
|
var labels []attribute.KeyValue
|
|
|
|
labels = append(labels, commonLabels...)
|
|
|
|
labels = append(labels, attribute.String("resource_namespace", namespace))
|
2023-01-12 01:23:06 +08:00
|
|
|
logger.WithValues("name", name, "namespace", namespace).Info("resource matched, it will be deleted...")
|
2022-12-08 11:31:28 +01:00
|
|
|
if err := h.client.DeleteResource(ctx, resource.GetAPIVersion(), resource.GetKind(), namespace, name, false); err != nil {
|
2023-02-27 14:02:20 +01:00
|
|
|
if h.metrics.cleanupFailuresTotal != nil {
|
2023-05-11 12:16:48 +02:00
|
|
|
h.metrics.cleanupFailuresTotal.Add(ctx, 1, metric.WithAttributes(labels...))
|
2023-02-27 14:02:20 +01:00
|
|
|
}
|
2022-12-09 11:24:04 +01:00
|
|
|
debug.Error(err, "failed to delete resource")
|
2022-12-08 11:31:28 +01:00
|
|
|
errs = append(errs, err)
|
2023-07-18 13:01:09 +03:00
|
|
|
e := event.NewCleanupPolicyEvent(policy, resource, err)
|
|
|
|
h.eventGen.Add(e)
|
2022-12-09 11:24:04 +01:00
|
|
|
} else {
|
2023-02-27 14:02:20 +01:00
|
|
|
if h.metrics.deletedObjectsTotal != nil {
|
2023-05-11 12:16:48 +02:00
|
|
|
h.metrics.deletedObjectsTotal.Add(ctx, 1, metric.WithAttributes(labels...))
|
2023-02-27 14:02:20 +01:00
|
|
|
}
|
2022-12-09 11:24:04 +01:00
|
|
|
debug.Info("deleted")
|
2023-07-18 13:01:09 +03:00
|
|
|
e := event.NewCleanupPolicyEvent(policy, resource, nil)
|
|
|
|
h.eventGen.Add(e)
|
2022-12-08 11:31:28 +01:00
|
|
|
}
|
|
|
|
}
|
2022-12-07 11:30:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-08 11:31:28 +01:00
|
|
|
return multierr.Combine(errs...)
|
2022-12-07 11:30:47 +01:00
|
|
|
}
|