k8s.io/apiserver@v0.31.1/pkg/admission/configuration/mutating_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 mutatingWebhookAccessorCreator func(uid string, configurationName string, h *v1.MutatingWebhook) webhook.WebhookAccessor 38 39 // mutatingWebhookConfigurationManager collects the mutating webhook objects so that they can be called. 40 type mutatingWebhookConfigurationManager struct { 41 lister admissionregistrationlisters.MutatingWebhookConfigurationLister 42 hasSynced func() bool 43 lazy synctrack.Lazy[[]webhook.WebhookAccessor] 44 configurationsCache sync.Map 45 // createMutatingWebhookAccessor 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 createMutatingWebhookAccessor mutatingWebhookAccessorCreator 49 } 50 51 var _ generic.Source = &mutatingWebhookConfigurationManager{} 52 53 func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source { 54 informer := f.Admissionregistration().V1().MutatingWebhookConfigurations() 55 manager := &mutatingWebhookConfigurationManager{ 56 lister: informer.Lister(), 57 createMutatingWebhookAccessor: webhook.NewMutatingWebhookAccessor, 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.MutatingWebhookConfiguration) 65 manager.configurationsCache.Delete(obj.GetName()) 66 manager.lazy.Notify() 67 }, 68 DeleteFunc: func(obj interface{}) { 69 vwc, ok := obj.(*v1.MutatingWebhookConfiguration) 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.MutatingWebhookConfiguration) 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 MutatingWebhookConfiguration. 92 func (m *mutatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccessor { 93 out, err := m.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 mutating webhook configurations 101 // has been loaded. 102 func (m *mutatingWebhookConfigurationManager) HasSynced() bool { return m.hasSynced() } 103 104 func (m *mutatingWebhookConfigurationManager) getConfiguration() ([]webhook.WebhookAccessor, error) { 105 configurations, err := m.lister.List(labels.Everything()) 106 if err != nil { 107 return []webhook.WebhookAccessor{}, err 108 } 109 return m.getMutatingWebhookConfigurations(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 (m *mutatingWebhookConfigurationManager) getMutatingWebhookConfigurations(configurations []*v1.MutatingWebhookConfiguration) []webhook.WebhookAccessor { 118 // The internal order of webhooks for each configuration is provided by the user 119 // but configurations themselves can be in any order. As we are going to run these 120 // webhooks in serial, they are sorted here to have a deterministic order. 121 sort.SliceStable(configurations, MutatingWebhookConfigurationSorter(configurations).ByName) 122 size := 0 123 for _, cfg := range configurations { 124 size += len(cfg.Webhooks) 125 } 126 accessors := make([]webhook.WebhookAccessor, 0, size) 127 128 for _, c := range configurations { 129 cachedConfigurationAccessors, ok := m.configurationsCache.Load(c.Name) 130 if ok { 131 // Pick an already cached webhookAccessor 132 accessors = append(accessors, cachedConfigurationAccessors.([]webhook.WebhookAccessor)...) 133 continue 134 } 135 136 // webhook names are not validated for uniqueness, so we check for duplicates and 137 // add a int suffix to distinguish between them 138 names := map[string]int{} 139 configurationAccessors := make([]webhook.WebhookAccessor, 0, len(c.Webhooks)) 140 for i := range c.Webhooks { 141 n := c.Webhooks[i].Name 142 uid := fmt.Sprintf("%s/%s/%d", c.Name, n, names[n]) 143 names[n]++ 144 configurationAccessor := m.createMutatingWebhookAccessor(uid, c.Name, &c.Webhooks[i]) 145 configurationAccessors = append(configurationAccessors, configurationAccessor) 146 } 147 accessors = append(accessors, configurationAccessors...) 148 m.configurationsCache.Store(c.Name, configurationAccessors) 149 } 150 return accessors 151 } 152 153 type MutatingWebhookConfigurationSorter []*v1.MutatingWebhookConfiguration 154 155 func (a MutatingWebhookConfigurationSorter) ByName(i, j int) bool { 156 return a[i].Name < a[j].Name 157 }