调用链背景
// 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
}