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