refactor admission flag: add two admission flags and make plugins auto in recommended order

pull/6/head
hzxuzhonghu 2018-01-15 14:58:57 +08:00
parent 621f3f3c0a
commit 7c5f9e0bba
28 changed files with 361 additions and 109 deletions

View File

@ -46,7 +46,7 @@ type ServerRunOptions struct {
InsecureServing *kubeoptions.InsecureServingOptions InsecureServing *kubeoptions.InsecureServingOptions
Audit *genericoptions.AuditOptions Audit *genericoptions.AuditOptions
Features *genericoptions.FeatureOptions Features *genericoptions.FeatureOptions
Admission *genericoptions.AdmissionOptions Admission *kubeoptions.AdmissionOptions
Authentication *kubeoptions.BuiltInAuthenticationOptions Authentication *kubeoptions.BuiltInAuthenticationOptions
Authorization *kubeoptions.BuiltInAuthorizationOptions Authorization *kubeoptions.BuiltInAuthorizationOptions
CloudProvider *kubeoptions.CloudProviderOptions CloudProvider *kubeoptions.CloudProviderOptions
@ -82,7 +82,7 @@ func NewServerRunOptions() *ServerRunOptions {
InsecureServing: kubeoptions.NewInsecureServingOptions(), InsecureServing: kubeoptions.NewInsecureServingOptions(),
Audit: genericoptions.NewAuditOptions(), Audit: genericoptions.NewAuditOptions(),
Features: genericoptions.NewFeatureOptions(), Features: genericoptions.NewFeatureOptions(),
Admission: genericoptions.NewAdmissionOptions(), Admission: kubeoptions.NewAdmissionOptions(),
Authentication: kubeoptions.NewBuiltInAuthenticationOptions().WithAll(), Authentication: kubeoptions.NewBuiltInAuthenticationOptions().WithAll(),
Authorization: kubeoptions.NewBuiltInAuthorizationOptions(), Authorization: kubeoptions.NewBuiltInAuthorizationOptions(),
CloudProvider: kubeoptions.NewCloudProviderOptions(), CloudProvider: kubeoptions.NewCloudProviderOptions(),
@ -116,10 +116,6 @@ func NewServerRunOptions() *ServerRunOptions {
// Overwrite the default for storage data format. // Overwrite the default for storage data format.
s.Etcd.DefaultStorageMediaType = "application/vnd.kubernetes.protobuf" s.Etcd.DefaultStorageMediaType = "application/vnd.kubernetes.protobuf"
// register all admission plugins
RegisterAllAdmissionPlugins(s.Admission.Plugins)
// Set the default for admission plugins names
s.Admission.PluginNames = []string{"AlwaysAdmit"}
return &s return &s
} }

View File

@ -0,0 +1,123 @@
/*
Copyright 2018 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 options
import (
"fmt"
"strings"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/server"
genericoptions "k8s.io/apiserver/pkg/server/options"
"k8s.io/client-go/informers"
"k8s.io/client-go/rest"
)
// AdmissionOptions holds the admission options.
// It is a wrap of generic AdmissionOptions.
type AdmissionOptions struct {
// GenericAdmission holds the generic admission options.
GenericAdmission *genericoptions.AdmissionOptions
// DEPRECATED flag, should use EnabledAdmissionPlugins and DisabledAdmissionPlugins.
// They are mutually exclusive, specify both will lead to an error.
PluginNames []string
}
// NewAdmissionOptions creates a new instance of AdmissionOptions
// Note:
// In addition it calls RegisterAllAdmissionPlugins to register
// all kube-apiserver admission plugins.
//
// Provides the list of RecommendedPluginOrder that holds sane values
// that can be used by servers that don't care about admission chain.
// Servers that do care can overwrite/append that field after creation.
func NewAdmissionOptions() *AdmissionOptions {
options := genericoptions.NewAdmissionOptions()
// register all admission plugins
RegisterAllAdmissionPlugins(options.Plugins)
// set RecommendedPluginOrder
options.RecommendedPluginOrder = AllOrderedPlugins
// set DefaultOffPlugins
options.DefaultOffPlugins = DefaultOffAdmissionPlugins()
return &AdmissionOptions{
GenericAdmission: options,
}
}
// AddFlags adds flags related to admission for kube-apiserver to the specified FlagSet
func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringSliceVar(&a.PluginNames, "admission-control", a.PluginNames, ""+
"Admission is divided into two phases. "+
"In the first phase, only mutating admission plugins run. "+
"In the second phase, only validating admission plugins run. "+
"The names in the below list may represent a validating plugin, a mutating plugin, or both. "+
"The order of plugins in which they are passed to this flag does not matter. "+
"Comma-delimited list of: "+strings.Join(a.GenericAdmission.Plugins.Registered(), ", ")+".")
fs.MarkDeprecated("admission-control", "Use --enable-admission-plugins or --disable-admission-plugins instead. Will be removed in a future version.")
a.GenericAdmission.AddFlags(fs)
}
// Validate verifies flags passed to kube-apiserver AdmissionOptions.
// Kube-apiserver verifies PluginNames and then call generic AdmissionOptions.Validate.
func (a *AdmissionOptions) Validate() []error {
if a == nil {
return nil
}
errs := []error{}
if a.PluginNames != nil &&
(a.GenericAdmission.EnablePlugins != nil || a.GenericAdmission.DisablePlugins != nil) {
errs = append(errs, fmt.Errorf("admission-control and enable-admission-plugins/disable-admission-plugins flags are mutually exclusive"))
}
registeredPlugins := sets.NewString(a.GenericAdmission.Plugins.Registered()...)
for _, name := range a.PluginNames {
if !registeredPlugins.Has(name) {
errs = append(errs, fmt.Errorf("admission-control plugin %q is unknown", name))
}
}
errs = append(errs, a.GenericAdmission.Validate()...)
return errs
}
// ApplyTo adds the admission chain to the server configuration.
// Kube-apiserver just call generic AdmissionOptions.ApplyTo.
func (a *AdmissionOptions) ApplyTo(
c *server.Config,
informers informers.SharedInformerFactory,
kubeAPIServerClientConfig *rest.Config,
scheme *runtime.Scheme,
pluginInitializers ...admission.PluginInitializer,
) error {
if a == nil {
return nil
}
if a.PluginNames != nil {
// pass PluginNames to generic AdmissionOptions
a.GenericAdmission.EnablePlugins = a.PluginNames
}
return a.GenericAdmission.ApplyTo(c, informers, kubeAPIServerClientConfig, scheme, pluginInitializers...)
}

View File

@ -24,7 +24,6 @@ import (
_ "k8s.io/kubernetes/pkg/cloudprovider/providers" _ "k8s.io/kubernetes/pkg/cloudprovider/providers"
// Admission policies // Admission policies
"k8s.io/apiserver/pkg/admission"
"k8s.io/kubernetes/plugin/pkg/admission/admit" "k8s.io/kubernetes/plugin/pkg/admission/admit"
"k8s.io/kubernetes/plugin/pkg/admission/alwayspullimages" "k8s.io/kubernetes/plugin/pkg/admission/alwayspullimages"
"k8s.io/kubernetes/plugin/pkg/admission/antiaffinity" "k8s.io/kubernetes/plugin/pkg/admission/antiaffinity"
@ -52,15 +51,59 @@ import (
"k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny" "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny"
"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount" "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
"k8s.io/kubernetes/plugin/pkg/admission/storageclass/setdefault" "k8s.io/kubernetes/plugin/pkg/admission/storageclass/setdefault"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/initialization"
"k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle"
mutatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
validatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
) )
// RegisterAllAdmissionPlugins registers all admission plugins // AllOrderedPlugins is the list of all the plugins in order.
var AllOrderedPlugins = []string{
admit.PluginName, // AlwaysAdmit
autoprovision.PluginName, // NamespaceAutoProvision
lifecycle.PluginName, // NamespaceLifecycle
exists.PluginName, // NamespaceExists
scdeny.PluginName, // SecurityContextDeny
antiaffinity.PluginName, // LimitPodHardAntiAffinity
initialresources.PluginName, // InitialResources
podpreset.PluginName, // PodPreset
limitranger.PluginName, // LimitRanger
serviceaccount.PluginName, // ServiceAccount
noderestriction.PluginName, // NodeRestriction
alwayspullimages.PluginName, // AlwaysPullImages
imagepolicy.PluginName, // ImagePolicyWebhook
podsecuritypolicy.PluginName, // PodSecurityPolicy
podnodeselector.PluginName, // PodNodeSelector
podpriority.PluginName, // Priority
defaulttolerationseconds.PluginName, // DefaultTolerationSeconds
podtolerationrestriction.PluginName, // PodTolerationRestriction
exec.DenyEscalatingExec, // DenyEscalatingExec
exec.DenyExecOnPrivileged, // DenyExecOnPrivileged
eventratelimit.PluginName, // EventRateLimit
extendedresourcetoleration.PluginName, // ExtendedResourceToleration
label.PluginName, // PersistentVolumeLabel
setdefault.PluginName, // DefaultStorageClass
pvcprotection.PluginName, // PVCProtection
gc.PluginName, // OwnerReferencesPermissionEnforcement
resize.PluginName, // PersistentVolumeClaimResize
mutatingwebhook.PluginName, // MutatingAdmissionWebhook
initialization.PluginName, // Initializers
validatingwebhook.PluginName, // ValidatingAdmissionWebhook
resourcequota.PluginName, // ResourceQuota
deny.PluginName, // AlwaysDeny
}
// RegisterAllAdmissionPlugins registers all admission plugins and
// sets the recommended plugins order.
func RegisterAllAdmissionPlugins(plugins *admission.Plugins) { func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
admit.Register(plugins) admit.Register(plugins) // DEPRECATED as no real meaning
alwayspullimages.Register(plugins) alwayspullimages.Register(plugins)
antiaffinity.Register(plugins) antiaffinity.Register(plugins)
defaulttolerationseconds.Register(plugins) defaulttolerationseconds.Register(plugins)
deny.Register(plugins) deny.Register(plugins) // DEPRECATED as no real meaning
eventratelimit.Register(plugins) eventratelimit.Register(plugins)
exec.Register(plugins) exec.Register(plugins)
extendedresourcetoleration.Register(plugins) extendedresourcetoleration.Register(plugins)
@ -84,3 +127,11 @@ func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
resize.Register(plugins) resize.Register(plugins)
pvcprotection.Register(plugins) pvcprotection.Register(plugins)
} }
// DefaultOffAdmissionPlugins get admission plugins off by default for kube-apiserver.
func DefaultOffAdmissionPlugins() sets.String {
defaultOffPlugins := sets.NewString(AllOrderedPlugins...)
defaultOffPlugins.Delete(lifecycle.PluginName)
return defaultOffPlugins
}

View File

@ -19,40 +19,46 @@ package admit
import ( import (
"io" "io"
"github.com/golang/glog"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
) )
// PluginName indicates name of admission plugin.
const PluginName = "AlwaysAdmit"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("AlwaysAdmit", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewAlwaysAdmit(), nil return NewAlwaysAdmit(), nil
}) })
} }
// AlwaysAdmit is an implementation of admission.Interface which always says yes to an admit request. // alwaysAdmit is an implementation of admission.Interface which always says yes to an admit request.
// It is useful in tests and when using kubernetes in an open manner. type alwaysAdmit struct{}
type AlwaysAdmit struct{}
var _ admission.MutationInterface = AlwaysAdmit{} var _ admission.MutationInterface = alwaysAdmit{}
var _ admission.ValidationInterface = AlwaysAdmit{} var _ admission.ValidationInterface = alwaysAdmit{}
// Admit makes an admission decision based on the request attributes // Admit makes an admission decision based on the request attributes
func (AlwaysAdmit) Admit(a admission.Attributes) (err error) { func (alwaysAdmit) Admit(a admission.Attributes) (err error) {
return nil return nil
} }
// Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate. // Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate.
func (AlwaysAdmit) Validate(a admission.Attributes) (err error) { func (alwaysAdmit) Validate(a admission.Attributes) (err error) {
return nil return nil
} }
// Handles returns true if this admission controller can handle the given operation // Handles returns true if this admission controller can handle the given operation
// where operation can be one of CREATE, UPDATE, DELETE, or CONNECT // where operation can be one of CREATE, UPDATE, DELETE, or CONNECT
func (AlwaysAdmit) Handles(operation admission.Operation) bool { func (alwaysAdmit) Handles(operation admission.Operation) bool {
return true return true
} }
// NewAlwaysAdmit creates a new always admit admission handler // NewAlwaysAdmit creates a new always admit admission handler
func NewAlwaysAdmit() *AlwaysAdmit { func NewAlwaysAdmit() admission.Interface {
return new(AlwaysAdmit) // DEPRECATED: AlwaysAdmit admit all admission request, it is no use.
glog.Warningf("%s admission controller is deprecated. "+
"Please remove this controller from your configuration files and scripts.", PluginName)
return new(alwaysAdmit)
} }

View File

@ -25,7 +25,7 @@ import (
func TestAdmissionNonNilAttribute(t *testing.T) { func TestAdmissionNonNilAttribute(t *testing.T) {
handler := NewAlwaysAdmit() handler := NewAlwaysAdmit()
err := handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil)) err := handler.(*alwaysAdmit).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil))
if err != nil { if err != nil {
t.Errorf("Unexpected error returned from admission handler") t.Errorf("Unexpected error returned from admission handler")
} }
@ -33,7 +33,7 @@ func TestAdmissionNonNilAttribute(t *testing.T) {
func TestAdmissionNilAttribute(t *testing.T) { func TestAdmissionNilAttribute(t *testing.T) {
handler := NewAlwaysAdmit() handler := NewAlwaysAdmit()
err := handler.Admit(nil) err := handler.(*alwaysAdmit).Admit(nil)
if err != nil { if err != nil {
t.Errorf("Unexpected error returned from admission handler") t.Errorf("Unexpected error returned from admission handler")
} }

View File

@ -33,9 +33,12 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
// PluginName indicates name of admission plugin.
const PluginName = "AlwaysPullImages"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("AlwaysPullImages", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewAlwaysPullImages(), nil return NewAlwaysPullImages(), nil
}) })
} }

View File

@ -26,9 +26,11 @@ import (
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
) )
const PluginName = "LimitPodHardAntiAffinity"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("LimitPodHardAntiAffinityTopology", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewInterPodAntiAffinity(), nil return NewInterPodAntiAffinity(), nil
}) })
} }

View File

@ -28,6 +28,9 @@ import (
"k8s.io/kubernetes/pkg/scheduler/algorithm" "k8s.io/kubernetes/pkg/scheduler/algorithm"
) )
// PluginName indicates name of admission plugin.
const PluginName = "DefaultTolerationSeconds"
var ( var (
defaultNotReadyTolerationSeconds = flag.Int64("default-not-ready-toleration-seconds", 300, defaultNotReadyTolerationSeconds = flag.Int64("default-not-ready-toleration-seconds", 300,
"Indicates the tolerationSeconds of the toleration for notReady:NoExecute"+ "Indicates the tolerationSeconds of the toleration for notReady:NoExecute"+
@ -40,7 +43,7 @@ var (
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("DefaultTolerationSeconds", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewDefaultTolerationSeconds(), nil return NewDefaultTolerationSeconds(), nil
}) })
} }

View File

@ -20,40 +20,47 @@ import (
"errors" "errors"
"io" "io"
"github.com/golang/glog"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
) )
// PluginName indicates name of admission plugin.
const PluginName = "AlwaysDeny"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("AlwaysDeny", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewAlwaysDeny(), nil return NewAlwaysDeny(), nil
}) })
} }
// AlwaysDeny is an implementation of admission.Interface which always says no to an admission request. // alwaysDeny is an implementation of admission.Interface which always says no to an admission request.
// It is useful in unit tests to force an operation to be forbidden. type alwaysDeny struct{}
type AlwaysDeny struct{}
var _ admission.MutationInterface = AlwaysDeny{} var _ admission.MutationInterface = alwaysDeny{}
var _ admission.ValidationInterface = AlwaysDeny{} var _ admission.ValidationInterface = alwaysDeny{}
// Admit makes an admission decision based on the request attributes. // Admit makes an admission decision based on the request attributes.
func (AlwaysDeny) Admit(a admission.Attributes) (err error) { func (alwaysDeny) Admit(a admission.Attributes) (err error) {
return admission.NewForbidden(a, errors.New("Admission control is denying all modifications")) return admission.NewForbidden(a, errors.New("admission control is denying all modifications"))
} }
// Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate. // Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate.
func (AlwaysDeny) Validate(a admission.Attributes) (err error) { func (alwaysDeny) Validate(a admission.Attributes) (err error) {
return admission.NewForbidden(a, errors.New("Admission control is denying all modifications")) return admission.NewForbidden(a, errors.New("admission control is denying all modifications"))
} }
// Handles returns true if this admission controller can handle the given operation // Handles returns true if this admission controller can handle the given operation
// where operation can be one of CREATE, UPDATE, DELETE, or CONNECT // where operation can be one of CREATE, UPDATE, DELETE, or CONNECT
func (AlwaysDeny) Handles(operation admission.Operation) bool { func (alwaysDeny) Handles(operation admission.Operation) bool {
return true return true
} }
// NewAlwaysDeny creates an always deny admission handler // NewAlwaysDeny creates an always deny admission handler
func NewAlwaysDeny() *AlwaysDeny { func NewAlwaysDeny() admission.Interface {
return new(AlwaysDeny) // DEPRECATED: AlwaysDeny denys all admission request, it is no use.
glog.Warningf("%s admission controller is deprecated. "+
"Please remove this controller from your configuration files and scripts.", PluginName)
return new(alwaysDeny)
} }

View File

@ -25,7 +25,7 @@ import (
func TestAdmission(t *testing.T) { func TestAdmission(t *testing.T) {
handler := NewAlwaysDeny() handler := NewAlwaysDeny()
err := handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil)) err := handler.(*alwaysDeny).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil))
if err == nil { if err == nil {
t.Error("Expected error returned from admission handler") t.Error("Expected error returned from admission handler")
} }

View File

@ -26,9 +26,12 @@ import (
"k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation" "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation"
) )
// PluginName indicates name of admission plugin.
const PluginName = "EventRateLimit"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("EventRateLimit", plugins.Register(PluginName,
func(config io.Reader) (admission.Interface, error) { func(config io.Reader) (admission.Interface, error) {
// load the configuration provided (if any) // load the configuration provided (if any)
configuration, err := LoadConfiguration(config) configuration, err := LoadConfiguration(config)

View File

@ -29,15 +29,23 @@ import (
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
) )
const (
// DenyEscalatingExec indicates name of admission plugin.
DenyEscalatingExec = "DenyEscalatingExec"
// DenyExecOnPrivileged indicates name of admission plugin.
// Deprecated, should use DenyEscalatingExec instead.
DenyExecOnPrivileged = "DenyExecOnPrivileged"
)
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("DenyEscalatingExec", func(config io.Reader) (admission.Interface, error) { plugins.Register(DenyEscalatingExec, func(config io.Reader) (admission.Interface, error) {
return NewDenyEscalatingExec(), nil return NewDenyEscalatingExec(), nil
}) })
// This is for legacy support of the DenyExecOnPrivileged admission controller. Most // This is for legacy support of the DenyExecOnPrivileged admission controller. Most
// of the time DenyEscalatingExec should be preferred. // of the time DenyEscalatingExec should be preferred.
plugins.Register("DenyExecOnPrivileged", func(config io.Reader) (admission.Interface, error) { plugins.Register(DenyExecOnPrivileged, func(config io.Reader) (admission.Interface, error) {
return NewDenyExecOnPrivileged(), nil return NewDenyExecOnPrivileged(), nil
}) })
} }

View File

@ -27,9 +27,12 @@ import (
"k8s.io/kubernetes/pkg/apis/core/helper" "k8s.io/kubernetes/pkg/apis/core/helper"
) )
// PluginName indicates name of admission plugin.
const PluginName = "ExtendedResourceToleration"
// Register is called by the apiserver to register the plugin factory. // Register is called by the apiserver to register the plugin factory.
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("ExtendedResourceToleration", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return newExtendedResourceToleration(), nil return newExtendedResourceToleration(), nil
}) })
} }

View File

@ -30,9 +30,12 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
) )
// PluginName indicates name of admission plugin.
const PluginName = "OwnerReferencesPermissionEnforcement"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("OwnerReferencesPermissionEnforcement", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
// the pods/status endpoint is ignored by this plugin since old kubelets // the pods/status endpoint is ignored by this plugin since old kubelets
// corrupt them. the pod status strategy ensures status updates cannot mutate // corrupt them. the pod status strategy ensures status updates cannot mutate
// ownerRef. // ownerRef.

View File

@ -43,13 +43,16 @@ import (
_ "k8s.io/kubernetes/pkg/apis/imagepolicy/install" _ "k8s.io/kubernetes/pkg/apis/imagepolicy/install"
) )
// PluginName indicates name of admission plugin.
const PluginName = "ImagePolicyWebhook"
var ( var (
groupVersions = []schema.GroupVersion{v1alpha1.SchemeGroupVersion} groupVersions = []schema.GroupVersion{v1alpha1.SchemeGroupVersion}
) )
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("ImagePolicyWebhook", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
newImagePolicyWebhook, err := NewImagePolicyWebhook(config) newImagePolicyWebhook, err := NewImagePolicyWebhook(config)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -42,12 +42,14 @@ const (
samplesThreshold = 30 samplesThreshold = 30
week = 7 * 24 * time.Hour week = 7 * 24 * time.Hour
month = 30 * 24 * time.Hour month = 30 * 24 * time.Hour
// PluginName indicates name of admission plugin.
PluginName = "InitialResources"
) )
// Register registers a plugin // Register registers a plugin
// WARNING: this feature is experimental and will definitely change. // WARNING: this feature is experimental and will definitely change.
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("InitialResources", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
// TODO: remove the usage of flags in favor of reading versioned configuration // TODO: remove the usage of flags in favor of reading versioned configuration
s, err := newDataSource(*source) s, err := newDataSource(*source)
if err != nil { if err != nil {

View File

@ -41,11 +41,13 @@ import (
const ( const (
limitRangerAnnotation = "kubernetes.io/limit-ranger" limitRangerAnnotation = "kubernetes.io/limit-ranger"
// PluginName indicates name of admission plugin.
PluginName = "LimitRanger"
) )
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("LimitRanger", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewLimitRanger(&DefaultLimitRangerActions{}) return NewLimitRanger(&DefaultLimitRangerActions{})
}) })
} }

View File

@ -30,9 +30,12 @@ import (
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
) )
// PluginName indicates name of admission plugin.
const PluginName = "NamespaceAutoProvision"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("NamespaceAutoProvision", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewProvision(), nil return NewProvision(), nil
}) })
} }

View File

@ -30,9 +30,12 @@ import (
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
) )
// PluginName indicates name of admission plugin.
const PluginName = "NamespaceExists"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("NamespaceExists", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewExists(), nil return NewExists(), nil
}) })
} }

View File

@ -33,9 +33,11 @@ import (
vol "k8s.io/kubernetes/pkg/volume" vol "k8s.io/kubernetes/pkg/volume"
) )
const PluginName = "PersistentVolumeLabel"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("PersistentVolumeLabel", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
persistentVolumeLabelAdmission := NewPersistentVolumeLabel() persistentVolumeLabelAdmission := NewPersistentVolumeLabel()
return persistentVolumeLabelAdmission, nil return persistentVolumeLabelAdmission, nil
}) })

View File

@ -40,9 +40,11 @@ import (
// node selectors labels to namespaces // node selectors labels to namespaces
var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"} var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"}
const PluginName = "PodNodeSelector"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("PodNodeSelector", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
// TODO move this to a versioned configuration file format. // TODO move this to a versioned configuration file format.
pluginConfig := readConfig(config) pluginConfig := readConfig(config)
plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig) plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig)

View File

@ -41,12 +41,12 @@ import (
const ( const (
annotationPrefix = "podpreset.admission.kubernetes.io" annotationPrefix = "podpreset.admission.kubernetes.io"
pluginName = "PodPreset" PluginName = "PodPreset"
) )
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register(pluginName, func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewPlugin(), nil return NewPlugin(), nil
}) })
} }
@ -72,10 +72,10 @@ func NewPlugin() *podPresetPlugin {
func (plugin *podPresetPlugin) ValidateInitialization() error { func (plugin *podPresetPlugin) ValidateInitialization() error {
if plugin.client == nil { if plugin.client == nil {
return fmt.Errorf("%s requires a client", pluginName) return fmt.Errorf("%s requires a client", PluginName)
} }
if plugin.lister == nil { if plugin.lister == nil {
return fmt.Errorf("%s requires a lister", pluginName) return fmt.Errorf("%s requires a lister", PluginName)
} }
return nil return nil
} }

View File

@ -40,9 +40,11 @@ import (
pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
) )
const PluginName = "PodTolerationRestriction"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("PodTolerationRestriction", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
pluginConfig, err := loadConfiguration(config) pluginConfig, err := loadConfiguration(config)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -34,7 +34,8 @@ import (
) )
const ( const (
pluginName = "Priority" // PluginName indicates name of admission plugin.
PluginName = "Priority"
// HighestUserDefinablePriority is the highest priority for user defined priority classes. Priority values larger than 1 billion are reserved for Kubernetes system use. // HighestUserDefinablePriority is the highest priority for user defined priority classes. Priority values larger than 1 billion are reserved for Kubernetes system use.
HighestUserDefinablePriority = 1000000000 HighestUserDefinablePriority = 1000000000
@ -50,7 +51,7 @@ var SystemPriorityClasses = map[string]int32{
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register(pluginName, func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewPlugin(), nil return NewPlugin(), nil
}) })
} }
@ -79,10 +80,10 @@ func NewPlugin() *PriorityPlugin {
// ValidateInitialization implements the InitializationValidator interface. // ValidateInitialization implements the InitializationValidator interface.
func (p *PriorityPlugin) ValidateInitialization() error { func (p *PriorityPlugin) ValidateInitialization() error {
if p.client == nil { if p.client == nil {
return fmt.Errorf("%s requires a client", pluginName) return fmt.Errorf("%s requires a client", PluginName)
} }
if p.lister == nil { if p.lister == nil {
return fmt.Errorf("%s requires a lister", pluginName) return fmt.Errorf("%s requires a lister", PluginName)
} }
return nil return nil
} }

View File

@ -31,9 +31,11 @@ import (
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation" "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation"
) )
const PluginName = "ResourceQuota"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("ResourceQuota", plugins.Register(PluginName,
func(config io.Reader) (admission.Interface, error) { func(config io.Reader) (admission.Interface, error) {
// load the configuration provided (if any) // load the configuration provided (if any)
configuration, err := LoadConfiguration(config) configuration, err := LoadConfiguration(config)

View File

@ -25,9 +25,12 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
// PluginName indicates name of admission plugin.
const PluginName = "SecurityContextDeny"
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
plugins.Register("SecurityContextDeny", func(config io.Reader) (admission.Interface, error) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewSecurityContextDeny(), nil return NewSecurityContextDeny(), nil
}) })
} }

View File

@ -110,7 +110,7 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi
scope.err(err, w, req) scope.err(err, w, req)
return return
} }
if admit.Handles(admission.Connect) { if admit != nil && admit.Handles(admission.Connect) {
connectRequest := &rest.ConnectRequest{ connectRequest := &rest.ConnectRequest{
Name: name, Name: name,
Options: opts, Options: opts,

View File

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
@ -49,11 +50,16 @@ func init() {
type AdmissionOptions struct { type AdmissionOptions struct {
// RecommendedPluginOrder holds an ordered list of plugin names we recommend to use by default // RecommendedPluginOrder holds an ordered list of plugin names we recommend to use by default
RecommendedPluginOrder []string RecommendedPluginOrder []string
// DefaultOffPlugins a list of plugin names that should be disabled by default // DefaultOffPlugins is a set of plugin names that is disabled by default
DefaultOffPlugins []string DefaultOffPlugins sets.String
PluginNames []string
ConfigFile string
// EnablePlugins indicates plugins to be enabled passed through `--enable-admission-plugins`.
EnablePlugins []string
// DisablePlugins indicates plugins to be disabled passed through `--disable-admission-plugins`.
DisablePlugins []string
// ConfigFile is the file path with admission control configuration.
ConfigFile string
// Plugins contains all registered plugins.
Plugins *admission.Plugins Plugins *admission.Plugins
} }
@ -68,13 +74,12 @@ type AdmissionOptions struct {
func NewAdmissionOptions() *AdmissionOptions { func NewAdmissionOptions() *AdmissionOptions {
options := &AdmissionOptions{ options := &AdmissionOptions{
Plugins: admission.NewPlugins(), Plugins: admission.NewPlugins(),
PluginNames: []string{},
// This list is mix of mutating admission plugins and validating // This list is mix of mutating admission plugins and validating
// admission plugins. The apiserver always runs the validating ones // admission plugins. The apiserver always runs the validating ones
// after all the mutating ones, so their relative order in this list // after all the mutating ones, so their relative order in this list
// doesn't matter. // doesn't matter.
RecommendedPluginOrder: []string{lifecycle.PluginName, initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName}, RecommendedPluginOrder: []string{lifecycle.PluginName, initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName},
DefaultOffPlugins: []string{initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName}, DefaultOffPlugins: sets.NewString(initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName),
} }
server.RegisterAllAdmissionPlugins(options.Plugins) server.RegisterAllAdmissionPlugins(options.Plugins)
return options return options
@ -82,14 +87,14 @@ func NewAdmissionOptions() *AdmissionOptions {
// AddFlags adds flags related to admission for a specific APIServer to the specified FlagSet // AddFlags adds flags related to admission for a specific APIServer to the specified FlagSet
func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) { func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringSliceVar(&a.PluginNames, "admission-control", a.PluginNames, ""+ fs.StringSliceVar(&a.EnablePlugins, "enable-admission-plugins", a.EnablePlugins, ""+
"Admission is divided into two phases. "+ "admission plugins that should be enabled in addition to default enabled ones. "+
"In the first phase, only mutating admission plugins run. "+ "Comma-delimited list of admission plugins: "+strings.Join(a.Plugins.Registered(), ", ")+". "+
"In the second phase, only validating admission plugins run. "+ "The order of plugins in this flag does not matter.")
"The names in the below list may represent a validating plugin, a mutating plugin, or both. "+ fs.StringSliceVar(&a.DisablePlugins, "disable-admission-plugins", a.DisablePlugins, ""+
"Within each phase, the plugins will run in the order in which they are passed to this flag. "+ "admission plugins that should be disabled although they are in the default enabled plugins list. "+
"Comma-delimited list of: "+strings.Join(a.Plugins.Registered(), ", ")+".") "Comma-delimited list of admission plugins: "+strings.Join(a.Plugins.Registered(), ", ")+". "+
"The order of plugins in this flag does not matter.")
fs.StringVar(&a.ConfigFile, "admission-control-config-file", a.ConfigFile, fs.StringVar(&a.ConfigFile, "admission-control-config-file", a.ConfigFile,
"File with admission control configuration.") "File with admission control configuration.")
} }
@ -120,10 +125,7 @@ func (a *AdmissionOptions) ApplyTo(
return fmt.Errorf("admission depends on a Kubernetes core API shared informer, it cannot be nil") return fmt.Errorf("admission depends on a Kubernetes core API shared informer, it cannot be nil")
} }
pluginNames := a.PluginNames pluginNames := a.enabledPluginNames()
if len(a.PluginNames) == 0 {
pluginNames = a.enabledPluginNames()
}
pluginsConfigProvider, err := admission.ReadAdmissionConfiguration(pluginNames, a.ConfigFile, configScheme) pluginsConfigProvider, err := admission.ReadAdmissionConfiguration(pluginNames, a.ConfigFile, configScheme)
if err != nil { if err != nil {
@ -148,6 +150,7 @@ func (a *AdmissionOptions) ApplyTo(
return nil return nil
} }
// Validate verifies flags passed to AdmissionOptions.
func (a *AdmissionOptions) Validate() []error { func (a *AdmissionOptions) Validate() []error {
if a == nil { if a == nil {
return nil return nil
@ -156,41 +159,57 @@ func (a *AdmissionOptions) Validate() []error {
errs := []error{} errs := []error{}
registeredPlugins := sets.NewString(a.Plugins.Registered()...) registeredPlugins := sets.NewString(a.Plugins.Registered()...)
for _, name := range a.PluginNames { for _, name := range a.EnablePlugins {
if !registeredPlugins.Has(name) { if !registeredPlugins.Has(name) {
errs = append(errs, fmt.Errorf("admission-control plugin %q is invalid", name)) errs = append(errs, fmt.Errorf("enable-admission-plugins plugin %q is unknown", name))
} }
} }
for _, name := range a.DisablePlugins {
if !registeredPlugins.Has(name) {
errs = append(errs, fmt.Errorf("disable-admission-plugins plugin %q is unknown", name))
}
}
enablePlugins := sets.NewString(a.EnablePlugins...)
disablePlugins := sets.NewString(a.DisablePlugins...)
if len(enablePlugins.Intersection(disablePlugins).List()) > 0 {
errs = append(errs, fmt.Errorf("%v in enable-admission-plugins and disable-admission-plugins "+
"overlapped", enablePlugins.Intersection(disablePlugins).List()))
}
// Verify RecommendedPluginOrder.
recommendPlugins := sets.NewString(a.RecommendedPluginOrder...)
intersections := registeredPlugins.Intersection(recommendPlugins)
if !intersections.Equal(recommendPlugins) {
// Developer error, this should never run in.
errs = append(errs, fmt.Errorf("plugins %v in RecommendedPluginOrder are not registered",
recommendPlugins.Difference(intersections).List()))
}
if !intersections.Equal(registeredPlugins) {
// Developer error, this should never run in.
errs = append(errs, fmt.Errorf("plugins %v registered are not in RecommendedPluginOrder",
registeredPlugins.Difference(intersections).List()))
}
return errs return errs
} }
// enabledPluginNames makes use of RecommendedPluginOrder and DefaultOffPlugins fields // enabledPluginNames makes use of RecommendedPluginOrder, DefaultOffPlugins,
// to prepare a list of plugin names that are enabled. // EnablePlugins, DisablePlugins fields
// // to prepare a list of ordered plugin names that are enabled.
// TODO(p0lyn0mial): In the end we will introduce two new flags:
// --disable-admission-plugin this would be a list of admission plugins that a cluster-admin wants to explicitly disable.
// --enable-admission-plugin this would be a list of admission plugins that a cluster-admin wants to explicitly enable.
// both flags are going to be handled by this method
func (a *AdmissionOptions) enabledPluginNames() []string { func (a *AdmissionOptions) enabledPluginNames() []string {
//TODO(p0lyn0mial): first subtract plugins that a user wants to explicitly enable from allOffPlugins (DefaultOffPlugins) allOffPlugins := append(a.DefaultOffPlugins.List(), a.DisablePlugins...)
//TODO(p0lyn0miial): then add/append plugins that a user wants to explicitly disable to allOffPlugins disabledPlugins := sets.NewString(allOffPlugins...)
//TODO(p0lyn0mial): so that --off=three --on=one,three default-off=one,two results in "one" being enabled. enabledPlugins := sets.NewString(a.EnablePlugins...)
allOffPlugins := a.DefaultOffPlugins disabledPlugins = disabledPlugins.Difference(enabledPlugins)
onlyEnabledPluginNames := []string{}
for _, pluginName := range a.RecommendedPluginOrder { orderedPlugins := []string{}
disablePlugin := false for _, plugin := range a.RecommendedPluginOrder {
for _, disabledPluginName := range allOffPlugins { if !disabledPlugins.Has(plugin) {
if pluginName == disabledPluginName { orderedPlugins = append(orderedPlugins, plugin)
disablePlugin = true
break
} }
} }
if disablePlugin {
continue
}
onlyEnabledPluginNames = append(onlyEnabledPluginNames, pluginName)
}
return onlyEnabledPluginNames return orderedPlugins
} }