add featuregate inspection as admission plugin initializer

Kubernetes-commit: 675c2fb924e82091f7ce4601e48daf4cc7030e72
This commit is contained in:
David Eads 2019-11-05 14:28:40 -05:00 committed by Kubernetes Publisher
parent a3ef59b336
commit 331894196f
6 changed files with 45 additions and 14 deletions

View File

@ -21,25 +21,30 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/component-base/featuregate"
)
type pluginInitializer struct {
externalClient kubernetes.Interface
externalInformers informers.SharedInformerFactory
authorizer authorizer.Authorizer
featureGates featuregate.FeatureGate
}
// New creates an instance of admission plugins initializer.
// TODO(p0lyn0mial): make the parameters public, this construction seems to be redundant.
// This constructor is public with a long param list so that callers immediately know that new information can be expected
// during compilation when they update a level.
func New(
extClientset kubernetes.Interface,
extInformers informers.SharedInformerFactory,
authz authorizer.Authorizer,
featureGates featuregate.FeatureGate,
) pluginInitializer {
return pluginInitializer{
externalClient: extClientset,
externalInformers: extInformers,
authorizer: authz,
featureGates: featureGates,
}
}
@ -57,6 +62,10 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) {
if wants, ok := plugin.(WantsAuthorizer); ok {
wants.SetAuthorizer(i.authorizer)
}
if wants, ok := plugin.(WantsFeatures); ok {
wants.InspectFeatureGates(i.featureGates)
}
}
var _ admission.PluginInitializer = pluginInitializer{}

View File

