apiserver-build-in-admission-plugins

Build some Admission Controllers into the Generic API server library

Related PR:

Topic Link
Admission Control https://git.k8s.io/community/contributors/design-proposals/api-machinery/admission_control.md

Introduction

An admission controller is a piece of code that intercepts requests to the Kubernetes API - think a middleware. The API server lets you have a whole chain of them. Each is run in sequence before a request is accepted into the cluster. If any of the plugins in the sequence rejects the request, the entire request is rejected immediately and an error is returned to the user.

Many features in Kubernetes require an admission control plugin to be enabled in order to properly support the feature. In fact in the documentation you will find a recommended set of them to use.

At the moment admission controllers are implemented as plugins and they have to be compiled into the final binary in order to be used at a later time. Some even require an access to cache, an authorizer etc. This is where an admission plugin initializer kicks in. An admission plugin initializer is used to pass additional configuration and runtime references to a cache, a client and an authorizer.

To streamline the process of adding new plugins especially for aggregated API servers we would like to build some plugins into the generic API server library and provide a plugin initializer. While anyone can author and register one, having a known set of provided references let’s people focus on what they need their admission plugin to do instead of paying attention to wiring.

Implementation

The first step would involve creating a “standard” plugin initializer that would be part of the generic API server. It would use kubeconfig to populate external clients and external informers. By default for servers that would be run on the kubernetes cluster in-cluster config would be used. The standard initializer would also provide a client config for connecting to the core kube-apiserver. Some API servers might be started as static pods, which don’t have in-cluster configs. In that case the config could be easily populated form the file.

The second step would be to move some plugins from admission pkg to the generic API server library. Some admission plugins are used to ensure consistent user expectations. These plugins should be moved. One example is the Namespace Lifecycle plugin which prevents users from creating resources in non-existent namespaces.

Note: For loading in-cluster configuration visit For loading the configuration directly from a file visit

How to add an admission plugin ?

At this point adding an admission plugin is very simple and boils down to performing the following series of steps: 1. Write an admission plugin 2. Register the plugin 3. Reference the plugin in the admission chain

An example

The sample apiserver provides an example admission plugin that makes meaningful use of the “standard” plugin initializer. The admission plugin ensures that a resource name is not on the list of banned names. The source code of the plugin can be found here.

Having the plugin, the next step is the registration. AdmissionOptions provides two important things. Firstly it exposes a register under which all admission plugins are registered. In fact, that’s exactly what the Register method does from our example admission plugin. It accepts a global registry as a parameter and then simply registers itself in that registry. Secondly, it adds an admission chain to the server configuration via ApplyTo method. The method accepts optional parameters in the form of pluginInitalizers. This is useful when admission plugins need custom configuration that is not provided by the generic initializer.

The following code has been extracted from the sample server and illustrates how to register and wire an admission plugin:

  // register admission plugins
  banflunder.Register(o.Admission.Plugins)

  // create custom plugin initializer
  informerFactory := informers.NewSharedInformerFactory(client, serverConfig.LoopbackClientConfig.Timeout)
  admissionInitializer, _ := wardleinitializer.New(informerFactory)

  // add admission chain to the server configuration
  o.Admission.ApplyTo(serverConfig, admissionInitializer)