Merge pull request #39109 from derekwaynecarr/admission-version-config

Automatic merge from submit-queue (batch tested with PRs 39807, 37505, 39844, 39525, 39109)

Admission control support for versioned configuration files

**What this PR does / why we need it**:
Today, the `--admission-control-config-file=` argument takes an opaque file that is shared across all admission controllers to provide configuration.  This file is not well-versioned and it's shared across multiple plug-ins.  Some plugins take file based configuration (`ImagePolicyWebhook`) and others abuse flags to provide configuration because we lacked a good example (`InitialResources`).  This PR defines a versioned configuration format that we can use moving forward to provide configuration input to admission controllers that is well-versioned, and does not require the addition of new flags.

The sample configuration file would look as follows:

```
apiVersion: componentconfig/v1alpha1
kind: AdmissionConfiguration
plugins:
- name: "ImagePolicyWebhook"
  path: "image-policy-webhook.json"
```

The general behavior is each plugin that requires additional configuration is enumerated by name.  An alternate file location is provided for its specific configuration, or the configuration can be embedded as a raw extension via the configuration section.

**Special notes for your reviewer**:
A follow-on PR will be needed to make `ImagePolicyWebhook` to use versioned configuration.  This PR maintains backwards compatibility by ignoring configuration it cannot understand and therefore treating the file as opaque.  I plan to make use of this PR to complete https://github.com/kubernetes/kubernetes/pull/36765 which attempts to allow more configuration parameters to the `ResourceQuota` admission plugin.
pull/6/head
Kubernetes Submit Queue 2017-01-13 13:40:47 -08:00 committed by GitHub
commit 983a47d876
15 changed files with 621 additions and 20 deletions

View File