@ -32,7 +32,7 @@ import (
// TestWantsAuthorizer ensures that the authorizer is injected
// when the WantsAuthorizer interface is implemented by a plugin.
func TestWantsAuthorizer(t *testing.T) {
target := initializer.New(nil, nil, &TestAuthorizer{})
target := initializer.New(nil, nil, &TestAuthorizer{}, nil)
wantAuthorizerAdmission := &WantAuthorizerAdmission{}
target.Initialize(wantAuthorizerAdmission)
if wantAuthorizerAdmission.auth == nil {
@ -44,7 +44,7 @@ func TestWantsAuthorizer(t *testing.T) {
// when the WantsExternalKubeClientSet interface is implemented by a plugin.
func TestWantsExternalKubeClientSet(t *testing.T) {
cs := &fake.Clientset{}
target := initializer.New(cs, nil, &TestAuthorizer{})
target := initializer.New(cs, nil, &TestAuthorizer{}, nil)
wantExternalKubeClientSet := &WantExternalKubeClientSet{}
target.Initialize(wantExternalKubeClientSet)
if wantExternalKubeClientSet.cs != cs {
@ -57,7 +57,7 @@ func TestWantsExternalKubeClientSet(t *testing.T) {
func TestWantsExternalKubeInformerFactory(t *testing.T) {
cs := &fake.Clientset{}
sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second)
target := initializer.New(cs, sf, &TestAuthorizer{})
target := initializer.New(cs, sf, &TestAuthorizer{}, nil)
wantExternalKubeInformerFactory := &WantExternalKubeInformerFactory{}
target.Initialize(wantExternalKubeInformerFactory)
if wantExternalKubeInformerFactory.sf != sf {

View File

@ -21,6 +21,7 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/component-base/featuregate"
)
// WantsExternalKubeClientSet defines a function which sets external ClientSet for admission plugins that need it
@ -40,3 +41,14 @@ type WantsAuthorizer interface {
SetAuthorizer(authorizer.Authorizer)
admission.InitializationValidator
}
// WantsFeatureGate defines a function which passes the featureGates for inspection by an admission plugin.
// Admission plugins should not hold a reference to the featureGates. Instead, they should query a particular one
// and assign it to a simple bool in the admission plugin struct.
// func (a *admissionPlugin) InspectFeatureGates(features featuregate.FeatureGate){
// a.myFeatureIsOn = features.Enabled("my-feature")
// }
type WantsFeatures interface {
InspectFeatureGates(featuregate.FeatureGate)
admission.InitializationValidator
}

View File

@ -52,7 +52,7 @@ func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) (
if err != nil {
return nil, f, err
}
pluginInitializer := kubeadmission.New(c, f, nil)
pluginInitializer := kubeadmission.New(c, f, nil, nil)
pluginInitializer.Initialize(handler)
err = admission.ValidateInitialization(handler)
return handler, f, err

View File

@ -37,6 +37,7 @@ import (
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/component-base/featuregate"
)
var configScheme = runtime.NewScheme()
@ -117,6 +118,7 @@ func (a *AdmissionOptions) ApplyTo(
c *server.Config,
informers informers.SharedInformerFactory,
kubeAPIServerClientConfig *rest.Config,
features featuregate.FeatureGate,
pluginInitializers ...admission.PluginInitializer,
) error {
if a == nil {
@ -139,7 +141,7 @@ func (a *AdmissionOptions) ApplyTo(
if err != nil {
return err
}
genericInitializer := initializer.New(clientset, informers, c.Authorization.Authorizer)
genericInitializer := initializer.New(clientset, informers, c.Authorization.Authorizer, features)
initializersChain := admission.PluginInitializers{}
pluginInitializers = append(pluginInitializers, genericInitializer)
initializersChain = append(initializersChain, pluginInitializers...)

View File

@ -18,11 +18,13 @@ package options
import (
"github.com/spf13/pflag"
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/storage/storagebackend"
"k8s.io/component-base/featuregate"
)
// RecommendedOptions contains the recommended options for running an API server.
@ -37,6 +39,8 @@ type RecommendedOptions struct {
Features *FeatureOptions
CoreAPI *CoreAPIOptions
// FeatureGate is a way to plumb feature gate through if you have them.
FeatureGate featuregate.FeatureGate
// ExtraAdmissionInitializers is called once after all ApplyTo from the options above, to pass the returned
// admission plugin initializers to Admission.ApplyTo.
ExtraAdmissionInitializers func(c *server.RecommendedConfig) ([]admission.PluginInitializer, error)
@ -58,13 +62,17 @@ func NewRecommendedOptions(prefix string, codec runtime.Codec, processInfo *Proc
sso.HTTP2MaxStreamsPerConnection = 1000
return &RecommendedOptions{
Etcd: NewEtcdOptions(storagebackend.NewDefaultConfig(prefix, codec)),
SecureServing: sso.WithLoopback(),
Authentication: NewDelegatingAuthenticationOptions(),
Authorization: NewDelegatingAuthorizationOptions(),
Audit: NewAuditOptions(),
Features: NewFeatureOptions(),
CoreAPI: NewCoreAPIOptions(),
Etcd: NewEtcdOptions(storagebackend.NewDefaultConfig(prefix, codec)),
SecureServing: sso.WithLoopback(),
Authentication: NewDelegatingAuthenticationOptions(),
Authorization: NewDelegatingAuthorizationOptions(),
Audit: NewAuditOptions(),
Features: NewFeatureOptions(),
CoreAPI: NewCoreAPIOptions(),
// Wired a global by default that sadly people will abuse to have different meanings in different repos.
// Please consider creating your own FeatureGate so you can have a consistent meaning for what a variable contains
// across different repos. Future you will thank you.
FeatureGate: feature.DefaultFeatureGate,
ExtraAdmissionInitializers: func(c *server.RecommendedConfig) ([]admission.PluginInitializer, error) { return nil, nil },
Admission: NewAdmissionOptions(),
ProcessInfo: processInfo,
@ -111,7 +119,7 @@ func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig) error {
}
if initializers, err := o.ExtraAdmissionInitializers(config); err != nil {
return err
} else if err := o.Admission.ApplyTo(&config.Config, config.SharedInformerFactory, config.ClientConfig, initializers...); err != nil {
} else if err := o.Admission.ApplyTo(&config.Config, config.SharedInformerFactory, config.ClientConfig, o.FeatureGate, initializers...); err != nil {
return err
}
if err := o.EgressSelector.ApplyTo(&config.Config); err != nil {