kubernetes apiserver源码: Admission Control

169 阅读2分钟

调用链背景


// Run runs the specified APIServer.  This should never exit.
func Run(completeOptions completedServerRunOptions, stopCh <-chan struct{}) error {
    
        // 1.  creates the apiservers connected via delegation.
	server, err := CreateServerChain(completeOptions, stopCh)
}

// CreateKubeAPIServerConfig creates all the resources for running the API server, but runs none of them
func CreateKubeAPIServerConfig(
	s completedServerRunOptions,
	nodeTunneler tunneler.Tunneler,
	proxyTransport *http.Transport,
) (
	*controlplane.Config,
	aggregatorapiserver.ServiceResolver,
	[]admission.PluginInitializer,
	error,
) {
    // 2. genericConfig is a structure used to configure a GenericAPIServer. Its members are sorted roughly in order of importance for composers.
    genericConfig, versionedInformers, serviceResolver, pluginInitializers, admissionPostStartHook, storageFactory, err := buildGenericConfig(s.ServerRunOptions, proxyTransport)
}

// BuildGenericConfig takes the master server options and produces the genericapiserver.Config associated with it
func buildGenericConfig(
	s *options.ServerRunOptions,
	proxyTransport *http.Transport,
) (
	genericConfig *genericapiserver.Config,
	versionedInformers clientgoinformers.SharedInformerFactory,
	serviceResolver aggregatorapiserver.ServiceResolver,
	pluginInitializers []admission.PluginInitializer,
	admissionPostStartHook genericapiserver.PostStartHookFunc,
	storageFactory *serverstorage.DefaultStorageFactory,
	lastErr error,
) {

        // 3. adds the admission chain to the server configuration.
	err = s.Admission.ApplyTo(
		genericConfig,
		versionedInformers,
		kubeClientConfig,
		feature.DefaultFeatureGate,
		pluginInitializers...)
	if err != nil {
		lastErr = fmt.Errorf("failed to initialize admission: %v", err)
	}

	return
}

后面的调用链条基本就很好找了...

Admission Control

// NewFromPlugins returns an admission.Interface that will enforce admission control decisions of all
// the given plugins.
func (ps *Plugins) NewFromPlugins(pluginNames []string, configProvider ConfigProvider, pluginInitializer PluginInitializer, decorator Decorator) (Interface, error) {
	handlers := []Interface{}
        // only for output purpose
        mutationPlugins := []string{}
	validationPlugins := []string{}
	for _, pluginName := range pluginNames {
		pluginConfig, err := configProvider.ConfigFor(pluginName)
		if err != nil {
			return nil, err
		}
                // 1. 初始化插件
		plugin, err := ps.InitPlugin(pluginName, pluginConfig, pluginInitializer)
		if err != nil {
			return nil, err
		}
		if plugin != nil {
                        // 如果有装饰器,则添加经过装饰的admission.Interface
			if decorator != nil {
				handlers = append(handlers, decorator.Decorate(plugin, pluginName))
			} else {
				handlers = append(handlers, plugin)
			}
                        // 收集mutation plugin
			if _, ok := plugin.(MutationInterface); ok {
				mutationPlugins = append(mutationPlugins, pluginName)
			}
                        // 收集validation plugin
			if _, ok := plugin.(ValidationInterface); ok {
				validationPlugins = append(validationPlugins, pluginName)
			}
		}
	}
	if len(mutationPlugins) != 0 {
		klog.Infof("Loaded %d mutating admission controller(s) successfully in the following order: %s.", len(mutationPlugins), strings.Join(mutationPlugins, ","))
	}
	if len(validationPlugins) != 0 {
		klog.Infof("Loaded %d validating admission controller(s) successfully in the following order: %s.", len(validationPlugins), strings.Join(validationPlugins, ","))
	}
        // + => 展开分析此处
	return newReinvocationHandler(chainAdmissionHandler(handlers)), nil
}
  • chainAdmissionHandler(handlers)

chainAdmissionHandler实际上是一个admission.Interface数组,也实现了Interface,MutationInterface,ValidationInterface接口

// chainAdmissionHandler is an instance of admission.NamedHandler that performs admission control using
// a chain of admission handlers
type chainAdmissionHandler []Interface

// ...

// Interface is an abstract, pluggable interface for Admission Control decisions.
type Interface interface {
	// Handles returns true if this admission controller can handle the given operation
	// where operation can be one of CREATE, UPDATE, DELETE, or CONNECT
	Handles(operation Operation) bool
}
// 校验接口MutationInterface是Interface子接口
type MutationInterface interface {
	Interface
        // mutation请求是否通过校验,通过返回err=nil
	// Admit makes an admission decision based on the request attributes.
	// Context is used only for timeout/deadline/cancellation and tracing information.
	Admit(ctx context.Context, a Attributes, o ObjectInterfaces) (err error)
}

// ValidationInterface is an abstract, pluggable interface for Admission Control decisions.
type ValidationInterface interface {
	Interface

	// Validate makes an admission decision based on the request attributes.  It is NOT allowed to mutate
	// Context is used only for timeout/deadline/cancellation and tracing information.
	Validate(ctx context.Context, a Attributes, o ObjectInterfaces) (err error)
}

chainAdmissionHandler的实际上就是把所有插件的相关方法串联起来执行,比如Admit方法。

// Admit performs an admission control check using a chain of handlers, and returns immediately on first error
func (admissionHandler chainAdmissionHandler) Admit(ctx context.Context, a Attributes, o ObjectInterfaces) error {
        // 迭代每个admission.Interface
	for _, handler := range admissionHandler {
                // 是否可以处理此类操作
		if !handler.Handles(a.GetOperation()) {
                        // 不可以则跳过
			continue
		}
                // 可以则执行Admit校验
		if mutator, ok := handler.(MutationInterface); ok {
			err := mutator.Admit(ctx, a, o)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

  • newReinvocationHandler(chainAdmissionHandler(handlers)), nil

主要是用于一些mutation插件本身也会修改一些对象信息,因此需要再次校验,因此reinvoker又在chainAdmissionHandler外又包了一层,用于再次校验。


// newReinvocationHandler creates a handler that wraps the provided admission chain and reinvokes it
// if needed according to re-invocation policy of the webhooks.
func newReinvocationHandler(admissionChain Interface) Interface {
	return &reinvoker{admissionChain}
}

type reinvoker struct {
	admissionChain Interface
}

简单看下reinvoker如何实现MutationInterface接口:

// Admit performs an admission control check using the wrapped admission chain, reinvoking the
// admission chain if needed according to the reinvocation policy.  Plugins are expected to check
// the admission attributes' reinvocation context against their reinvocation policy to decide if
// they should re-run, and to update the reinvocation context if they perform any mutations.
func (r *reinvoker) Admit(ctx context.Context, a Attributes, o ObjectInterfaces) error {
	if mutator, ok := r.admissionChain.(MutationInterface); ok {
                // 进行第一次校验,插件可能修改了上下文中的对象信息
		err := mutator.Admit(ctx, a, o)
		if err != nil {
			return err
		}
                
		s := a.GetReinvocationContext()
                // 判断是否需要再次校验
		if s.ShouldReinvoke() {
			s.SetIsReinvoke()
			// Calling admit a second time will reinvoke all in-tree plugins
			// as well as any webhook plugins that need to be reinvoked based on the
                        // 需要再次校验的话,进行第二次校验
			// reinvocation policy.
			return mutator.Admit(ctx, a, o)
		}
	}
	return nil
}