diff --git a/pkg/controllers/ttl/manager.go b/pkg/controllers/ttl/manager.go index fa76b2fa5b..4e496b4def 100644 --- a/pkg/controllers/ttl/manager.go +++ b/pkg/controllers/ttl/manager.go @@ -3,6 +3,7 @@ package ttl import ( "context" "fmt" + "sync" "time" "github.com/go-logr/logr" @@ -10,6 +11,10 @@ import ( "github.com/kyverno/kyverno/pkg/auth/checker" "github.com/kyverno/kyverno/pkg/controllers" "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/metrics" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" @@ -35,6 +40,8 @@ type manager struct { resController map[schema.GroupVersionResource]stopFunc logger logr.Logger interval time.Duration + lock sync.Mutex + infoMetric metric.Int64ObservableGauge } func NewManager( @@ -44,16 +51,30 @@ func NewManager( timeInterval time.Duration, ) controllers.Controller { logger := logging.WithName(ControllerName) - selfChecker := checker.NewSelfChecker(authorizationInterface.SelfSubjectAccessReviews()) - resController := map[schema.GroupVersionResource]stopFunc{} - return &manager{ + meterProvider := otel.GetMeterProvider() + meter := meterProvider.Meter(metrics.MeterName) + infoMetric, err := meter.Int64ObservableGauge( + "kyverno_ttl_controller_info", + metric.WithDescription("can be used to track individual resource controllers running for ttl based cleanup"), + ) + if err != nil { + logger.Error(err, "Failed to create instrument, kyverno_ttl_controller_info") + } + mgr := &manager{ metadataClient: metadataInterface, discoveryClient: discoveryInterface, - checker: selfChecker, - resController: resController, + checker: checker.NewSelfChecker(authorizationInterface.SelfSubjectAccessReviews()), + resController: map[schema.GroupVersionResource]stopFunc{}, logger: logger, interval: timeInterval, + infoMetric: infoMetric, } + if infoMetric != nil { + if _, err := meter.RegisterCallback(mgr.report, infoMetric); err != nil { + logger.Error(err, "failed to register callback") + } + } + return mgr } func (m *manager) Run(ctx context.Context, worker int) { @@ -168,6 +189,23 @@ func (m *manager) filterPermissionsResource(resources []schema.GroupVersionResou return validResources } +func (m *manager) report(ctx context.Context, observer metric.Observer) error { + m.lock.Lock() + defer m.lock.Unlock() + for gvr := range m.resController { + observer.ObserveInt64( + m.infoMetric, + 1, + metric.WithAttributes( + attribute.String("resource_group", gvr.Group), + attribute.String("resource_version", gvr.Version), + attribute.String("resource_resource", gvr.Resource), + ), + ) + } + return nil +} + func (m *manager) reconcile(ctx context.Context, workers int) error { defer m.logger.V(3).Info("manager reconciliation done") m.logger.V(3).Info("beginning reconciliation", "interval", m.interval) @@ -179,6 +217,8 @@ func (m *manager) reconcile(ctx context.Context, workers int) error { if err != nil { return err } + m.lock.Lock() + defer m.lock.Unlock() for gvr := range observedState.Difference(desiredState) { if err := m.stop(ctx, gvr); err != nil { return err