mirror of https://github.com/k3s-io/k3s
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
commit
983a47d876
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue