@@ -67,6 +67,7 @@ type controllerManager struct {
6767 // leaderElectionRunnables is the set of Controllers that the controllerManager injects deps into and Starts.
6868 // These Runnables are managed by lead election.
6969 leaderElectionRunnables []Runnable
70+
7071 // nonLeaderElectionRunnables is the set of webhook servers that the controllerManager injects deps into and Starts.
7172 // These Runnables will not be blocked by lead election.
7273 nonLeaderElectionRunnables []Runnable
@@ -577,10 +578,27 @@ func (cm *controllerManager) startNonLeaderElectionRunnables() {
577578 cm .mu .Lock ()
578579 defer cm .mu .Unlock ()
579580
581+ // First start any webhook servers, which includes conversion, validation, and defaulting
582+ // webhooks that are registered.
583+ //
584+ // WARNING: Webhooks MUST start before any cache is populated, otherwise there is a race condition
585+ // between conversion webhooks and the cache sync (usually initial list) which causes the webhooks
586+ // to never start because no cache can be populated.
587+ for _ , c := range cm .nonLeaderElectionRunnables {
588+ if _ , ok := c .(* webhook.Server ); ok {
589+ cm .startRunnable (c )
590+ }
591+ }
592+
593+ // Start and wait for caches.
580594 cm .waitForCache (cm .internalCtx )
581595
582596 // Start the non-leaderelection Runnables after the cache has synced
583597 for _ , c := range cm .nonLeaderElectionRunnables {
598+ if _ , ok := c .(* webhook.Server ); ok {
599+ continue
600+ }
601+
584602 // Controllers block, but we want to return an error if any have an error starting.
585603 // Write any Start errors to a channel so we can return them
586604 cm .startRunnable (c )
0 commit comments