k8s.io/apiserver@v0.31.1/pkg/admission/configuration/validating_webhook_manager.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package configuration 18 19 import ( 20 "fmt" 21 "sort" 22 "sync" 23 24 v1 "k8s.io/api/admissionregistration/v1" 25 "k8s.io/apimachinery/pkg/labels" 26 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 27 "k8s.io/apiserver/pkg/admission/plugin/webhook" 28 "k8s.io/apiserver/pkg/admission/plugin/webhook/generic" 29 "k8s.io/client-go/informers" 30 admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1" 31 "k8s.io/client-go/tools/cache" 32 "k8s.io/client-go/tools/cache/synctrack" 33 "k8s.io/klog/v2" 34 ) 35 36 // Type for test injection. 37 type validatingWebhookAccessorCreator func(uid string, configurationName string, h *v1.ValidatingWebhook) webhook.WebhookAccessor 38 39 // validatingWebhookConfigurationManager collects the validating webhook objects so that they can be called. 40 type validatingWebhookConfigurationManager struct { 41 lister admissionregistrationlisters.ValidatingWebhookConfigurationLister 42 hasSynced func() bool 43 lazy synctrack.Lazy[[]webhook.WebhookAccessor] 44 configurationsCache sync.Map 45 // createValidatingWebhookAccessor is used to instantiate webhook accessors. 46 // This function is defined as field instead of a struct method to allow injection 47 // during tests 48 createValidatingWebhookAccessor validatingWebhookAccessorCreator 49 } 50 51 var _ generic.Source = &validatingWebhookConfigurationManager{} 52 53 func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source { 54 informer := f.Admissionregistration().V1().ValidatingWebhookConfigurations() 55 manager := &validatingWebhookConfigurationManager{ 56 lister: informer.Lister(), 57 createValidatingWebhookAccessor: webhook.NewValidatingWebhookAccessor, 58 } 59 manager.lazy.Evaluate = manager.getConfiguration 60 61 handle, _ := informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ 62 AddFunc: func(_ interface{}) { manager.lazy.Notify() }, 63 UpdateFunc: func(old, new interface{}) { 64 obj := new.(*v1.ValidatingWebhookConfiguration) 65 manager.configurationsCache.Delete(obj.GetName()) 66 manager.lazy.Notify() 67 }, 68 DeleteFunc: func(obj interface{}) { 69 vwc, ok := obj.(*v1.ValidatingWebhookConfiguration) 70 if !ok { 71 tombstone, ok := obj.(cache.DeletedFinalStateUnknown) 72 if !ok { 73 klog.V(2).Infof("Couldn't get object from tombstone %#v", obj) 74 return 75 } 76 vwc, ok = tombstone.Obj.(*v1.ValidatingWebhookConfiguration) 77 if !ok { 78 klog.V(2).Infof("Tombstone contained object that is not expected %#v", obj) 79 return 80 } 81 } 82 manager.configurationsCache.Delete(vwc.Name) 83 manager.lazy.Notify() 84 }, 85 }) 86 manager.hasSynced = handle.HasSynced 87 88 return manager 89 } 90 91 // Webhooks returns the merged ValidatingWebhookConfiguration. 92 func (v *validatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccessor { 93 out, err := v.lazy.Get() 94 if err != nil { 95 utilruntime.HandleError(fmt.Errorf("error getting webhook configuration: %v", err)) 96 } 97 return out 98 } 99 100 // HasSynced returns true if the initial set of validating webhook configurations 101 // has been loaded. 102 func (v *validatingWebhookConfigurationManager) HasSynced() bool { return v.hasSynced() } 103 104 func (v *validatingWebhookConfigurationManager) getConfiguration() ([]webhook.WebhookAccessor, error) { 105 configurations, err := v.lister.List(labels.Everything()) 106 if err != nil { 107 return []webhook.WebhookAccessor{}, err 108 } 109 return v.getValidatingWebhookConfigurations(configurations), nil 110 } 111 112 // getMutatingWebhookConfigurations returns the webhook accessors for a given list of 113 // mutating webhook configurations. 114 // 115 // This function will, first, try to load the webhook accessors from the cache and avoid 116 // recreating them, which can be expessive (requiring CEL expression recompilation). 117 func (v *validatingWebhookConfigurationManager) getValidatingWebhookConfigurations(configurations []*v1.ValidatingWebhookConfiguration) []webhook.WebhookAccessor { 118 sort.SliceStable(configurations, ValidatingWebhookConfigurationSorter(configurations).ByName) 119 size := 0 120 for _, cfg := range configurations { 121 size += len(cfg.Webhooks) 122 } 123 accessors := make([]webhook.WebhookAccessor, 0, size) 124 125 for _, c := range configurations { 126 cachedConfigurationAccessors, ok := v.configurationsCache.Load(c.Name) 127 if ok { 128 // Pick an already cached webhookAccessor 129 accessors = append(accessors, cachedConfigurationAccessors.([]webhook.WebhookAccessor)...) 130 continue 131 } 132 133 // webhook names are not validated for uniqueness, so we check for duplicates and 134 // add a int suffix to distinguish between them 135 names := map[string]int{} 136 configurationAccessors := make([]webhook.WebhookAccessor, 0, len(c.Webhooks)) 137 for i := range c.Webhooks { 138 n := c.Webhooks[i].Name 139 uid := fmt.Sprintf("%s/%s/%d", c.Name, n, names[n]) 140 names[n]++ 141 configurationAccessor := v.createValidatingWebhookAccessor(uid, c.Name, &c.Webhooks[i]) 142 configurationAccessors = append(configurationAccessors, configurationAccessor) 143 } 144 accessors = append(accessors, configurationAccessors...) 145 v.configurationsCache.Store(c.Name, configurationAccessors) 146 } 147 148 return accessors 149 } 150 151 type ValidatingWebhookConfigurationSorter []*v1.ValidatingWebhookConfiguration 152 153 func (a ValidatingWebhookConfigurationSorter) ByName(i, j int) bool { 154 return a[i].Name < a[j].Name 155 }