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  }