webhook config manager: HasSynced returns true when the manager is synced with existing webhookconfig objects at startup

This commit is contained in:
Haowei Cai 2021-03-17 14:34:06 -07:00
parent 2f8a2258f4
commit 37d171e5bc
2 changed files with 63 additions and 9 deletions

View File

@ -36,6 +36,11 @@ type mutatingWebhookConfigurationManager struct {
configuration *atomic.Value configuration *atomic.Value
lister admissionregistrationlisters.MutatingWebhookConfigurationLister lister admissionregistrationlisters.MutatingWebhookConfigurationLister
hasSynced func() bool 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{} var _ generic.Source = &mutatingWebhookConfigurationManager{}
@ -43,13 +48,15 @@ var _ generic.Source = &mutatingWebhookConfigurationManager{}
func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source { func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source {
informer := f.Admissionregistration().V1().MutatingWebhookConfigurations() informer := f.Admissionregistration().V1().MutatingWebhookConfigurations()
manager := &mutatingWebhookConfigurationManager{ manager := &mutatingWebhookConfigurationManager{
configuration: &atomic.Value{}, configuration: &atomic.Value{},
lister: informer.Lister(), lister: informer.Lister(),
hasSynced: informer.Informer().HasSynced, hasSynced: informer.Informer().HasSynced,
initialConfigurationSynced: &atomic.Value{},
} }
// Start with an empty list // Start with an empty list
manager.configuration.Store([]webhook.WebhookAccessor{}) manager.configuration.Store([]webhook.WebhookAccessor{})
manager.initialConfigurationSynced.Store(false)
// On any change, rebuild the config // On any change, rebuild the config
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
@ -66,8 +73,27 @@ func (m *mutatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccess
return m.configuration.Load().([]webhook.WebhookAccessor) 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 { 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() { func (m *mutatingWebhookConfigurationManager) updateConfiguration() {
@ -77,6 +103,7 @@ func (m *mutatingWebhookConfigurationManager) updateConfiguration() {
return return
} }
m.configuration.Store(mergeMutatingWebhookConfigurations(configurations)) m.configuration.Store(mergeMutatingWebhookConfigurations(configurations))
m.initialConfigurationSynced.Store(true)
} }
func mergeMutatingWebhookConfigurations(configurations []*v1.MutatingWebhookConfiguration) []webhook.WebhookAccessor { func mergeMutatingWebhookConfigurations(configurations []*v1.MutatingWebhookConfiguration) []webhook.WebhookAccessor {

View File

@ -36,6 +36,11 @@ type validatingWebhookConfigurationManager struct {
configuration *atomic.Value configuration *atomic.Value
lister admissionregistrationlisters.ValidatingWebhookConfigurationLister lister admissionregistrationlisters.ValidatingWebhookConfigurationLister
hasSynced func() bool 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{} var _ generic.Source = &validatingWebhookConfigurationManager{}
@ -43,13 +48,15 @@ var _ generic.Source = &validatingWebhookConfigurationManager{}
func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source { func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source {
informer := f.Admissionregistration().V1().ValidatingWebhookConfigurations() informer := f.Admissionregistration().V1().ValidatingWebhookConfigurations()
manager := &validatingWebhookConfigurationManager{ manager := &validatingWebhookConfigurationManager{
configuration: &atomic.Value{}, configuration: &atomic.Value{},
lister: informer.Lister(), lister: informer.Lister(),
hasSynced: informer.Informer().HasSynced, hasSynced: informer.Informer().HasSynced,
initialConfigurationSynced: &atomic.Value{},
} }
// Start with an empty list // Start with an empty list
manager.configuration.Store([]webhook.WebhookAccessor{}) manager.configuration.Store([]webhook.WebhookAccessor{})
manager.initialConfigurationSynced.Store(false)
// On any change, rebuild the config // On any change, rebuild the config
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
@ -66,9 +73,28 @@ func (v *validatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAcce
return v.configuration.Load().([]webhook.WebhookAccessor) 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 { 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() { func (v *validatingWebhookConfigurationManager) updateConfiguration() {
@ -78,6 +104,7 @@ func (v *validatingWebhookConfigurationManager) updateConfiguration() {
return return
} }
v.configuration.Store(mergeValidatingWebhookConfigurations(configurations)) v.configuration.Store(mergeValidatingWebhookConfigurations(configurations))
v.initialConfigurationSynced.Store(true)
} }
func mergeValidatingWebhookConfigurations(configurations []*v1.ValidatingWebhookConfiguration) []webhook.WebhookAccessor { func mergeValidatingWebhookConfigurations(configurations []*v1.ValidatingWebhookConfiguration) []webhook.WebhookAccessor {