From 4dfefb0a946ef4fb0a6bfbc1d02fa12e338e3487 Mon Sep 17 00:00:00 2001 From: Alvaro Aleman Date: Mon, 11 Aug 2025 11:15:21 -0400 Subject: [PATCH] :seedling: Certwatcher: Don't require leaderelection Controller-Runtime starts the certwatcher in the webhook server, which means it always runs regardless of the current replica being leader or not. It turns out that kubebuilder adds it to the manager and as a result, it only runs on leader replicas. Make it a `LeaderElectionRunnable` and don't require leader election so that it will work correctly even if other projects use it in ways that were not originally anticipated. --- pkg/certwatcher/certwatcher.go | 6 ++++++ pkg/certwatcher/certwatcher_test.go | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/pkg/certwatcher/certwatcher.go b/pkg/certwatcher/certwatcher.go index c323240982..1cb1168373 100644 --- a/pkg/certwatcher/certwatcher.go +++ b/pkg/certwatcher/certwatcher.go @@ -240,3 +240,9 @@ func (cw *CertWatcher) handleEvent(event fsnotify.Event) { log.Error(err, "error re-reading certificate") } } + +// NeedLeaderElection indicates that the cert-manager +// does not need leader election. +func (cw *CertWatcher) NeedLeaderElection() bool { + return false +} diff --git a/pkg/certwatcher/certwatcher_test.go b/pkg/certwatcher/certwatcher_test.go index 5579c3fdad..9737925a6b 100644 --- a/pkg/certwatcher/certwatcher_test.go +++ b/pkg/certwatcher/certwatcher_test.go @@ -37,6 +37,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/certwatcher/metrics" + "sigs.k8s.io/controller-runtime/pkg/manager" ) var _ = Describe("CertWatcher", func() { @@ -92,6 +93,12 @@ var _ = Describe("CertWatcher", func() { } }) + It("should not require LeaderElection", func() { + leaderElectionRunnable, isLeaderElectionRunnable := any(watcher).(manager.LeaderElectionRunnable) + Expect(isLeaderElectionRunnable).To(BeTrue()) + Expect(leaderElectionRunnable.NeedLeaderElection()).To(BeFalse()) + }) + It("should read the initial cert/key", func() { // This test verifies the initial read succeeded. So interval doesn't matter. doneCh := startWatcher(10 * time.Second)