diff --git a/pkg/kubeapiserver/admission/BUILD b/pkg/kubeapiserver/admission/BUILD index cd6d3bcaa96..17b595921d6 100644 --- a/pkg/kubeapiserver/admission/BUILD +++ b/pkg/kubeapiserver/admission/BUILD @@ -14,7 +14,6 @@ go_test( library = ":go_default_library", tags = ["automanaged"], deps = [ - "//pkg/apis/admissionregistration:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", ], @@ -25,7 +24,6 @@ go_library( srcs = ["initializer.go"], tags = ["automanaged"], deps = [ - "//pkg/apis/admissionregistration:go_default_library", "//pkg/client/clientset_generated/clientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library", diff --git a/pkg/kubeapiserver/admission/configuration/BUILD b/pkg/kubeapiserver/admission/configuration/BUILD index 8096c526efd..218ed11ea54 100644 --- a/pkg/kubeapiserver/admission/configuration/BUILD +++ b/pkg/kubeapiserver/admission/configuration/BUILD @@ -10,12 +10,17 @@ load( go_test( name = "go_default_test", - srcs = ["initializer_manager_test.go"], + srcs = [ + "configuration_manager_test.go", + "initializer_manager_test.go", + ], library = ":go_default_library", tags = ["automanaged"], deps = [ "//pkg/apis/admissionregistration/v1alpha1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", ], ) diff --git a/pkg/kubeapiserver/admission/configuration/configuration_manager.go b/pkg/kubeapiserver/admission/configuration/configuration_manager.go index 450befa9539..d31b391c0b7 100644 --- a/pkg/kubeapiserver/admission/configuration/configuration_manager.go +++ b/pkg/kubeapiserver/admission/configuration/configuration_manager.go @@ -26,8 +26,10 @@ import ( ) const ( - defaultInterval = 1 * time.Second - defaultFailureThreshold = 5 + defaultInterval = 1 * time.Second + defaultFailureThreshold = 5 + defaultBootstrapRetries = 5 + defaultBootstrapGraceperiod = 5 * time.Second ) var ( @@ -47,26 +49,43 @@ type poller struct { // a function to consistently read the latest configuration get getFunc // consistent read interval + // read-only interval time.Duration // if the number of consecutive read failure equals or exceeds the failureThreshold , the // configuration is regarded as not ready. + // read-only failureThreshold int // number of consecutive failures so far. failures int + // If the poller has passed the bootstrap phase. The poller is considered + // bootstrapped either bootstrapGracePeriod after the first call of + // configuration(), or when setConfigurationAndReady() is called, whichever + // comes first. + bootstrapped bool + // configuration() retries bootstrapRetries times if poller is not bootstrapped + // read-only + bootstrapRetries int + // Grace period for bootstrapping + // read-only + bootstrapGracePeriod time.Duration + once sync.Once // if the configuration is regarded as ready. ready bool mergedConfiguration runtime.Object - // lock much be hold when reading ready or mergedConfiguration - lock sync.RWMutex - lastErr error + lastErr error + // lock must be hold when reading/writing the data fields of poller. + lock sync.RWMutex } func newPoller(get getFunc) *poller { - return &poller{ - get: get, - interval: defaultInterval, - failureThreshold: defaultFailureThreshold, + p := poller{ + get: get, + interval: defaultInterval, + failureThreshold: defaultFailureThreshold, + bootstrapRetries: defaultBootstrapRetries, + bootstrapGracePeriod: defaultBootstrapGraceperiod, } + return &p } func (a *poller) lastError(err error) { @@ -81,21 +100,47 @@ func (a *poller) notReady() { a.ready = false } +func (a *poller) bootstrapping() { + // bootstrapGracePeriod is read-only, so no lock is required + timer := time.NewTimer(a.bootstrapGracePeriod) + go func() { + <-timer.C + a.lock.Lock() + defer a.lock.Unlock() + a.bootstrapped = true + }() +} + +// If the poller is not bootstrapped yet, the configuration() gets a few chances +// to retry. This hides transient failures during system startup. func (a *poller) configuration() (runtime.Object, error) { + a.once.Do(a.bootstrapping) a.lock.RLock() defer a.lock.RUnlock() - if !a.ready { - if a.lastErr != nil { - return nil, a.lastErr - } - return nil, ErrNotReady + retries := 1 + if !a.bootstrapped { + retries = a.bootstrapRetries } - return a.mergedConfiguration, nil + for count := 0; count < retries; count++ { + if count > 0 { + a.lock.RUnlock() + time.Sleep(a.interval) + a.lock.RLock() + } + if a.ready { + return a.mergedConfiguration, nil + } + } + if a.lastErr != nil { + return nil, a.lastErr + } + return nil, ErrNotReady } func (a *poller) setConfigurationAndReady(value runtime.Object) { a.lock.Lock() defer a.lock.Unlock() + a.bootstrapped = true a.mergedConfiguration = value a.ready = true a.lastErr = nil diff --git a/pkg/kubeapiserver/admission/configuration/configuration_manager_test.go b/pkg/kubeapiserver/admission/configuration/configuration_manager_test.go new file mode 100644 index 00000000000..26c262e3ef6 --- /dev/null +++ b/pkg/kubeapiserver/admission/configuration/configuration_manager_test.go @@ -0,0 +1,93 @@ +/* +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 configuration + +import ( + "fmt" + "math" + "sync" + "testing" + "time" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/wait" +) + +func TestTolerateBootstrapFailure(t *testing.T) { + var fakeGetSucceed bool + var fakeGetSucceedLock sync.RWMutex + fakeGetFn := func() (runtime.Object, error) { + fakeGetSucceedLock.RLock() + defer fakeGetSucceedLock.RUnlock() + if fakeGetSucceed { + return nil, nil + } else { + return nil, fmt.Errorf("this error shouldn't be exposed to caller") + } + } + poller := newPoller(fakeGetFn) + poller.bootstrapGracePeriod = 100 * time.Second + poller.bootstrapRetries = math.MaxInt32 + // set failureThreshold to 0 so that one single failure will set "ready" to false. + poller.failureThreshold = 0 + stopCh := make(chan struct{}) + defer close(stopCh) + go poller.Run(stopCh) + go func() { + // The test might have false negative, but won't be flaky + timer := time.NewTimer(2 * time.Second) + <-timer.C + fakeGetSucceedLock.Lock() + defer fakeGetSucceedLock.Unlock() + fakeGetSucceed = true + }() + + done := make(chan struct{}) + go func(t *testing.T) { + _, err := poller.configuration() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + close(done) + }(t) + <-done +} + +func TestNotTolerateNonbootstrapFailure(t *testing.T) { + fakeGetFn := func() (runtime.Object, error) { + return nil, fmt.Errorf("this error should be exposed to caller") + } + poller := newPoller(fakeGetFn) + poller.bootstrapGracePeriod = 1 * time.Second + poller.interval = 1 * time.Millisecond + stopCh := make(chan struct{}) + defer close(stopCh) + go poller.Run(stopCh) + // to kick the bootstrap timer + go poller.configuration() + + wait.PollInfinite(1*time.Second, func() (bool, error) { + poller.lock.Lock() + defer poller.lock.Unlock() + return poller.bootstrapped, nil + }) + + _, err := poller.configuration() + if err == nil { + t.Errorf("unexpected no error") + } +} diff --git a/pkg/kubeapiserver/admission/init_test.go b/pkg/kubeapiserver/admission/init_test.go index d09e4cc601c..74d24fe58ac 100644 --- a/pkg/kubeapiserver/admission/init_test.go +++ b/pkg/kubeapiserver/admission/init_test.go @@ -22,7 +22,6 @@ import ( "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/authorization/authorizer" - "k8s.io/kubernetes/pkg/apis/admissionregistration" ) // TestAuthorizer is a testing struct for testing that fulfills the authorizer interface. @@ -123,26 +122,3 @@ func TestWantsClientCert(t *testing.T) { t.Errorf("plumbing fail - %v %v", ccw.gotCert, ccw.gotKey) } } - -type fakeHookSource struct{} - -func (f *fakeHookSource) List() ([]admissionregistration.ExternalAdmissionHook, error) { - return nil, nil -} - -type hookSourceWanter struct { - doNothingAdmission - got WebhookSource -} - -func (s *hookSourceWanter) SetWebhookSource(w WebhookSource) { s.got = w } - -func TestWantsWebhookSource(t *testing.T) { - hsw := &hookSourceWanter{} - fhs := &fakeHookSource{} - i := &PluginInitializer{} - i.SetWebhookSource(fhs).Initialize(hsw) - if got, ok := hsw.got.(*fakeHookSource); !ok || got != fhs { - t.Errorf("plumbing fail - %v %v#", ok, got) - } -} diff --git a/pkg/kubeapiserver/admission/initializer.go b/pkg/kubeapiserver/admission/initializer.go index 275fde87f37..95b83dcd3ec 100644 --- a/pkg/kubeapiserver/admission/initializer.go +++ b/pkg/kubeapiserver/admission/initializer.go @@ -22,7 +22,6 @@ import ( "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/authorization/authorizer" - "k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" @@ -83,23 +82,12 @@ type WantsClientCert interface { SetClientCert(cert, key []byte) } -// WantsWebhookSource defines a function that accepts a webhook lister for the -// dynamic webhook plugin. -type WantsWebhookSource interface { - SetWebhookSource(WebhookSource) -} - // ServiceResolver knows how to convert a service reference into an actual // location. type ServiceResolver interface { ResolveEndpoint(namespace, name string) (*url.URL, error) } -// WebhookSource can list dynamic webhook plugins. -type WebhookSource interface { - List() ([]admissionregistration.ExternalAdmissionHook, error) -} - type PluginInitializer struct { internalClient internalclientset.Interface externalClient clientset.Interface @@ -109,7 +97,6 @@ type PluginInitializer struct { restMapper meta.RESTMapper quotaRegistry quota.Registry serviceResolver ServiceResolver - webhookSource WebhookSource // for proving we are apiserver in call-outs clientCert []byte @@ -155,13 +142,6 @@ func (i *PluginInitializer) SetClientCert(cert, key []byte) *PluginInitializer { return i } -// SetWebhookSource sets the webhook source-- admittedly this is probably -// specific to the external admission hook plugin. -func (i *PluginInitializer) SetWebhookSource(w WebhookSource) *PluginInitializer { - i.webhookSource = w - return i -} - // Initialize checks the initialization interfaces implemented by each plugin // and provide the appropriate initialization data func (i *PluginInitializer) Initialize(plugin admission.Interface) { @@ -206,11 +186,4 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) { } wants.SetClientCert(i.clientCert, i.clientKey) } - - if wants, ok := plugin.(WantsWebhookSource); ok { - if i.webhookSource == nil { - panic("An admission plugin wants a webhook source, but it was not provided.") - } - wants.SetWebhookSource(i.webhookSource) - } } diff --git a/plugin/pkg/admission/webhook/BUILD b/plugin/pkg/admission/webhook/BUILD index a6466fffa00..5faad954b69 100644 --- a/plugin/pkg/admission/webhook/BUILD +++ b/plugin/pkg/admission/webhook/BUILD @@ -21,7 +21,7 @@ go_test( "//pkg/api:go_default_library", "//pkg/apis/admission/install:go_default_library", "//pkg/apis/admission/v1alpha1:go_default_library", - "//pkg/apis/admissionregistration:go_default_library", + "//pkg/apis/admissionregistration/v1alpha1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", @@ -41,14 +41,18 @@ go_library( "//pkg/api:go_default_library", "//pkg/apis/admission/install:go_default_library", "//pkg/apis/admission/v1alpha1:go_default_library", - "//pkg/apis/admissionregistration:go_default_library", + "//pkg/apis/admissionregistration/v1alpha1:go_default_library", + "//pkg/client/clientset_generated/clientset:go_default_library", "//pkg/kubeapiserver/admission:go_default_library", + "//pkg/kubeapiserver/admission/configuration:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", ], diff --git a/plugin/pkg/admission/webhook/admission.go b/plugin/pkg/admission/webhook/admission.go index 7575d7aed80..6236fb4fa98 100644 --- a/plugin/pkg/admission/webhook/admission.go +++ b/plugin/pkg/admission/webhook/admission.go @@ -27,16 +27,20 @@ import ( "github.com/golang/glog" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/admission" "k8s.io/client-go/rest" "k8s.io/kubernetes/pkg/api" admissionv1alpha1 "k8s.io/kubernetes/pkg/apis/admission/v1alpha1" - "k8s.io/kubernetes/pkg/apis/admissionregistration" + "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1" + "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" admissioninit "k8s.io/kubernetes/pkg/kubeapiserver/admission" + "k8s.io/kubernetes/pkg/kubeapiserver/admission/configuration" // install the clientgo admission API for use with api registry _ "k8s.io/kubernetes/pkg/apis/admission/install" @@ -72,6 +76,12 @@ func Register(plugins *admission.Plugins) { }) } +// WebhookSource can list dynamic webhook plugins. +type WebhookSource interface { + Run(stopCh <-chan struct{}) + ExternalAdmissionHooks() (*v1alpha1.ExternalAdmissionHookConfiguration, error) +} + // NewGenericAdmissionWebhook returns a generic admission webhook plugin. func NewGenericAdmissionWebhook() (*GenericAdmissionWebhook, error) { return &GenericAdmissionWebhook{ @@ -90,7 +100,7 @@ func NewGenericAdmissionWebhook() (*GenericAdmissionWebhook, error) { // GenericAdmissionWebhook is an implementation of admission.Interface. type GenericAdmissionWebhook struct { *admission.Handler - hookSource admissioninit.WebhookSource + hookSource WebhookSource serviceResolver admissioninit.ServiceResolver negotiatedSerializer runtime.NegotiatedSerializer clientCert []byte @@ -100,7 +110,7 @@ type GenericAdmissionWebhook struct { var ( _ = admissioninit.WantsServiceResolver(&GenericAdmissionWebhook{}) _ = admissioninit.WantsClientCert(&GenericAdmissionWebhook{}) - _ = admissioninit.WantsWebhookSource(&GenericAdmissionWebhook{}) + _ = admissioninit.WantsExternalKubeClientSet(&GenericAdmissionWebhook{}) ) func (a *GenericAdmissionWebhook) SetServiceResolver(sr admissioninit.ServiceResolver) { @@ -112,23 +122,51 @@ func (a *GenericAdmissionWebhook) SetClientCert(cert, key []byte) { a.clientKey = key } -func (a *GenericAdmissionWebhook) SetWebhookSource(ws admissioninit.WebhookSource) { - a.hookSource = ws +func (a *GenericAdmissionWebhook) SetExternalKubeClientSet(client clientset.Interface) { + a.hookSource = configuration.NewExternalAdmissionHookConfigurationManager(client.Admissionregistration().ExternalAdmissionHookConfigurations()) +} + +func (a *GenericAdmissionWebhook) Validate() error { + if a.hookSource == nil { + return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a Kubernetes client to be provided") + } + go a.hookSource.Run(wait.NeverStop) + return nil +} + +func (a *GenericAdmissionWebhook) loadConfiguration(attr admission.Attributes) (*v1alpha1.ExternalAdmissionHookConfiguration, error) { + hookConfig, err := a.hookSource.ExternalAdmissionHooks() + // if ExternalAdmissionHook configuration is disabled, fail open + if err == configuration.ErrDisabled { + return &v1alpha1.ExternalAdmissionHookConfiguration{}, nil + } + if err != nil { + e := apierrors.NewServerTimeout(attr.GetResource().GroupResource(), string(attr.GetOperation()), 1) + e.ErrStatus.Message = fmt.Sprintf("Unable to refresh the ExternalAdmissionHook configuration: %v", err) + e.ErrStatus.Reason = "LoadingConfiguration" + e.ErrStatus.Details.Causes = append(e.ErrStatus.Details.Causes, metav1.StatusCause{ + Type: "ExternalAdmissionHookConfigurationFailure", + Message: "An error has occurred while refreshing the externalAdmissionHook configuration, no resources can be created/updated/deleted/connected until a refresh succeeds.", + }) + return nil, e + } + return hookConfig, nil } // Admit makes an admission decision based on the request attributes. func (a *GenericAdmissionWebhook) Admit(attr admission.Attributes) error { - hooks, err := a.hookSource.List() + hookConfig, err := a.loadConfiguration(attr) if err != nil { - return fmt.Errorf("failed listing hooks: %v", err) + return err } + hooks := hookConfig.ExternalAdmissionHooks ctx := context.TODO() errCh := make(chan error, len(hooks)) wg := sync.WaitGroup{} wg.Add(len(hooks)) for i := range hooks { - go func(hook *admissionregistration.ExternalAdmissionHook) { + go func(hook *v1alpha1.ExternalAdmissionHook) { defer wg.Done() if err := a.callHook(ctx, hook, attr); err == nil { return @@ -161,7 +199,7 @@ func (a *GenericAdmissionWebhook) Admit(attr admission.Attributes) error { return errs[0] } -func (a *GenericAdmissionWebhook) callHook(ctx context.Context, h *admissionregistration.ExternalAdmissionHook, attr admission.Attributes) error { +func (a *GenericAdmissionWebhook) callHook(ctx context.Context, h *v1alpha1.ExternalAdmissionHook, attr admission.Attributes) error { matches := false for _, r := range h.Rules { m := RuleMatcher{Rule: r, Attr: attr} @@ -197,7 +235,7 @@ func (a *GenericAdmissionWebhook) callHook(ctx context.Context, h *admissionregi } } -func (a *GenericAdmissionWebhook) hookClient(h *admissionregistration.ExternalAdmissionHook) (*rest.RESTClient, error) { +func (a *GenericAdmissionWebhook) hookClient(h *v1alpha1.ExternalAdmissionHook) (*rest.RESTClient, error) { u, err := a.serviceResolver.ResolveEndpoint(h.ClientConfig.Service.Namespace, h.ClientConfig.Service.Name) if err != nil { return nil, err diff --git a/plugin/pkg/admission/webhook/admission_test.go b/plugin/pkg/admission/webhook/admission_test.go index 15f331f6510..5e2380421bb 100644 --- a/plugin/pkg/admission/webhook/admission_test.go +++ b/plugin/pkg/admission/webhook/admission_test.go @@ -32,23 +32,25 @@ import ( "k8s.io/apiserver/pkg/authentication/user" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/admission/v1alpha1" - "k8s.io/kubernetes/pkg/apis/admissionregistration" + registrationv1alpha1 "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1" _ "k8s.io/kubernetes/pkg/apis/admission/install" ) type fakeHookSource struct { - hooks []admissionregistration.ExternalAdmissionHook + hooks []registrationv1alpha1.ExternalAdmissionHook err error } -func (f *fakeHookSource) List() ([]admissionregistration.ExternalAdmissionHook, error) { +func (f *fakeHookSource) ExternalAdmissionHooks() (*registrationv1alpha1.ExternalAdmissionHookConfiguration, error) { if f.err != nil { return nil, f.err } - return f.hooks, nil + return ®istrationv1alpha1.ExternalAdmissionHookConfiguration{ExternalAdmissionHooks: f.hooks}, nil } +func (f *fakeHookSource) Run(stopCh <-chan struct{}) {} + type fakeServiceResolver struct { base url.URL } @@ -124,17 +126,17 @@ func TestAdmit(t *testing.T) { expectAllow bool errorContains string } - ccfg := func(result string) admissionregistration.AdmissionHookClientConfig { - return admissionregistration.AdmissionHookClientConfig{ - Service: admissionregistration.ServiceReference{ + ccfg := func(result string) registrationv1alpha1.AdmissionHookClientConfig { + return registrationv1alpha1.AdmissionHookClientConfig{ + Service: registrationv1alpha1.ServiceReference{ Name: result, }, CABundle: caCert, } } - matchEverythingRules := []admissionregistration.RuleWithOperations{{ - Operations: []admissionregistration.OperationType{admissionregistration.OperationAll}, - Rule: admissionregistration.Rule{ + matchEverythingRules := []registrationv1alpha1.RuleWithOperations{{ + Operations: []registrationv1alpha1.OperationType{registrationv1alpha1.OperationAll}, + Rule: registrationv1alpha1.Rule{ APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*/*"}, @@ -144,11 +146,11 @@ func TestAdmit(t *testing.T) { table := map[string]test{ "no match": { hookSource: fakeHookSource{ - hooks: []admissionregistration.ExternalAdmissionHook{{ + hooks: []registrationv1alpha1.ExternalAdmissionHook{{ Name: "nomatch", ClientConfig: ccfg("disallow"), - Rules: []admissionregistration.RuleWithOperations{{ - Operations: []admissionregistration.OperationType{admissionregistration.Create}, + Rules: []registrationv1alpha1.RuleWithOperations{{ + Operations: []registrationv1alpha1.OperationType{registrationv1alpha1.Create}, }}, }}, }, @@ -156,7 +158,7 @@ func TestAdmit(t *testing.T) { }, "match & allow": { hookSource: fakeHookSource{ - hooks: []admissionregistration.ExternalAdmissionHook{{ + hooks: []registrationv1alpha1.ExternalAdmissionHook{{ Name: "allow", ClientConfig: ccfg("allow"), Rules: matchEverythingRules, @@ -166,7 +168,7 @@ func TestAdmit(t *testing.T) { }, "match & disallow": { hookSource: fakeHookSource{ - hooks: []admissionregistration.ExternalAdmissionHook{{ + hooks: []registrationv1alpha1.ExternalAdmissionHook{{ Name: "disallow", ClientConfig: ccfg("disallow"), Rules: matchEverythingRules, @@ -176,7 +178,7 @@ func TestAdmit(t *testing.T) { }, "match & disallow ii": { hookSource: fakeHookSource{ - hooks: []admissionregistration.ExternalAdmissionHook{{ + hooks: []registrationv1alpha1.ExternalAdmissionHook{{ Name: "disallowReason", ClientConfig: ccfg("disallowReason"), Rules: matchEverythingRules, @@ -186,7 +188,7 @@ func TestAdmit(t *testing.T) { }, "match & fail (but allow because fail open)": { hookSource: fakeHookSource{ - hooks: []admissionregistration.ExternalAdmissionHook{{ + hooks: []registrationv1alpha1.ExternalAdmissionHook{{ Name: "internalErr A", ClientConfig: ccfg("internalErr"), Rules: matchEverythingRules, diff --git a/plugin/pkg/admission/webhook/rules.go b/plugin/pkg/admission/webhook/rules.go index 2ae2afee5a1..70af1032205 100644 --- a/plugin/pkg/admission/webhook/rules.go +++ b/plugin/pkg/admission/webhook/rules.go @@ -21,11 +21,11 @@ import ( "strings" "k8s.io/apiserver/pkg/admission" - "k8s.io/kubernetes/pkg/apis/admissionregistration" + "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1" ) type RuleMatcher struct { - Rule admissionregistration.RuleWithOperations + Rule v1alpha1.RuleWithOperations Attr admission.Attributes } @@ -60,12 +60,12 @@ func (r *RuleMatcher) version() bool { func (r *RuleMatcher) operation() bool { attrOp := r.Attr.GetOperation() for _, op := range r.Rule.Operations { - if op == admissionregistration.OperationAll { + if op == v1alpha1.OperationAll { return true } // The constants are the same such that this is a valid cast (and this // is tested). - if op == admissionregistration.OperationType(attrOp) { + if op == v1alpha1.OperationType(attrOp) { return true } } diff --git a/plugin/pkg/admission/webhook/rules_test.go b/plugin/pkg/admission/webhook/rules_test.go index dc6a79fbc3a..ad063ef74ed 100644 --- a/plugin/pkg/admission/webhook/rules_test.go +++ b/plugin/pkg/admission/webhook/rules_test.go @@ -21,7 +21,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" - adreg "k8s.io/kubernetes/pkg/apis/admissionregistration" + adreg "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1" ) type ruleTest struct {