diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go b/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go index d9b28ad7856..ae57e6739a2 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go @@ -36,6 +36,11 @@ type mutatingWebhookConfigurationManager struct { configuration *atomic.Value lister admissionregistrationlisters.MutatingWebhookConfigurationLister hasSynced func() bool + // initialConfigurationSynced stores a boolean value, which tracks if + // the existing webhook configs have been synced (honored) by the + // manager at startup-- the informer has synced and either has no items + // or has finished executing updateConfiguration() once. + initialConfigurationSynced *atomic.Value } var _ generic.Source = &mutatingWebhookConfigurationManager{} @@ -43,13 +48,15 @@ var _ generic.Source = &mutatingWebhookConfigurationManager{} func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source { informer := f.Admissionregistration().V1().MutatingWebhookConfigurations() manager := &mutatingWebhookConfigurationManager{ - configuration: &atomic.Value{}, - lister: informer.Lister(), - hasSynced: informer.Informer().HasSynced, + configuration: &atomic.Value{}, + lister: informer.Lister(), + hasSynced: informer.Informer().HasSynced, + initialConfigurationSynced: &atomic.Value{}, } // Start with an empty list manager.configuration.Store([]webhook.WebhookAccessor{}) + manager.initialConfigurationSynced.Store(false) // On any change, rebuild the config informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -66,8 +73,27 @@ func (m *mutatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccess return m.configuration.Load().([]webhook.WebhookAccessor) } +// HasSynced returns true when the manager is synced with existing webhookconfig +// objects at startup-- which means the informer is synced and either has no items +// or updateConfiguration() has completed. func (m *mutatingWebhookConfigurationManager) HasSynced() bool { - return m.hasSynced() + if !m.hasSynced() { + return false + } + if m.initialConfigurationSynced.Load().(bool) { + // the informer has synced and configuration has been updated + return true + } + if configurations, err := m.lister.List(labels.Everything()); err == nil && len(configurations) == 0 { + // the empty list we initially stored is valid to use. + // Setting initialConfigurationSynced to true, so subsequent checks + // would be able to take the fast path on the atomic boolean in a + // cluster without any admission webhooks configured. + m.initialConfigurationSynced.Store(true) + // the informer has synced and we don't have any items + return true + } + return false } func (m *mutatingWebhookConfigurationManager) updateConfiguration() { @@ -77,6 +103,7 @@ func (m *mutatingWebhookConfigurationManager) updateConfiguration() { return } m.configuration.Store(mergeMutatingWebhookConfigurations(configurations)) + m.initialConfigurationSynced.Store(true) } func mergeMutatingWebhookConfigurations(configurations []*v1.MutatingWebhookConfiguration) []webhook.WebhookAccessor { diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go b/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go index 37062b082e1..b8c1904ea8a 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go @@ -36,6 +36,11 @@ type validatingWebhookConfigurationManager struct { configuration *atomic.Value lister admissionregistrationlisters.ValidatingWebhookConfigurationLister hasSynced func() bool + // initialConfigurationSynced stores a boolean value, which tracks if + // the existing webhook configs have been synced (honored) by the + // manager at startup-- the informer has synced and either has no items + // or has finished executing updateConfiguration() once. + initialConfigurationSynced *atomic.Value } var _ generic.Source = &validatingWebhookConfigurationManager{} @@ -43,13 +48,15 @@ var _ generic.Source = &validatingWebhookConfigurationManager{} func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source { informer := f.Admissionregistration().V1().ValidatingWebhookConfigurations() manager := &validatingWebhookConfigurationManager{ - configuration: &atomic.Value{}, - lister: informer.Lister(), - hasSynced: informer.Informer().HasSynced, + configuration: &atomic.Value{}, + lister: informer.Lister(), + hasSynced: informer.Informer().HasSynced, + initialConfigurationSynced: &atomic.Value{}, } // Start with an empty list manager.configuration.Store([]webhook.WebhookAccessor{}) + manager.initialConfigurationSynced.Store(false) // On any change, rebuild the config informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -66,9 +73,28 @@ func (v *validatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAcce return v.configuration.Load().([]webhook.WebhookAccessor) } -// HasSynced returns true if the shared informers have synced. +// HasSynced returns true when the manager is synced with existing webhookconfig +// objects at startup-- which means the informer is synced and either has no items +// or updateConfiguration() has completed. func (v *validatingWebhookConfigurationManager) HasSynced() bool { - return v.hasSynced() + if !v.hasSynced() { + return false + } + if v.initialConfigurationSynced.Load().(bool) { + // the informer has synced and configuration has been updated + return true + } + if configurations, err := v.lister.List(labels.Everything()); err == nil && len(configurations) == 0 { + // the empty list we initially stored is valid to use. + // Setting initialConfigurationSynced to true, so subsequent checks + // would be able to take the fast path on the atomic boolean in a + // cluster without any admission webhooks configured. + v.initialConfigurationSynced.Store(true) + // the informer has synced and we don't have any items + return true + } + return false + } func (v *validatingWebhookConfigurationManager) updateConfiguration() { @@ -78,6 +104,7 @@ func (v *validatingWebhookConfigurationManager) updateConfiguration() { return } v.configuration.Store(mergeValidatingWebhookConfigurations(configurations)) + v.initialConfigurationSynced.Store(true) } func mergeValidatingWebhookConfigurations(configurations []*v1.ValidatingWebhookConfiguration) []webhook.WebhookAccessor {