package certmanager import ( "context" "time" "github.com/go-logr/logr" "github.com/kyverno/kyverno/pkg/controllers" "github.com/kyverno/kyverno/pkg/tls" controllerutils "github.com/kyverno/kyverno/pkg/utils/controller" retryutils "github.com/kyverno/kyverno/pkg/utils/retry" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" corev1informers "k8s.io/client-go/informers/core/v1" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/util/workqueue" ) const ( // Workers is the number of workers for this controller Workers = 1 ControllerName = "certmanager-controller" maxRetries = 10 ) type controller struct { renewer tls.CertRenewer // listers caLister corev1listers.SecretLister tlsLister corev1listers.SecretLister // queue queue workqueue.TypedRateLimitingInterface[any] caEnqueue controllerutils.EnqueueFunc tlsEnqueue controllerutils.EnqueueFunc caSecretName string tlsSecretName string namespace string } func NewController( caInformer corev1informers.SecretInformer, tlsInformer corev1informers.SecretInformer, certRenewer tls.CertRenewer, caSecretName string, tlsSecretName string, namespace string, ) controllers.Controller { queue := workqueue.NewTypedRateLimitingQueueWithConfig( workqueue.DefaultTypedControllerRateLimiter[any](), workqueue.TypedRateLimitingQueueConfig[any]{Name: ControllerName}, ) caEnqueue, _, _ := controllerutils.AddDefaultEventHandlers(logger, caInformer.Informer(), queue) tlsEnqueue, _, _ := controllerutils.AddDefaultEventHandlers(logger, tlsInformer.Informer(), queue) c := controller{ renewer: certRenewer, caLister: caInformer.Lister(), tlsLister: tlsInformer.Lister(), queue: queue, caEnqueue: caEnqueue, tlsEnqueue: tlsEnqueue, caSecretName: caSecretName, tlsSecretName: tlsSecretName, namespace: namespace, } return &c } func (c *controller) Run(ctx context.Context, workers int) { // we need to enqueue our secrets in case they don't exist yet in the cluster // this way we ensure the reconcile happens (hence renewal/creation) if err := c.tlsEnqueue(&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: c.namespace, Name: c.tlsSecretName, }, }); err != nil { logger.Error(err, "failed to enqueue secret", "name", c.tlsSecretName) } if err := c.caEnqueue(&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: c.namespace, Name: c.caSecretName, }, }); err != nil { logger.Error(err, "failed to enqueue CA secret", "name", c.caSecretName) } controllerutils.Run(ctx, logger, ControllerName, time.Second, c.queue, workers, maxRetries, c.reconcile, c.ticker) } func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, namespace, name string) error { if namespace != c.namespace { return nil } if name != c.caSecretName && name != c.tlsSecretName { return nil } return c.renewCertificates(ctx) } func (c *controller) ticker(ctx context.Context, logger logr.Logger) { certsRenewalTicker := time.NewTicker(tls.CertRenewalInterval) defer certsRenewalTicker.Stop() for { select { case <-certsRenewalTicker.C: { list, err := c.caLister.List(labels.Everything()) if err == nil { for _, secret := range list { if err := c.caEnqueue(secret); err != nil { logger.Error(err, "failed to enqueue secret", "name", secret.Name) } } } else { logger.Error(err, "falied to list secrets") } } { list, err := c.tlsLister.List(labels.Everything()) if err == nil { for _, secret := range list { if err := c.tlsEnqueue(secret); err != nil { logger.Error(err, "failed to enqueue secret", "name", secret.Name) } } } else { logger.Error(err, "falied to list secrets") } } case <-ctx.Done(): return } } } func (c *controller) renewCertificates(ctx context.Context) error { if err := retryutils.RetryFunc(ctx, time.Second, 5*time.Second, logger, "failed to renew CA", c.renewer.RenewCA)(); err != nil { return err } if err := retryutils.RetryFunc(ctx, time.Second, 5*time.Second, logger, "failed to renew TLS", c.renewer.RenewTLS)(); err != nil { return err } return nil }