diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 5f6c634aee..386e7e7ecc 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -484,6 +484,7 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp certBytes, keyBytes, kubeClientConfig, + api.Scheme, pluginInitializer) if err != nil { return nil, nil, nil, nil, nil, fmt.Errorf("failed to initialize admission: %v", err) diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index 3baaa71a7e..cb901459ac 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -218,6 +218,7 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error { nil, nil, kubeClientConfig, + api.Scheme, pluginInitializer, ) if err != nil { diff --git a/plugin/pkg/admission/gc/gc_admission_test.go b/plugin/pkg/admission/gc/gc_admission_test.go index 30a25f89b6..80233326ac 100644 --- a/plugin/pkg/admission/gc/gc_admission_test.go +++ b/plugin/pkg/admission/gc/gc_admission_test.go @@ -85,7 +85,7 @@ func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) { whiteList: whiteList, } - genericPluginInitializer, err := initializer.New(nil, nil, fakeAuthorizer{}, nil, nil) + genericPluginInitializer, err := initializer.New(nil, nil, fakeAuthorizer{}, nil, nil, nil) if err != nil { return nil, err } diff --git a/plugin/pkg/admission/webhook/BUILD b/plugin/pkg/admission/webhook/BUILD index e524f966ab..f73e3cd1ba 100644 --- a/plugin/pkg/admission/webhook/BUILD +++ b/plugin/pkg/admission/webhook/BUILD @@ -11,7 +11,6 @@ go_library( ], visibility = ["//visibility:public"], deps = [ - "//pkg/api:go_default_library", "//pkg/apis/admission/install:go_default_library", "//pkg/kubeapiserver/admission:go_default_library", "//pkg/kubeapiserver/admission/configuration:go_default_library", diff --git a/plugin/pkg/admission/webhook/admission.go b/plugin/pkg/admission/webhook/admission.go index a73f8fabe0..249dfc1a7c 100644 --- a/plugin/pkg/admission/webhook/admission.go +++ b/plugin/pkg/admission/webhook/admission.go @@ -41,7 +41,6 @@ import ( genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - "k8s.io/kubernetes/pkg/api" admissioninit "k8s.io/kubernetes/pkg/kubeapiserver/admission" "k8s.io/kubernetes/pkg/kubeapiserver/admission/configuration" @@ -94,9 +93,6 @@ func NewGenericAdmissionWebhook() (*GenericAdmissionWebhook, error) { admission.Delete, admission.Update, ), - negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{ - Serializer: api.Codecs.LegacyCodec(admissionv1alpha1.SchemeGroupVersion), - }), serviceResolver: defaultServiceResolver{}, }, nil } @@ -130,6 +126,15 @@ func (a *GenericAdmissionWebhook) SetServiceResolver(sr admissioninit.ServiceRes } } +// SetScheme sets a serializer(NegotiatedSerializer) which is derived from the scheme +func (a *GenericAdmissionWebhook) SetScheme(scheme *runtime.Scheme) { + if scheme != nil { + a.negotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{ + Serializer: serializer.NewCodecFactory(scheme).LegacyCodec(admissionv1alpha1.SchemeGroupVersion), + }) + } +} + func (a *GenericAdmissionWebhook) SetClientCert(cert, key []byte) { a.clientCert = cert a.clientKey = key @@ -146,6 +151,9 @@ func (a *GenericAdmissionWebhook) Validate() error { if a.hookSource == nil { return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a Kubernetes client to be provided") } + if a.negotiatedSerializer == nil { + return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a runtime.Scheme to be provided to derive a serializer") + } go a.hookSource.Run(wait.NeverStop) return nil } diff --git a/plugin/pkg/admission/webhook/admission_test.go b/plugin/pkg/admission/webhook/admission_test.go index 26cb3221e3..a5ccc2e39e 100644 --- a/plugin/pkg/admission/webhook/admission_test.go +++ b/plugin/pkg/admission/webhook/admission_test.go @@ -214,6 +214,7 @@ func TestAdmit(t *testing.T) { for name, tt := range table { wh.hookSource = &tt.hookSource wh.serviceResolver = fakeServiceResolver{base: *serverURL, path: tt.path} + wh.SetScheme(api.Scheme) err = wh.Admit(admission.NewAttributesRecord(&object, &oldObject, kind, namespace, name, resource, subResource, operation, &userInfo)) if tt.expectAllow != (err == nil) { diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/initializer/BUILD index 2755ab0901..d274f7370c 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/BUILD @@ -13,6 +13,7 @@ go_library( "interfaces.go", ], deps = [ + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//vendor/k8s.io/client-go/informers:go_default_library", @@ -24,6 +25,7 @@ go_test( name = "go_default_xtest", srcs = ["initializer_test.go"], deps = [ + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go index 3120087e17..bce5433a34 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go @@ -17,6 +17,7 @@ limitations under the License. package initializer import ( + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/client-go/informers" @@ -31,17 +32,26 @@ type pluginInitializer struct { serverIdentifyingClientCert []byte // serverIdentifyingClientKey private key for the client certificate used when calling out to admission plugins serverIdentifyingClientKey []byte + scheme *runtime.Scheme } // New creates an instance of admission plugins initializer. // TODO(p0lyn0mial): make the parameters public, this construction seems to be redundant. -func New(extClientset kubernetes.Interface, extInformers informers.SharedInformerFactory, authz authorizer.Authorizer, serverIdentifyingClientCert, serverIdentifyingClientKey []byte) (pluginInitializer, error) { +func New( + extClientset kubernetes.Interface, + extInformers informers.SharedInformerFactory, + authz authorizer.Authorizer, + serverIdentifyingClientCert, + serverIdentifyingClientKey []byte, + scheme *runtime.Scheme, +) (pluginInitializer, error) { return pluginInitializer{ externalClient: extClientset, externalInformers: extInformers, authorizer: authz, serverIdentifyingClientCert: serverIdentifyingClientCert, serverIdentifyingClientKey: serverIdentifyingClientKey, + scheme: scheme, }, nil } @@ -63,6 +73,10 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) { if wants, ok := plugin.(WantsClientCert); ok { wants.SetClientCert(i.serverIdentifyingClientCert, i.serverIdentifyingClientKey) } + + if wants, ok := plugin.(WantsScheme); ok { + wants.SetScheme(i.scheme) + } } var _ admission.PluginInitializer = pluginInitializer{} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go index 2bd805ff5e..0550bd17cc 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go @@ -20,6 +20,7 @@ import ( "testing" "time" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission/initializer" "k8s.io/apiserver/pkg/authorization/authorizer" @@ -28,12 +29,27 @@ import ( "k8s.io/client-go/kubernetes/fake" ) +// TestWantsScheme ensures that the scheme is injected when +// the WantsScheme interface is implemented by a plugin. +func TestWantsScheme(t *testing.T) { + scheme := runtime.NewScheme() + target, err := initializer.New(nil, nil, nil, nil, nil, scheme) + if err != nil { + t.Fatal(err) + } + wantSchemeAdmission := &WantSchemeAdmission{} + target.Initialize(wantSchemeAdmission) + if wantSchemeAdmission.scheme != scheme { + t.Errorf("expected scheme to be initialized") + } +} + // TestWantsAuthorizer ensures that the authorizer is injected // when the WantsAuthorizer interface is implemented by a plugin. func TestWantsAuthorizer(t *testing.T) { - target, err := initializer.New(nil, nil, &TestAuthorizer{}, nil, nil) + target, err := initializer.New(nil, nil, &TestAuthorizer{}, nil, nil, nil) if err != nil { - t.Fatalf("expected to create an instance of initializer but got an error = %s", err.Error()) + t.Fatal(err) } wantAuthorizerAdmission := &WantAuthorizerAdmission{} target.Initialize(wantAuthorizerAdmission) @@ -46,9 +62,9 @@ func TestWantsAuthorizer(t *testing.T) { // when the WantsExternalKubeClientSet interface is implemented by a plugin. func TestWantsExternalKubeClientSet(t *testing.T) { cs := &fake.Clientset{} - target, err := initializer.New(cs, nil, &TestAuthorizer{}, nil, nil) + target, err := initializer.New(cs, nil, &TestAuthorizer{}, nil, nil, nil) if err != nil { - t.Fatalf("expected to create an instance of initializer but got an error = %s", err.Error()) + t.Fatal(err) } wantExternalKubeClientSet := &WantExternalKubeClientSet{} target.Initialize(wantExternalKubeClientSet) @@ -62,9 +78,9 @@ func TestWantsExternalKubeClientSet(t *testing.T) { func TestWantsExternalKubeInformerFactory(t *testing.T) { cs := &fake.Clientset{} sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second) - target, err := initializer.New(cs, sf, &TestAuthorizer{}, nil, nil) + target, err := initializer.New(cs, sf, &TestAuthorizer{}, nil, nil, nil) if err != nil { - t.Fatalf("expected to create an instance of initializer but got an error = %s", err.Error()) + t.Fatal(err) } wantExternalKubeInformerFactory := &WantExternalKubeInformerFactory{} target.Initialize(wantExternalKubeInformerFactory) @@ -76,9 +92,9 @@ func TestWantsExternalKubeInformerFactory(t *testing.T) { // TestWantsClientCert ensures that the client certificate and key are injected // when the WantsClientCert interface is implemented by a plugin. func TestWantsClientCert(t *testing.T) { - target, err := initializer.New(nil, nil, nil, []byte("cert"), []byte("key")) + target, err := initializer.New(nil, nil, nil, []byte("cert"), []byte("key"), nil) if err != nil { - t.Fatalf("expected to create an instance of initializer but got an error = %s", err.Error()) + t.Fatal(err) } wantClientCert := &clientCertWanter{} target.Initialize(wantClientCert) @@ -144,3 +160,16 @@ func (s *clientCertWanter) SetClientCert(cert, key []byte) { s.gotCert, s.go func (s *clientCertWanter) Admit(a admission.Attributes) error { return nil } func (s *clientCertWanter) Handles(o admission.Operation) bool { return false } func (s *clientCertWanter) Validate() error { return nil } + +// WantSchemeAdmission is a test stub that fulfills the WantsScheme interface. +type WantSchemeAdmission struct { + scheme *runtime.Scheme +} + +func (self *WantSchemeAdmission) SetScheme(s *runtime.Scheme) { self.scheme = s } +func (self *WantSchemeAdmission) Admit(a admission.Attributes) error { return nil } +func (self *WantSchemeAdmission) Handles(o admission.Operation) bool { return false } +func (self *WantSchemeAdmission) Validate() error { return nil } + +var _ admission.Interface = &WantSchemeAdmission{} +var _ initializer.WantsScheme = &WantSchemeAdmission{} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go index b8a72f8c1b..067b4a1da2 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go @@ -17,6 +17,7 @@ limitations under the License. package initializer import ( + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/client-go/informers" @@ -47,3 +48,9 @@ type WantsClientCert interface { SetClientCert(cert, key []byte) admission.Validator } + +// WantsScheme defines a function that accepts runtime.Scheme for admission plugins that need it. +type WantsScheme interface { + SetScheme(*runtime.Scheme) + admission.Validator +} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go index c3e8162e72..b0cad52f82 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go @@ -48,7 +48,7 @@ func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) ( if err != nil { return nil, f, err } - pluginInitializer, err := kubeadmission.New(c, f, nil, nil, nil) + pluginInitializer, err := kubeadmission.New(c, f, nil, nil, nil, nil) if err != nil { return handler, f, err } diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/admission.go b/staging/src/k8s.io/apiserver/pkg/server/options/admission.go index 5b3e3ab3f4..fbac6089b1 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/admission.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/admission.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission/initializer" "k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle" @@ -80,6 +81,7 @@ func (a *AdmissionOptions) ApplyTo( serverIdentifyingClientCert []byte, serverIdentifyingClientKey []byte, clientConfig *rest.Config, + scheme *runtime.Scheme, pluginInitializers ...admission.PluginInitializer, ) error { pluginNames := a.PluginNames @@ -96,7 +98,7 @@ func (a *AdmissionOptions) ApplyTo( if err != nil { return err } - genericInitializer, err := initializer.New(clientset, informers, c.Authorizer, serverIdentifyingClientCert, serverIdentifyingClientKey) + genericInitializer, err := initializer.New(clientset, informers, c.Authorizer, serverIdentifyingClientCert, serverIdentifyingClientKey, scheme) if err != nil { return err } diff --git a/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go b/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go index 8d15c8d846..eb899feb58 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go +++ b/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go @@ -119,7 +119,7 @@ func (o WardleServerOptions) Config() (*apiserver.Config, error) { return nil, err } - if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, nil, nil, serverConfig.ClientConfig, admissionInitializer); err != nil { + if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, nil, nil, serverConfig.ClientConfig, apiserver.Scheme, admissionInitializer); err != nil { return nil, err }