k8s.io/apiserver@v0.31.1/pkg/admission/plugins.go (about) 1 /* 2 Copyright 2014 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 admission 18 19 import ( 20 "bytes" 21 "fmt" 22 "io" 23 "reflect" 24 "sort" 25 "strings" 26 "sync" 27 28 "k8s.io/klog/v2" 29 ) 30 31 // Factory is a function that returns an Interface for admission decisions. 32 // The config parameter provides an io.Reader handler to the factory in 33 // order to load specific configurations. If no configuration is provided 34 // the parameter is nil. 35 type Factory func(config io.Reader) (Interface, error) 36 37 type Plugins struct { 38 lock sync.Mutex 39 registry map[string]Factory 40 } 41 42 func NewPlugins() *Plugins { 43 return &Plugins{} 44 } 45 46 // All registered admission options. 47 var ( 48 // PluginEnabledFn checks whether a plugin is enabled. By default, if you ask about it, it's enabled. 49 PluginEnabledFn = func(name string, config io.Reader) bool { 50 return true 51 } 52 ) 53 54 // PluginEnabledFunc is a function type that can provide an external check on whether an admission plugin may be enabled 55 type PluginEnabledFunc func(name string, config io.Reader) bool 56 57 // Registered enumerates the names of all registered plugins. 58 func (ps *Plugins) Registered() []string { 59 ps.lock.Lock() 60 defer ps.lock.Unlock() 61 keys := []string{} 62 for k := range ps.registry { 63 keys = append(keys, k) 64 } 65 sort.Strings(keys) 66 return keys 67 } 68 69 // Register registers a plugin Factory by name. This 70 // is expected to happen during app startup. 71 func (ps *Plugins) Register(name string, plugin Factory) { 72 ps.lock.Lock() 73 defer ps.lock.Unlock() 74 if ps.registry != nil { 75 _, found := ps.registry[name] 76 if found { 77 klog.Fatalf("Admission plugin %q was registered twice", name) 78 } 79 } else { 80 ps.registry = map[string]Factory{} 81 } 82 83 klog.V(1).InfoS("Registered admission plugin", "plugin", name) 84 ps.registry[name] = plugin 85 } 86 87 // getPlugin creates an instance of the named plugin. It returns `false` if 88 // the name is not known. The error is returned only when the named provider was 89 // known but failed to initialize. The config parameter specifies the io.Reader 90 // handler of the configuration file for the cloud provider, or nil for no configuration. 91 func (ps *Plugins) getPlugin(name string, config io.Reader) (Interface, bool, error) { 92 ps.lock.Lock() 93 defer ps.lock.Unlock() 94 f, found := ps.registry[name] 95 if !found { 96 return nil, false, nil 97 } 98 99 config1, config2, err := splitStream(config) 100 if err != nil { 101 return nil, true, err 102 } 103 if !PluginEnabledFn(name, config1) { 104 return nil, true, nil 105 } 106 107 ret, err := f(config2) 108 return ret, true, err 109 } 110 111 // splitStream reads the stream bytes and constructs two copies of it. 112 func splitStream(config io.Reader) (io.Reader, io.Reader, error) { 113 if config == nil || reflect.ValueOf(config).IsNil() { 114 return nil, nil, nil 115 } 116 117 configBytes, err := io.ReadAll(config) 118 if err != nil { 119 return nil, nil, err 120 } 121 122 return bytes.NewBuffer(configBytes), bytes.NewBuffer(configBytes), nil 123 } 124 125 // NewFromPlugins returns an admission.Interface that will enforce admission control decisions of all 126 // the given plugins. 127 func (ps *Plugins) NewFromPlugins(pluginNames []string, configProvider ConfigProvider, pluginInitializer PluginInitializer, decorator Decorator) (Interface, error) { 128 handlers := []Interface{} 129 mutationPlugins := []string{} 130 validationPlugins := []string{} 131 for _, pluginName := range pluginNames { 132 pluginConfig, err := configProvider.ConfigFor(pluginName) 133 if err != nil { 134 return nil, err 135 } 136 137 plugin, err := ps.InitPlugin(pluginName, pluginConfig, pluginInitializer) 138 if err != nil { 139 return nil, err 140 } 141 if plugin != nil { 142 if decorator != nil { 143 handlers = append(handlers, decorator.Decorate(plugin, pluginName)) 144 } else { 145 handlers = append(handlers, plugin) 146 } 147 148 if _, ok := plugin.(MutationInterface); ok { 149 mutationPlugins = append(mutationPlugins, pluginName) 150 } 151 if _, ok := plugin.(ValidationInterface); ok { 152 validationPlugins = append(validationPlugins, pluginName) 153 } 154 } 155 } 156 if len(mutationPlugins) != 0 { 157 klog.Infof("Loaded %d mutating admission controller(s) successfully in the following order: %s.", len(mutationPlugins), strings.Join(mutationPlugins, ",")) 158 } 159 if len(validationPlugins) != 0 { 160 klog.Infof("Loaded %d validating admission controller(s) successfully in the following order: %s.", len(validationPlugins), strings.Join(validationPlugins, ",")) 161 } 162 return newReinvocationHandler(chainAdmissionHandler(handlers)), nil 163 } 164 165 // InitPlugin creates an instance of the named interface. 166 func (ps *Plugins) InitPlugin(name string, config io.Reader, pluginInitializer PluginInitializer) (Interface, error) { 167 if name == "" { 168 klog.Info("No admission plugin specified.") 169 return nil, nil 170 } 171 172 plugin, found, err := ps.getPlugin(name, config) 173 if err != nil { 174 return nil, fmt.Errorf("couldn't init admission plugin %q: %v", name, err) 175 } 176 if !found { 177 return nil, fmt.Errorf("unknown admission plugin: %s", name) 178 } 179 180 pluginInitializer.Initialize(plugin) 181 // ensure that plugins have been properly initialized 182 if err := ValidateInitialization(plugin); err != nil { 183 return nil, fmt.Errorf("failed to initialize admission plugin %q: %v", name, err) 184 } 185 186 return plugin, nil 187 } 188 189 // ValidateInitialization will call the InitializationValidate function in each plugin if they implement 190 // the InitializationValidator interface. 191 func ValidateInitialization(plugin Interface) error { 192 if validater, ok := plugin.(InitializationValidator); ok { 193 err := validater.ValidateInitialization() 194 if err != nil { 195 return err 196 } 197 } 198 return nil 199 } 200 201 type PluginInitializers []PluginInitializer 202 203 func (pp PluginInitializers) Initialize(plugin Interface) { 204 for _, p := range pp { 205 p.Initialize(plugin) 206 } 207 }