@ -13,6 +13,7 @@ go_library(
srcs = [
"attributes.go",
"chain.go",
"config.go",
"errors.go",
"handler.go",
"interfaces.go",
@ -20,7 +21,12 @@ go_library(
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/apis/componentconfig:go_default_library",
"//pkg/apis/componentconfig/v1alpha1:go_default_library",
"//pkg/util/sets:go_default_library",
"//vendor:github.com/ghodss/yaml",
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/runtime",
@ -33,10 +39,17 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["chain_test.go"],
srcs = [
"chain_test.go",
"config_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//vendor:k8s.io/apimachinery/pkg/runtime/schema"],
deps = [
"//pkg/apis/componentconfig:go_default_library",
"//pkg/apis/componentconfig/install:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
],
)
filegroup(

176
pkg/admission/config.go Normal file
View File

@ -0,0 +1,176 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package admission
import (
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"github.com/ghodss/yaml"
"github.com/golang/glog"
"bytes"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/componentconfig"
componentconfigv1alpha1 "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
"k8s.io/kubernetes/pkg/util/sets"
runtime "k8s.io/apimachinery/pkg/runtime"
)
func makeAbs(path, base string) (string, error) {
if filepath.IsAbs(path) {
return path, nil
}
if len(base) == 0 || base == "." {
cwd, err := os.Getwd()
if err != nil {
return "", err
}
base = cwd
}
return filepath.Join(base, path), nil
}
// ReadAdmissionConfiguration reads the admission configuration at the specified path.
// It returns the loaded admission configuration if the input file aligns with the required syntax.
// If it does not align with the provided syntax, it returns a default configuration for the enumerated
// set of pluginNames whose config location references the specified configFilePath.
// It does this to preserve backward compatibility when admission control files were opaque.
// It returns an error if the file did not exist.
func ReadAdmissionConfiguration(pluginNames []string, configFilePath string) (*componentconfig.AdmissionConfiguration, error) {
if configFilePath == "" {
return &componentconfig.AdmissionConfiguration{}, nil
}
// a file was provided, so we just read it.
data, err := ioutil.ReadFile(configFilePath)
if err != nil {
return nil, fmt.Errorf("unable to read admission control configuration from %q [%v]", configFilePath, err)
}
decoder := api.Codecs.UniversalDecoder()
decodedObj, err := runtime.Decode(decoder, data)
// we were able to decode the file successfully
if err == nil {
decodedConfig, ok := decodedObj.(*componentconfig.AdmissionConfiguration)
if !ok {
return nil, fmt.Errorf("unexpected type: %T", decodedObj)
}
baseDir := path.Dir(configFilePath)
for i := range decodedConfig.Plugins {
if decodedConfig.Plugins[i].Path == "" {
continue
}
// we update relative file paths to absolute paths
absPath, err := makeAbs(decodedConfig.Plugins[i].Path, baseDir)
if err != nil {
return nil, err
}
decodedConfig.Plugins[i].Path = absPath
}
return decodedConfig, nil
}
// we got an error where the decode wasn't related to a missing type
if !(runtime.IsMissingVersion(err) || runtime.IsMissingKind(err) || runtime.IsNotRegisteredError(err)) {
return nil, err
}
// convert the legacy format to the new admission control format
// in order to preserve backwards compatibility, we set plugins that
// previously read input from a non-versioned file configuration to the
// current input file.
legacyPluginsWithUnversionedConfig := sets.NewString("ImagePolicyWebhook", "PodNodeSelector")
externalConfig := &componentconfigv1alpha1.AdmissionConfiguration{}
for _, pluginName := range pluginNames {
if legacyPluginsWithUnversionedConfig.Has(pluginName) {
externalConfig.Plugins = append(externalConfig.Plugins,
componentconfigv1alpha1.AdmissionPluginConfiguration{
Name: pluginName,
Path: configFilePath})
}
}
api.Scheme.Default(externalConfig)
internalConfig := &componentconfig.AdmissionConfiguration{}
if err := api.Scheme.Convert(externalConfig, internalConfig, nil); err != nil {
return internalConfig, err
}
return internalConfig, nil
}
// GetAdmissionPluginConfigurationFor returns a reader that holds the admission plugin configuration.
func GetAdmissionPluginConfigurationFor(pluginCfg componentconfig.AdmissionPluginConfiguration) (io.Reader, error) {
// if there is nothing nested in the object, we return the named location
obj := pluginCfg.Configuration
if obj != nil {
// serialize the configuration and build a reader for it
content, err := writeYAML(obj)
if err != nil {
return nil, err
}
return bytes.NewBuffer(content), nil
}
// there is nothing nested, so we delegate to path
if pluginCfg.Path != "" {
content, err := ioutil.ReadFile(pluginCfg.Path)
if err != nil {
glog.Fatalf("Couldn't open admission plugin configuration %s: %#v", pluginCfg.Path, err)
return nil, err
}
return bytes.NewBuffer(content), nil
}
// there is no special config at all
return nil, nil
}
// GetAdmissionPluginConfiguration takes the admission configuration and returns a reader
// for the specified plugin. If no specific configuration is present, we return a nil reader.
func GetAdmissionPluginConfiguration(cfg *componentconfig.AdmissionConfiguration, pluginName string) (io.Reader, error) {
// there is no config, so there is no potential config
if cfg == nil {
return nil, nil
}
// look for matching plugin and get configuration
for _, pluginCfg := range cfg.Plugins {
if pluginName != pluginCfg.Name {
continue
}
pluginConfig, err := GetAdmissionPluginConfigurationFor(pluginCfg)
if err != nil {
return nil, err
}
return pluginConfig, nil
}
// there is no registered config that matches on plugin name.
return nil, nil
}
// writeYAML writes the specified object to a byte array as yaml.
func writeYAML(obj runtime.Object) ([]byte, error) {
json, err := runtime.Encode(api.Codecs.LegacyCodec(), obj)
if err != nil {
return nil, err
}
content, err := yaml.JSONToYAML(json)
if err != nil {
return nil, err
}
return content, err
}

View File

@ -0,0 +1,148 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package admission
import (
"io/ioutil"
"os"
"reflect"
"testing"
"k8s.io/kubernetes/pkg/apis/componentconfig"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
)
func TestReadAdmissionConfiguration(t *testing.T) {
// create a place holder file to hold per test config
configFile, err := ioutil.TempFile("", "admission-plugin-config")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err = configFile.Close(); err != nil {
t.Fatalf("unexpected err: %v", err)
}
configFileName := configFile.Name()
// the location that will be fixed up to be relative to the test config file.
imagePolicyWebhookFile, err := makeAbs("image-policy-webhook.json", os.TempDir())
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
// individual test scenarios
testCases := map[string]struct {
ConfigBody string
ExpectedAdmissionConfig *componentconfig.AdmissionConfiguration
PluginNames []string
}{
"v1Alpha1 configuration - path fixup": {
ConfigBody: `{
"apiVersion": "componentconfig/v1alpha1",
"kind": "AdmissionConfiguration",
"plugins": [
{"name": "ImagePolicyWebhook", "path": "image-policy-webhook.json"},
{"name": "ResourceQuota"}
]}`,
ExpectedAdmissionConfig: &componentconfig.AdmissionConfiguration{
Plugins: []componentconfig.AdmissionPluginConfiguration{
{
Name: "ImagePolicyWebhook",
Path: imagePolicyWebhookFile,
},
{
Name: "ResourceQuota",
},
},
},
PluginNames: []string{},
},
"v1Alpha1 configuration - abspath": {
ConfigBody: `{
"apiVersion": "componentconfig/v1alpha1",
"kind": "AdmissionConfiguration",
"plugins": [
{"name": "ImagePolicyWebhook", "path": "/tmp/image-policy-webhook.json"},
{"name": "ResourceQuota"}
]}`,
ExpectedAdmissionConfig: &componentconfig.AdmissionConfiguration{
Plugins: []componentconfig.AdmissionPluginConfiguration{
{
Name: "ImagePolicyWebhook",
Path: "/tmp/image-policy-webhook.json",
},
{
Name: "ResourceQuota",
},
},
},
PluginNames: []string{},
},
"legacy configuration with using legacy plugins": {
ConfigBody: `{
"imagePolicy": {
"kubeConfigFile": "/home/user/.kube/config",
"allowTTL": 30,
"denyTTL": 30,
"retryBackoff": 500,
"defaultAllow": true
},
"podNodeSelectorPluginConfig": {
"clusterDefaultNodeSelector": ""
}
}`,
ExpectedAdmissionConfig: &componentconfig.AdmissionConfiguration{
Plugins: []componentconfig.AdmissionPluginConfiguration{
{
Name: "ImagePolicyWebhook",
Path: configFileName,
},
{
Name: "PodNodeSelector",
Path: configFileName,
},
},
},
PluginNames: []string{"ImagePolicyWebhook", "PodNodeSelector"},
},
"legacy configuration not using legacy plugins": {
ConfigBody: `{
"imagePolicy": {
"kubeConfigFile": "/home/user/.kube/config",
"allowTTL": 30,
"denyTTL": 30,
"retryBackoff": 500,
"defaultAllow": true
},
"podNodeSelectorPluginConfig": {
"clusterDefaultNodeSelector": ""
}
}`,
ExpectedAdmissionConfig: &componentconfig.AdmissionConfiguration{},
PluginNames: []string{"NamespaceLifecycle", "InitialResources"},
},
}
for testName, testCase := range testCases {
if err = ioutil.WriteFile(configFileName, []byte(testCase.ConfigBody), 0644); err != nil {
t.Fatalf("unexpected err writing temp file: %v", err)
}
config, err := ReadAdmissionConfiguration(testCase.PluginNames, configFileName)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if !reflect.DeepEqual(config, testCase.ExpectedAdmissionConfig) {
t.Errorf("%s: Expected:\n\t%#v\nGot:\n\t%#v", testName, testCase.ExpectedAdmissionConfig, config)
}
}
}

View File

@ -21,7 +21,6 @@ import (
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"sort"
"sync"
@ -115,9 +114,20 @@ func splitStream(config io.Reader) (io.Reader, io.Reader, error) {
// NewFromPlugins returns an admission.Interface that will enforce admission control decisions of all
// the given plugins.
func NewFromPlugins(pluginNames []string, configFilePath string, pluginInitializer PluginInitializer) (Interface, error) {
// load config file path into a componentconfig.AdmissionConfiguration
admissionCfg, err := ReadAdmissionConfiguration(pluginNames, configFilePath)
if err != nil {
return nil, err
}
plugins := []Interface{}
for _, pluginName := range pluginNames {
plugin, err := InitPlugin(pluginName, configFilePath, pluginInitializer)
pluginConfig, err := GetAdmissionPluginConfiguration(admissionCfg, pluginName)
if err != nil {
return nil, err
}
plugin, err := InitPlugin(pluginName, pluginConfig, pluginInitializer)
if err != nil {
return nil, err
}
@ -129,27 +139,12 @@ func NewFromPlugins(pluginNames []string, configFilePath string, pluginInitializ
}
// InitPlugin creates an instance of the named interface.
func InitPlugin(name string, configFilePath string, pluginInitializer PluginInitializer) (Interface, error) {
var (
config *os.File
err error
)
func InitPlugin(name string, config io.Reader, pluginInitializer PluginInitializer) (Interface, error) {
if name == "" {
glog.Info("No admission plugin specified.")
return nil, nil
}
if configFilePath != "" {
config, err = os.Open(configFilePath)
if err != nil {
glog.Fatalf("Couldn't open admission plugin configuration %s: %#v",
configFilePath, err)
}
defer config.Close()
}
plugin, found, err := getPlugin(name, config)
if err != nil {
return nil, fmt.Errorf("Couldn't init admission plugin %q: %v", name, err)

View File

@ -48,6 +48,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&KubeProxyConfiguration{},
&KubeSchedulerConfiguration{},
&KubeletConfiguration{},
&AdmissionConfiguration{},
)
return nil
}
@ -55,3 +56,4 @@ func addKnownTypes(scheme *runtime.Scheme) error {
func (obj *KubeProxyConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *KubeSchedulerConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *KubeletConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *AdmissionConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }

View File

@ -18,6 +18,7 @@ package componentconfig
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
utilconfig "k8s.io/kubernetes/pkg/util/config"
)
@ -835,3 +836,28 @@ type PersistentVolumeRecyclerConfiguration struct {
// in a multi-node cluster.
IncrementTimeoutHostPath int32
}
// AdmissionConfiguration provides versioned configuration for admission controllers.
type AdmissionConfiguration struct {
metav1.TypeMeta
// Plugins allows specifying a configuration per admission control plugin.
Plugins []AdmissionPluginConfiguration
}
// AdmissionPluginConfiguration provides the configuration for a single plug-in.
type AdmissionPluginConfiguration struct {
// Name is the name of the admission controller.
// It must match the registered admission plugin name.
Name string
// Path is the path to a configuration file that contains the plugin's
// configuration
// +optional
Path string
// Configuration is an embedded configuration object to be used as the plugin's
// configuration. If present, it will be used instead of the path to the configuration file.
// +optional
Configuration runtime.Object
}

View File

@ -37,6 +37,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&KubeProxyConfiguration{},
&KubeSchedulerConfiguration{},
&KubeletConfiguration{},
&AdmissionConfiguration{},
)
return nil
}
@ -44,3 +45,4 @@ func addKnownTypes(scheme *runtime.Scheme) error {
func (obj *KubeProxyConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *KubeSchedulerConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *KubeletConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *AdmissionConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }

View File

@ -18,6 +18,7 @@ package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api/v1"
)
@ -578,3 +579,29 @@ type KubeletAnonymousAuthentication struct {
// Anonymous requests have a username of system:anonymous, and a group name of system:unauthenticated.
Enabled *bool `json:"enabled"`
}
// AdmissionConfiguration provides versioned configuration for admission controllers.
type AdmissionConfiguration struct {
metav1.TypeMeta `json:",inline"`
// Plugins allows specifying a configuration per admission control plugin.
// +optional
Plugins []AdmissionPluginConfiguration `json:"plugins"`
}
// AdmissionPluginConfiguration provides the configuration for a single plug-in.
type AdmissionPluginConfiguration struct {
// Name is the name of the admission controller.
// It must match the registered admission plugin name.
Name string `json:"name"`
// Path is the path to a configuration file that contains the plugin's
// configuration
// +optional
Path string `json:"path"`
// Configuration is an embedded configuration object to be used as the plugin's
// configuration. If present, it will be used instead of the path to the configuration file.
// +optional
Configuration runtime.RawExtension `json:"configuration"`
}

View File

@ -38,6 +38,10 @@ func init() {
// Public to allow building arbitrary schemes.
func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs(
Convert_v1alpha1_AdmissionConfiguration_To_componentconfig_AdmissionConfiguration,
Convert_componentconfig_AdmissionConfiguration_To_v1alpha1_AdmissionConfiguration,
Convert_v1alpha1_AdmissionPluginConfiguration_To_componentconfig_AdmissionPluginConfiguration,
Convert_componentconfig_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginConfiguration,
Convert_v1alpha1_KubeProxyConfiguration_To_componentconfig_KubeProxyConfiguration,
Convert_componentconfig_KubeProxyConfiguration_To_v1alpha1_KubeProxyConfiguration,
Convert_v1alpha1_KubeSchedulerConfiguration_To_componentconfig_KubeSchedulerConfiguration,
@ -61,6 +65,70 @@ func RegisterConversions(scheme *runtime.Scheme) error {
)
}
func autoConvert_v1alpha1_AdmissionConfiguration_To_componentconfig_AdmissionConfiguration(in *AdmissionConfiguration, out *componentconfig.AdmissionConfiguration, s conversion.Scope) error {
if in.Plugins != nil {
in, out := &in.Plugins, &out.Plugins
*out = make([]componentconfig.AdmissionPluginConfiguration, len(*in))
for i := range *in {
if err := Convert_v1alpha1_AdmissionPluginConfiguration_To_componentconfig_AdmissionPluginConfiguration(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Plugins = nil
}
return nil
}
func Convert_v1alpha1_AdmissionConfiguration_To_componentconfig_AdmissionConfiguration(in *AdmissionConfiguration, out *componentconfig.AdmissionConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha1_AdmissionConfiguration_To_componentconfig_AdmissionConfiguration(in, out, s)
}
func autoConvert_componentconfig_AdmissionConfiguration_To_v1alpha1_AdmissionConfiguration(in *componentconfig.AdmissionConfiguration, out *AdmissionConfiguration, s conversion.Scope) error {
if in.Plugins != nil {
in, out := &in.Plugins, &out.Plugins
*out = make([]AdmissionPluginConfiguration, len(*in))
for i := range *in {
if err := Convert_componentconfig_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginConfiguration(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Plugins = nil
}
return nil
}
func Convert_componentconfig_AdmissionConfiguration_To_v1alpha1_AdmissionConfiguration(in *componentconfig.AdmissionConfiguration, out *AdmissionConfiguration, s conversion.Scope) error {
return autoConvert_componentconfig_AdmissionConfiguration_To_v1alpha1_AdmissionConfiguration(in, out, s)
}
func autoConvert_v1alpha1_AdmissionPluginConfiguration_To_componentconfig_AdmissionPluginConfiguration(in *AdmissionPluginConfiguration, out *componentconfig.AdmissionPluginConfiguration, s conversion.Scope) error {
out.Name = in.Name
out.Path = in.Path
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Configuration, &out.Configuration, s); err != nil {
return err
}
return nil
}
func Convert_v1alpha1_AdmissionPluginConfiguration_To_componentconfig_AdmissionPluginConfiguration(in *AdmissionPluginConfiguration, out *componentconfig.AdmissionPluginConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha1_AdmissionPluginConfiguration_To_componentconfig_AdmissionPluginConfiguration(in, out, s)
}
func autoConvert_componentconfig_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginConfiguration(in *componentconfig.AdmissionPluginConfiguration, out *AdmissionPluginConfiguration, s conversion.Scope) error {
out.Name = in.Name
out.Path = in.Path
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Configuration, &out.Configuration, s); err != nil {
return err
}
return nil
}
func Convert_componentconfig_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginConfiguration(in *componentconfig.AdmissionPluginConfiguration, out *AdmissionPluginConfiguration, s conversion.Scope) error {
return autoConvert_componentconfig_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginConfiguration(in, out, s)
}
func autoConvert_v1alpha1_KubeProxyConfiguration_To_componentconfig_KubeProxyConfiguration(in *KubeProxyConfiguration, out *componentconfig.KubeProxyConfiguration, s conversion.Scope) error {
out.BindAddress = in.BindAddress
out.ClusterCIDR = in.ClusterCIDR

View File

@ -35,6 +35,8 @@ func init() {
// to allow building arbitrary schemes.
func RegisterDeepCopies(scheme *runtime.Scheme) error {
return scheme.AddGeneratedDeepCopyFuncs(
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_AdmissionConfiguration, InType: reflect.TypeOf(&AdmissionConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_AdmissionPluginConfiguration, InType: reflect.TypeOf(&AdmissionPluginConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_KubeProxyConfiguration, InType: reflect.TypeOf(&KubeProxyConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_KubeSchedulerConfiguration, InType: reflect.TypeOf(&KubeSchedulerConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_KubeletAnonymousAuthentication, InType: reflect.TypeOf(&KubeletAnonymousAuthentication{})},
@ -48,6 +50,38 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
)
}
func DeepCopy_v1alpha1_AdmissionConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*AdmissionConfiguration)
out := out.(*AdmissionConfiguration)
*out = *in
if in.Plugins != nil {
in, out := &in.Plugins, &out.Plugins
*out = make([]AdmissionPluginConfiguration, len(*in))
for i := range *in {
if err := DeepCopy_v1alpha1_AdmissionPluginConfiguration(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
}
return nil
}
}
func DeepCopy_v1alpha1_AdmissionPluginConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*AdmissionPluginConfiguration)
out := out.(*AdmissionPluginConfiguration)
*out = *in
if newVal, err := c.DeepCopy(&in.Configuration); err != nil {
return err
} else {
out.Configuration = *newVal.(*runtime.RawExtension)
}
return nil
}
}
func DeepCopy_v1alpha1_KubeProxyConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*KubeProxyConfiguration)

View File

@ -36,6 +36,8 @@ func init() {
// to allow building arbitrary schemes.
func RegisterDeepCopies(scheme *runtime.Scheme) error {
return scheme.AddGeneratedDeepCopyFuncs(
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_componentconfig_AdmissionConfiguration, InType: reflect.TypeOf(&AdmissionConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_componentconfig_AdmissionPluginConfiguration, InType: reflect.TypeOf(&AdmissionPluginConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_componentconfig_IPVar, InType: reflect.TypeOf(&IPVar{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_componentconfig_KubeControllerManagerConfiguration, InType: reflect.TypeOf(&KubeControllerManagerConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_componentconfig_KubeProxyConfiguration, InType: reflect.TypeOf(&KubeProxyConfiguration{})},
@ -54,6 +56,41 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
)
}
func DeepCopy_componentconfig_AdmissionConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*AdmissionConfiguration)
out := out.(*AdmissionConfiguration)
*out = *in
if in.Plugins != nil {
in, out := &in.Plugins, &out.Plugins
*out = make([]AdmissionPluginConfiguration, len(*in))
for i := range *in {
if err := DeepCopy_componentconfig_AdmissionPluginConfiguration(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
}
return nil
}
}
func DeepCopy_componentconfig_AdmissionPluginConfiguration(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*AdmissionPluginConfiguration)
out := out.(*AdmissionPluginConfiguration)
*out = *in
// in.Configuration is kind 'Interface'
if in.Configuration != nil {
if newVal, err := c.DeepCopy(&in.Configuration); err != nil {
return err
} else {
out.Configuration = *newVal.(*runtime.Object)
}
}
return nil
}
}
func DeepCopy_componentconfig_IPVar(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*IPVar)

View File

@ -8812,6 +8812,76 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
},
Dependencies: []string{},
},
"v1alpha1.AdmissionConfiguration": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AdmissionConfiguration provides versioned configuration for admission controllers.",
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"plugins": {
SchemaProps: spec.SchemaProps{
Description: "Plugins allows specifying a configuration per admission control plugin.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef("#/definitions/v1alpha1.AdmissionPluginConfiguration"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"v1alpha1.AdmissionPluginConfiguration"},
},
"v1alpha1.AdmissionPluginConfiguration": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AdmissionPluginConfiguration provides the configuration for a single plug-in.",
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Description: "Name is the name of the admission controller. It must match the registered admission plugin name.",
Type: []string{"string"},
Format: "",
},
},
"path": {
SchemaProps: spec.SchemaProps{
Description: "Path is the path to a configuration file that contains the plugin's configuration",
Type: []string{"string"},
Format: "",
},
},
"configuration": {
SchemaProps: spec.SchemaProps{
Description: "Configuration is an embedded configuration object to be used as the plugin's configuration. If present, it will be used instead of the path to the configuration file.",
Ref: spec.MustCreateRef("#/definitions/runtime.RawExtension"),
},
},
},
Required: []string{"name"},
},
},
Dependencies: []string{
"runtime.RawExtension"},
},
"v1alpha1.CertificateSigningRequest": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{

View File

@ -215,6 +215,7 @@ func (a *imagePolicyWebhook) admitPod(attributes admission.Attributes, review *v
// For additional HTTP configuration, refer to the kubeconfig documentation
// http://kubernetes.io/v1.1/docs/user-guide/kubeconfig-file.html.
func NewImagePolicyWebhook(configFile io.Reader) (admission.Interface, error) {
// TODO: move this to a versioned configuration file format
var config AdmissionConfig
d := yaml.NewYAMLOrJSONDecoder(configFile, 4096)
err := d.Decode(&config)

View File

@ -47,6 +47,7 @@ const (
// WARNING: this feature is experimental and will definitely change.
func init() {
admission.RegisterPlugin("InitialResources", func(config io.Reader) (admission.Interface, error) {
// TODO: remove the usage of flags in favor of reading versioned configuration
s, err := newDataSource(*source)
if err != nil {
return nil, err

View File

@ -41,6 +41,7 @@ var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-select
func init() {
admission.RegisterPlugin("PodNodeSelector", func(config io.Reader) (admission.Interface, error) {
// TODO move this to a versioned configuration file format.
pluginConfig := readConfig(config)
plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig)
return plugin, nil