From 7006d224bebb5a1aee9c70387a8584e0a0e8b10f Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Fri, 27 Oct 2017 14:42:09 -0700 Subject: [PATCH 1/2] add NamespaceSelector to the api business logic in webhook plugin and unit test add a e2e test for namespace selector --- pkg/apis/admissionregistration/types.go | 46 +++++ .../v1alpha1/defaults.go | 5 + .../validation/validation.go | 5 + .../admissionregistration/v1alpha1/types.go | 46 +++++ .../pkg/admission/plugin/webhook/admission.go | 84 ++++++++ .../plugin/webhook/admission_test.go | 194 ++++++++++++++++-- test/e2e/apimachinery/webhook.go | 100 ++++++++- test/images/webhook/Makefile | 4 +- test/images/webhook/main.go | 56 ++++- 9 files changed, 510 insertions(+), 30 deletions(-) diff --git a/pkg/apis/admissionregistration/types.go b/pkg/apis/admissionregistration/types.go index f92f6f89e2a..5e81ce43c92 100644 --- a/pkg/apis/admissionregistration/types.go +++ b/pkg/apis/admissionregistration/types.go @@ -191,6 +191,52 @@ type Webhook struct { // allowed values are Ignore or Fail. Defaults to Ignore. // +optional FailurePolicy *FailurePolicyType + + // NamespaceSelector decides whether to run the webhook on an object based + // on whether the namespace for that object matches the selector. If the + // object itself is a namespace, the matching is performed on + // object.metadata.labels. If the object is other cluster scoped resource, + // it is not subjected to the webhook. + // + // For example, to run the webhook on any objects whose namespace is not + // associated with "runlevel" of "0" or "1"; you will set the selector as + // follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "runlevel", + // "operator": "NotIn", + // "values": [ + // "0", + // "1" + // ] + // } + // ] + // } + // + // If instead you want to only run the webhook on any objects whose + // namespace is associated with the "environment" of "prod" or "staging"; + // you will set the selector as follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "environment", + // "operator": "In", + // "values": [ + // "prod", + // "staging" + // ] + // } + // ] + // } + // + // See + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // for more examples of label selectors. + // + // Default to the empty LabelSelector, which matches everything. + // +optional + NamespaceSelector *metav1.LabelSelector } // RuleWithOperations is a tuple of Operations and Resources. It is recommended to make diff --git a/pkg/apis/admissionregistration/v1alpha1/defaults.go b/pkg/apis/admissionregistration/v1alpha1/defaults.go index 92fe685c768..3170c28727a 100644 --- a/pkg/apis/admissionregistration/v1alpha1/defaults.go +++ b/pkg/apis/admissionregistration/v1alpha1/defaults.go @@ -18,6 +18,7 @@ package v1alpha1 import ( admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -30,4 +31,8 @@ func SetDefaults_Webhook(obj *admissionregistrationv1alpha1.Webhook) { policy := admissionregistrationv1alpha1.Ignore obj.FailurePolicy = &policy } + if obj.NamespaceSelector == nil { + selector := metav1.LabelSelector{} + obj.NamespaceSelector = &selector + } } diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go index e907487f334..11df7ac4f57 100644 --- a/pkg/apis/admissionregistration/validation/validation.go +++ b/pkg/apis/admissionregistration/validation/validation.go @@ -21,6 +21,7 @@ import ( "strings" genericvalidation "k8s.io/apimachinery/pkg/api/validation" + metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" @@ -195,6 +196,10 @@ func validateWebhook(hook *admissionregistration.Webhook, fldPath *field.Path) f allErrors = append(allErrors, validateURLPath(fldPath.Child("clientConfig", "urlPath"), hook.ClientConfig.URLPath)...) } + if hook.NamespaceSelector != nil { + allErrors = append(allErrors, metav1validation.ValidateLabelSelector(hook.NamespaceSelector, fldPath.Child("namespaceSelector"))...) + } + return allErrors } diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/types.go b/staging/src/k8s.io/api/admissionregistration/v1alpha1/types.go index 9ac0576bfd7..9232c1eb34f 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/types.go +++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/types.go @@ -197,6 +197,52 @@ type Webhook struct { // allowed values are Ignore or Fail. Defaults to Ignore. // +optional FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" protobuf:"bytes,4,opt,name=failurePolicy,casttype=FailurePolicyType"` + + // NamespaceSelector decides whether to run the webhook on an object based + // on whether the namespace for that object matches the selector. If the + // object itself is a namespace, the matching is performed on + // object.metadata.labels. If the object is other cluster scoped resource, + // it is not subjected to the webhook. + // + // For example, to run the webhook on any objects whose namespace is not + // associated with "runlevel" of "0" or "1"; you will set the selector as + // follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "runlevel", + // "operator": "NotIn", + // "values": [ + // "0", + // "1" + // ] + // } + // ] + // } + // + // If instead you want to only run the webhook on any objects whose + // namespace is associated with the "environment" of "prod" or "staging"; + // you will set the selector as follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "environment", + // "operator": "In", + // "values": [ + // "prod", + // "staging" + // ] + // } + // ] + // } + // + // See + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // for more examples of label selectors. + // + // Default to the empty LabelSelector, which matches everything. + // +optional + NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,5,opt,name=namespaceSelector"` } // RuleWithOperations is a tuple of Operations and Resources. It is recommended to make diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission.go index af4c3a9c249..96f5b1ea34f 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission.go @@ -32,7 +32,9 @@ import ( admissionv1alpha1 "k8s.io/api/admission/v1alpha1" "k8s.io/api/admissionregistration/v1alpha1" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -41,7 +43,9 @@ import ( "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission/configuration" genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer" + "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" + corelisters "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/rest" ) @@ -123,6 +127,8 @@ type GenericAdmissionWebhook struct { hookSource WebhookSource serviceResolver ServiceResolver negotiatedSerializer runtime.NegotiatedSerializer + namespaceLister corelisters.NamespaceLister + client clientset.Interface authInfoResolver AuthenticationInfoResolver cache *lru.Cache @@ -163,9 +169,17 @@ func (a *GenericAdmissionWebhook) SetScheme(scheme *runtime.Scheme) { // WantsExternalKubeClientSet defines a function which sets external ClientSet for admission plugins that need it func (a *GenericAdmissionWebhook) SetExternalKubeClientSet(client clientset.Interface) { + a.client = client a.hookSource = configuration.NewValidatingWebhookConfigurationManager(client.AdmissionregistrationV1alpha1().ValidatingWebhookConfigurations()) } +// SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface. +func (a *GenericAdmissionWebhook) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { + namespaceInformer := f.Core().V1().Namespaces() + a.namespaceLister = namespaceInformer.Lister() + a.SetReadyFunc(namespaceInformer.Informer().HasSynced) +} + // ValidateInitialization implements the InitializationValidator interface. func (a *GenericAdmissionWebhook) ValidateInitialization() error { if a.hookSource == nil { @@ -174,6 +188,9 @@ func (a *GenericAdmissionWebhook) ValidateInitialization() error { if a.negotiatedSerializer == nil { return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a runtime.Scheme to be provided to derive a serializer") } + if a.namespaceLister == nil { + return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a namespaceLister") + } go a.hookSource.Run(wait.NeverStop) return nil } @@ -255,7 +272,74 @@ func (a *GenericAdmissionWebhook) Admit(attr admission.Attributes) error { return errs[0] } +func (a *GenericAdmissionWebhook) getNamespaceLabels(attr admission.Attributes) (map[string]string, error) { + // If the request itself is creating or updating a namespace, then get the + // labels from attr.Object, because namespaceLister doesn't have the latest + // namespace yet. + // + // However, if the request is deleting a namespace, then get the label from + // the namespace in the namespaceLister, because a delete request is not + // going to change the object, and attr.Object will be a DeleteOptions + // rather than a namespace object. + if attr.GetResource().Resource == "namespaces" && + len(attr.GetSubresource()) == 0 && + (attr.GetOperation() == admission.Create || attr.GetOperation() == admission.Update) { + accessor, err := meta.Accessor(attr.GetObject()) + if err != nil { + return nil, err + } + return accessor.GetLabels(), nil + } + + namespaceName := attr.GetNamespace() + namespace, err := a.namespaceLister.Get(namespaceName) + if err != nil && !apierrors.IsNotFound(err) { + return nil, err + } + if apierrors.IsNotFound(err) { + // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not + namespace, err = a.client.Core().Namespaces().Get(namespaceName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + } + return namespace.Labels, nil +} + +// whether the request is exempted by the webhook because of the +// namespaceSelector of the webhook. +func (a *GenericAdmissionWebhook) exemptedByNamespaceSelector(h *v1alpha1.Webhook, attr admission.Attributes) (bool, error) { + namespaceName := attr.GetNamespace() + if len(namespaceName) == 0 && attr.GetResource().Resource != "namespaces" { + // If the request is about a cluster scoped resource, and it is not a + // namespace, it is exempted from all webhooks for now. + // TODO: figure out a way selective exempt cluster scoped resources. + // Also update the comment in types.go + return true, nil + } + namespaceLabels, err := a.getNamespaceLabels(attr) + if apierrors.IsNotFound(err) { + return false, err + } + if err != nil { + return false, apierrors.NewInternalError(err) + } + // TODO: adding an LRU cache to cache the translation + selector, err := metav1.LabelSelectorAsSelector(h.NamespaceSelector) + if err != nil { + return false, apierrors.NewInternalError(err) + } + return !selector.Matches(labels.Set(namespaceLabels)), nil +} + func (a *GenericAdmissionWebhook) callHook(ctx context.Context, h *v1alpha1.Webhook, attr admission.Attributes) error { + excluded, err := a.exemptedByNamespaceSelector(h, attr) + if err != nil { + return err + } + if excluded { + return nil + } matches := false for _, r := range h.Rules { m := RuleMatcher{Rule: r, Attr: attr} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission_test.go index 39ad0c34f7b..25308fa230c 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/admission_test.go @@ -24,16 +24,20 @@ import ( "net/http" "net/http/httptest" "net/url" + "reflect" "strings" "sync/atomic" "testing" "k8s.io/api/admission/v1alpha1" registrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1" - api "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/client-go/rest" @@ -48,6 +52,11 @@ func (f *fakeHookSource) Webhooks() (*registrationv1alpha1.ValidatingWebhookConf if f.err != nil { return nil, f.err } + for i, h := range f.hooks { + if h.NamespaceSelector == nil { + f.hooks[i].NamespaceSelector = &metav1.LabelSelector{} + } + } return ®istrationv1alpha1.ValidatingWebhookConfiguration{Webhooks: f.hooks}, nil } @@ -65,11 +74,26 @@ func (f fakeServiceResolver) ResolveEndpoint(namespace, name string) (*url.URL, return &u, nil } +type fakeNamespaceLister struct { + namespaces map[string]*corev1.Namespace +} + +func (f fakeNamespaceLister) List(selector labels.Selector) (ret []*corev1.Namespace, err error) { + return nil, nil +} +func (f fakeNamespaceLister) Get(name string) (*corev1.Namespace, error) { + ns, ok := f.namespaces[name] + if ok { + return ns, nil + } + return nil, errors.NewNotFound(corev1.Resource("namespaces"), name) +} + // TestAdmit tests that GenericAdmissionWebhook#Admit works as expected func TestAdmit(t *testing.T) { scheme := runtime.NewScheme() v1alpha1.AddToScheme(scheme) - api.AddToScheme(scheme) + corev1.AddToScheme(scheme) testServer := newTestServer(t) testServer.StartTLS() @@ -85,12 +109,22 @@ func TestAdmit(t *testing.T) { wh.authInfoResolver = newFakeAuthenticationInfoResolver() wh.serviceResolver = fakeServiceResolver{base: *serverURL} wh.SetScheme(scheme) + namespace := "webhook-test" + wh.namespaceLister = fakeNamespaceLister{map[string]*corev1.Namespace{ + namespace: { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "runlevel": "0", + }, + }, + }, + }, + } // Set up a test object for the call - kind := api.SchemeGroupVersion.WithKind("Pod") + kind := corev1.SchemeGroupVersion.WithKind("Pod") name := "my-pod" - namespace := "webhook-test" - object := api.Pod{ + object := corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ "pod.name": name, @@ -103,11 +137,11 @@ func TestAdmit(t *testing.T) { Kind: "Pod", }, } - oldObject := api.Pod{ + oldObject := corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, } operation := admission.Update - resource := api.Resource("pods").WithVersion("v1") + resource := corev1.Resource("pods").WithVersion("v1") subResource := "" userInfo := user.DefaultInfo{ Name: "webhook-test", @@ -167,6 +201,40 @@ func TestAdmit(t *testing.T) { }, errorContains: "you shall not pass", }, + "match & disallow & but allowed because namespaceSelector exempt the namespace": { + hookSource: fakeHookSource{ + hooks: []registrationv1alpha1.Webhook{{ + Name: "disallow", + ClientConfig: newFakeHookClientConfig("disallow"), + Rules: newMatchEverythingRules(), + NamespaceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "runlevel", + Values: []string{"1"}, + Operator: metav1.LabelSelectorOpIn, + }}, + }, + }}, + }, + expectAllow: true, + }, + "match & disallow & but allowed because namespaceSelector exempt the namespace ii": { + hookSource: fakeHookSource{ + hooks: []registrationv1alpha1.Webhook{{ + Name: "disallow", + ClientConfig: newFakeHookClientConfig("disallow"), + Rules: newMatchEverythingRules(), + NamespaceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "runlevel", + Values: []string{"0"}, + Operator: metav1.LabelSelectorOpNotIn, + }}, + }, + }}, + }, + expectAllow: true, + }, "match & fail (but allow because fail open)": { hookSource: fakeHookSource{ hooks: []registrationv1alpha1.Webhook{{ @@ -230,9 +298,11 @@ func TestAdmit(t *testing.T) { } for name, tt := range table { + if !strings.Contains(name, "no match") { + continue + } t.Run(name, func(t *testing.T) { wh.hookSource = &tt.hookSource - err = wh.Admit(admission.NewAttributesRecord(&object, &oldObject, kind, namespace, name, resource, subResource, operation, &userInfo)) if tt.expectAllow != (err == nil) { t.Errorf("expected allowed=%v, but got err=%v", tt.expectAllow, err) @@ -254,7 +324,7 @@ func TestAdmit(t *testing.T) { func TestAdmitCachedClient(t *testing.T) { scheme := runtime.NewScheme() v1alpha1.AddToScheme(scheme) - api.AddToScheme(scheme) + corev1.AddToScheme(scheme) testServer := newTestServer(t) testServer.StartTLS() @@ -270,12 +340,22 @@ func TestAdmitCachedClient(t *testing.T) { wh.authInfoResolver = newFakeAuthenticationInfoResolver() wh.serviceResolver = fakeServiceResolver{base: *serverURL} wh.SetScheme(scheme) + namespace := "webhook-test" + wh.namespaceLister = fakeNamespaceLister{map[string]*corev1.Namespace{ + namespace: { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "runlevel": "0", + }, + }, + }, + }, + } // Set up a test object for the call - kind := api.SchemeGroupVersion.WithKind("Pod") + kind := corev1.SchemeGroupVersion.WithKind("Pod") name := "my-pod" - namespace := "webhook-test" - object := api.Pod{ + object := corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ "pod.name": name, @@ -288,11 +368,11 @@ func TestAdmitCachedClient(t *testing.T) { Kind: "Pod", }, } - oldObject := api.Pod{ + oldObject := corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, } operation := admission.Update - resource := api.Resource("pods").WithVersion("v1") + resource := corev1.Resource("pods").WithVersion("v1") subResource := "" userInfo := user.DefaultInfo{ Name: "webhook-test", @@ -522,3 +602,89 @@ func newMatchEverythingRules() []registrationv1alpha1.RuleWithOperations { }, }} } + +func TestGetNamespaceLabels(t *testing.T) { + namespace1Labels := map[string]string{ + "runlevel": "1", + } + namespace1 := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "1", + Labels: namespace1Labels, + }, + } + namespace2Labels := map[string]string{ + "runlevel": "2", + } + namespace2 := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "2", + Labels: namespace2Labels, + }, + } + namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{ + "1": &namespace1, + }, + } + + tests := []struct { + name string + attr admission.Attributes + expectedLabels map[string]string + }{ + { + name: "request is for creating namespace, the labels should be from the object itself", + attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, "", namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Create, nil), + expectedLabels: namespace2Labels, + }, + { + name: "request is for updating namespace, the labels should be from the new object", + attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace2.Name, namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Update, nil), + expectedLabels: namespace2Labels, + }, + { + name: "request is for deleting namespace, the labels should be from the cache", + attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace1.Name, namespace1.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Delete, nil), + expectedLabels: namespace1Labels, + }, + { + name: "request is for namespace/finalizer", + attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "namespaces"}, "finalizers", admission.Create, nil), + expectedLabels: namespace1Labels, + }, + { + name: "request is for pod", + attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "pods"}, "", admission.Create, nil), + expectedLabels: namespace1Labels, + }, + } + wh, err := NewGenericAdmissionWebhook(nil) + if err != nil { + t.Fatal(err) + } + wh.namespaceLister = namespaceLister + for _, tt := range tests { + actualLabels, err := wh.getNamespaceLabels(tt.attr) + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(actualLabels, tt.expectedLabels) { + t.Errorf("expected labels to be %#v, got %#v", tt.expectedLabels, actualLabels) + } + } +} + +func TestExemptClusterScopedResource(t *testing.T) { + hook := ®istrationv1alpha1.Webhook{ + NamespaceSelector: &metav1.LabelSelector{}, + } + attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, nil) + g := GenericAdmissionWebhook{} + exempted, err := g.exemptedByNamespaceSelector(hook, attr) + if err != nil { + t.Fatal(err) + } + if !exempted { + t.Errorf("cluster scoped resources (but not a namespace) should be exempted from all webhooks") + } +} diff --git a/test/e2e/apimachinery/webhook.go b/test/e2e/apimachinery/webhook.go index 22c0e1af69b..08e02b82435 100644 --- a/test/e2e/apimachinery/webhook.go +++ b/test/e2e/apimachinery/webhook.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/wait" utilversion "k8s.io/kubernetes/pkg/util/version" "k8s.io/kubernetes/test/e2e/framework" @@ -36,11 +37,16 @@ import ( ) const ( - secretName = "sample-webhook-secret" - deploymentName = "sample-webhook-deployment" - serviceName = "e2e-test-webhook" - roleBindingName = "webhook-auth-reader" - webhookConfigName = "e2e-test-webhook-config" + secretName = "sample-webhook-secret" + deploymentName = "sample-webhook-deployment" + serviceName = "e2e-test-webhook" + roleBindingName = "webhook-auth-reader" + webhookConfigName = "e2e-test-webhook-config" + skipNamespaceLabelKey = "skip-webhook-admission" + skipNamespaceLabelValue = "yes" + skippedNamespaceName = "exempted-namesapce" + disallowedPodName = "disallowed-pod" + disallowedConfigMapName = "disallowed-configmap" ) var serverWebhookVersion = utilversion.MustParseSemantic("v1.8.0") @@ -51,7 +57,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() { cleanWebhookTest(f) }) - It("Should be able to deny pod creation", func() { + It("Should be able to deny pod and configmap creation", func() { // Make sure the relevant provider supports admission webhook framework.SkipUnlessServerVersionGTE(serverWebhookVersion, f.ClientSet.Discovery()) framework.SkipUnlessProviderIs("gce", "gke") @@ -68,7 +74,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() { // Note that in 1.9 we will have backwards incompatible change to // admission webhooks, so the image will be updated to 1.9 sometime in // the development 1.9 cycle. - deployWebhookAndService(f, "gcr.io/kubernetes-e2e-test-images/k8s-sample-admission-webhook-amd64:1.8v1", context) + deployWebhookAndService(f, "gcr.io/kubernetes-e2e-test-images/k8s-sample-admission-webhook-amd64:1.8v2", context) registerWebhook(f, context) testWebhook(f) }) @@ -223,7 +229,7 @@ func registerWebhook(f *framework.Framework, context *certContext) { }, Webhooks: []v1alpha1.Webhook{ { - Name: "e2e-test-webhook.k8s.io", + Name: "deny-unwanted-pod-container-name-and-label.k8s.io", Rules: []v1alpha1.RuleWithOperations{{ Operations: []v1alpha1.OperationType{v1alpha1.Create}, Rule: v1alpha1.Rule{ @@ -237,6 +243,36 @@ func registerWebhook(f *framework.Framework, context *certContext) { Namespace: namespace, Name: serviceName, }, + URLPath: "/pods", + CABundle: context.signingCert, + }, + }, + { + Name: "deny-unwanted-configmap-data.k8s.io", + Rules: []v1alpha1.RuleWithOperations{{ + Operations: []v1alpha1.OperationType{v1alpha1.Create}, + Rule: v1alpha1.Rule{ + APIGroups: []string{""}, + APIVersions: []string{"v1"}, + Resources: []string{"configmaps"}, + }, + }}, + // The webhook skips the namespace that has label "skip-webhook-admission":"yes" + NamespaceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: skipNamespaceLabelKey, + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{skipNamespaceLabelValue}, + }, + }, + }, + ClientConfig: v1alpha1.WebhookClientConfig{ + Service: v1alpha1.ServiceReference{ + Namespace: namespace, + Name: serviceName, + }, + URLPath: "/configmaps", CABundle: context.signingCert, }, }, @@ -262,12 +298,45 @@ func testWebhook(f *framework.Framework) { // TODO: Test if webhook can detect pod with non-compliant metadata. // Currently metadata is lost because webhook uses the external version of // the objects, and the apiserver sends the internal objects. + + By("create a configmap that should be denied by the webhook") + // Creating the configmap, the request should be rejected + configmap := nonCompliantConfigMap(f) + _, err = client.CoreV1().ConfigMaps(f.Namespace.Name).Create(configmap) + Expect(err).NotTo(BeNil()) + expectedErrMsg = "the configmap contains unwanted key and value" + if !strings.Contains(err.Error(), expectedErrMsg) { + framework.Failf("expect error contains %q, got %q", expectedErrMsg, err.Error()) + } + + By("create a namespace that bypass the webhook") + err = wait.Poll(100*time.Millisecond, 30*time.Second, func() (bool, error) { + _, err2 := client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{ + Name: skippedNamespaceName, + Labels: map[string]string{ + skipNamespaceLabelKey: skipNamespaceLabelValue, + }, + }}) + if err2 != nil { + if strings.HasPrefix(err2.Error(), "object is being deleted:") { + return false, nil + } + return false, err2 + } + return true, nil + }) + framework.ExpectNoError(err, "creating namespace %q", skippedNamespaceName) + + By("create a configmap that violates the webhook policy but is in a whitelisted namespace") + configmap = nonCompliantConfigMap(f) + _, err = client.CoreV1().ConfigMaps(skippedNamespaceName).Create(configmap) + Expect(err).To(BeNil()) } func nonCompliantPod(f *framework.Framework) *v1.Pod { return &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: "disallowed-pod", + Name: disallowedPodName, Labels: map[string]string{ "webhook-e2e-test": "disallow", }, @@ -283,6 +352,17 @@ func nonCompliantPod(f *framework.Framework) *v1.Pod { } } +func nonCompliantConfigMap(f *framework.Framework) *v1.ConfigMap { + return &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: disallowedConfigMapName, + }, + Data: map[string]string{ + "webhook-e2e-test": "webhook-disallow", + }, + } +} + func cleanWebhookTest(f *framework.Framework) { client := f.ClientSet _ = client.AdmissionregistrationV1alpha1().ValidatingWebhookConfigurations().Delete(webhookConfigName, nil) @@ -291,4 +371,6 @@ func cleanWebhookTest(f *framework.Framework) { _ = client.ExtensionsV1beta1().Deployments(namespaceName).Delete(deploymentName, nil) _ = client.CoreV1().Secrets(namespaceName).Delete(secretName, nil) _ = client.RbacV1beta1().RoleBindings("kube-system").Delete(roleBindingName, nil) + _ = client.CoreV1().ConfigMaps(skippedNamespaceName).Delete(disallowedConfigMapName, nil) + _ = client.CoreV1().Namespaces().Delete(skippedNamespaceName, nil) } diff --git a/test/images/webhook/Makefile b/test/images/webhook/Makefile index 7f706cbaf13..84b76aade12 100644 --- a/test/images/webhook/Makefile +++ b/test/images/webhook/Makefile @@ -14,6 +14,6 @@ build: CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o webhook . - docker build --no-cache -t gcr.io/kubernetes-e2e-test-images/k8s-sample-admission-webhook-amd64:1.8v1 . + docker build --no-cache -t gcr.io/kubernetes-e2e-test-images/k8s-sample-admission-webhook-amd64:1.8v2 . push: - gcloud docker --push gcr.io/kubernetes-e2e-test-images/k8s-sample-admission-webhook-amd64:1.8v1 . + gcloud docker -- push gcr.io/kubernetes-e2e-test-images/k8s-sample-admission-webhook-amd64:1.8v2 diff --git a/test/images/webhook/main.go b/test/images/webhook/main.go index f6e6500e5f3..bb4065196bd 100644 --- a/test/images/webhook/main.go +++ b/test/images/webhook/main.go @@ -44,7 +44,8 @@ func (c *Config) addFlags() { } // only allow pods to pull images from specific registry. -func admit(data []byte) *v1alpha1.AdmissionReviewStatus { +func admitPods(data []byte) *v1alpha1.AdmissionReviewStatus { + glog.V(2).Info("admitting pods") ar := v1alpha1.AdmissionReview{} if err := json.Unmarshal(data, &ar); err != nil { glog.Error(err) @@ -86,7 +87,42 @@ func admit(data []byte) *v1alpha1.AdmissionReviewStatus { return &reviewStatus } -func serve(w http.ResponseWriter, r *http.Request) { +// deny configmaps with specific key-value pair. +func admitConfigMaps(data []byte) *v1alpha1.AdmissionReviewStatus { + glog.V(2).Info("admitting configmaps") + ar := v1alpha1.AdmissionReview{} + if err := json.Unmarshal(data, &ar); err != nil { + glog.Error(err) + return nil + } + configMapResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"} + if ar.Spec.Resource != configMapResource { + glog.Errorf("expect resource to be %s", configMapResource) + return nil + } + + raw := ar.Spec.Object.Raw + configmap := v1.ConfigMap{} + if err := json.Unmarshal(raw, &configmap); err != nil { + glog.Error(err) + return nil + } + reviewStatus := v1alpha1.AdmissionReviewStatus{} + reviewStatus.Allowed = true + for k, v := range configmap.Data { + if k == "webhook-e2e-test" && v == "webhook-disallow" { + reviewStatus.Allowed = false + reviewStatus.Result = &metav1.Status{ + Reason: "the configmap contains unwanted key and value", + } + } + } + return &reviewStatus +} + +type admitFunc func(data []byte) *v1alpha1.AdmissionReviewStatus + +func serve(w http.ResponseWriter, r *http.Request, admit admitFunc) { var body []byte if r.Body != nil { if data, err := ioutil.ReadAll(r.Body); err == nil { @@ -102,8 +138,10 @@ func serve(w http.ResponseWriter, r *http.Request) { } reviewStatus := admit(body) - ar := v1alpha1.AdmissionReview{ - Status: *reviewStatus, + + ar := v1alpha1.AdmissionReview{} + if reviewStatus != nil { + ar.Status = *reviewStatus } resp, err := json.Marshal(ar) @@ -115,12 +153,20 @@ func serve(w http.ResponseWriter, r *http.Request) { } } +func servePods(w http.ResponseWriter, r *http.Request) { + serve(w, r, admitPods) +} +func serveConfigmaps(w http.ResponseWriter, r *http.Request) { + serve(w, r, admitConfigMaps) +} + func main() { var config Config config.addFlags() flag.Parse() - http.HandleFunc("/", serve) + http.HandleFunc("/pods", servePods) + http.HandleFunc("/configmaps", serveConfigmaps) clientset := getClient() server := &http.Server{ Addr: ":443", From 2f83748068e45fd548b1caaac2cc0ca06a769653 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Fri, 10 Nov 2017 11:06:07 -0800 Subject: [PATCH 2/2] generated --- api/openapi-spec/swagger.json | 4 + ...admissionregistration.k8s.io_v1alpha1.json | 46 +++++ .../v1alpha1/definitions.html | 126 +++++++++++++ pkg/apis/admissionregistration/v1alpha1/BUILD | 1 + .../v1alpha1/zz_generated.conversion.go | 3 + .../admissionregistration/validation/BUILD | 1 + .../zz_generated.deepcopy.go | 10 + .../v1alpha1/generated.pb.go | 173 ++++++++++++------ .../v1alpha1/generated.proto | 47 +++++ .../v1alpha1/types_swagger_doc_generated.go | 11 +- .../v1alpha1/zz_generated.deepcopy.go | 10 + .../pkg/admission/plugin/webhook/BUILD | 5 + 12 files changed, 372 insertions(+), 65 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index c9639f65b6b..767f4a32f0c 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -68023,6 +68023,10 @@ "description": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", "type": "string" }, + "namespaceSelector": { + "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is other cluster scoped resource, it is not subjected to the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" + }, "rules": { "description": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule.", "type": "array", diff --git a/api/swagger-spec/admissionregistration.k8s.io_v1alpha1.json b/api/swagger-spec/admissionregistration.k8s.io_v1alpha1.json index 115a23699a5..37fa62ad7be 100644 --- a/api/swagger-spec/admissionregistration.k8s.io_v1alpha1.json +++ b/api/swagger-spec/admissionregistration.k8s.io_v1alpha1.json @@ -2619,6 +2619,10 @@ "failurePolicy": { "$ref": "v1alpha1.FailurePolicyType", "description": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore." + }, + "namespaceSelector": { + "$ref": "v1.LabelSelector", + "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is other cluster scoped resource, it is not subjected to the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." } } }, @@ -2705,6 +2709,48 @@ "id": "v1alpha1.FailurePolicyType", "properties": {} }, + "v1.LabelSelector": { + "id": "v1.LabelSelector", + "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchLabels": { + "type": "object", + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed." + }, + "matchExpressions": { + "type": "array", + "items": { + "$ref": "v1.LabelSelectorRequirement" + }, + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed." + } + } + }, + "v1.LabelSelectorRequirement": { + "id": "v1.LabelSelectorRequirement", + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "required": [ + "key", + "operator" + ], + "properties": { + "key": { + "type": "string", + "description": "key is the label key that the selector applies to." + }, + "operator": { + "type": "string", + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist." + }, + "values": { + "type": "array", + "items": { + "type": "string" + }, + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch." + } + } + }, "v1alpha1.ValidatingWebhookConfigurationList": { "id": "v1alpha1.ValidatingWebhookConfigurationList", "description": "ValidatingWebhookConfigurationList is a list of ValidatingWebhookConfiguration.", diff --git a/docs/api-reference/admissionregistration.k8s.io/v1alpha1/definitions.html b/docs/api-reference/admissionregistration.k8s.io/v1alpha1/definitions.html index 8fb0a427394..537738e8a76 100755 --- a/docs/api-reference/admissionregistration.k8s.io/v1alpha1/definitions.html +++ b/docs/api-reference/admissionregistration.k8s.io/v1alpha1/definitions.html @@ -500,6 +500,43 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

v1alpha1.FailurePolicyType

+ +

namespaceSelector

+

NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is other cluster scoped resource, it is not subjected to the webhook.
+
+For example, to run the webhook on any objects whose namespace is not associated with "runlevel" of "0" or "1"; you will set the selector as follows: "namespaceSelector": {
+ "matchExpressions": [
+ {
+ "key": "runlevel",
+ "operator": "NotIn",
+ "values": [
+ "0",
+ "1"
+ ]
+ }
+ ]
+}
+
+If instead you want to only run the webhook on any objects whose namespace is associated with the "environment" of "prod" or "staging"; you will set the selector as follows: "namespaceSelector": {
+ "matchExpressions": [
+ {
+ "key": "environment",
+ "operator": "In",
+ "values": [
+ "prod",
+ "staging"
+ ]
+ }
+ ]
+}
+
+See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.
+
+Default to the empty LabelSelector, which matches everything.

+

false

+

v1.LabelSelector

+ + @@ -918,6 +955,95 @@ Depending on the enclosing object, subresources might not be allowed. Required.< + +
+

v1.LabelSelector

+
+

A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.

+
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionRequiredSchemaDefault

matchLabels

matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.

false

object

matchExpressions

matchExpressions is a list of label selector requirements. The requirements are ANDed.

false

v1.LabelSelectorRequirement array

+ +
+
+

v1.LabelSelectorRequirement

+
+

A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.

+
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionRequiredSchemaDefault

key

key is the label key that the selector applies to.

true

string

operator

operator represents a key’s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.

true

string

values

values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.

false

string array

+

v1alpha1.ServiceReference

diff --git a/pkg/apis/admissionregistration/v1alpha1/BUILD b/pkg/apis/admissionregistration/v1alpha1/BUILD index 81773c8a9cd..b23347bd226 100644 --- a/pkg/apis/admissionregistration/v1alpha1/BUILD +++ b/pkg/apis/admissionregistration/v1alpha1/BUILD @@ -18,6 +18,7 @@ go_library( deps = [ "//pkg/apis/admissionregistration:go_default_library", "//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", diff --git a/pkg/apis/admissionregistration/v1alpha1/zz_generated.conversion.go b/pkg/apis/admissionregistration/v1alpha1/zz_generated.conversion.go index bf8ad8ce02d..e7ad250ca1e 100644 --- a/pkg/apis/admissionregistration/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/admissionregistration/v1alpha1/zz_generated.conversion.go @@ -22,6 +22,7 @@ package v1alpha1 import ( v1alpha1 "k8s.io/api/admissionregistration/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" admissionregistration "k8s.io/kubernetes/pkg/apis/admissionregistration" @@ -296,6 +297,7 @@ func autoConvert_v1alpha1_Webhook_To_admissionregistration_Webhook(in *v1alpha1. } out.Rules = *(*[]admissionregistration.RuleWithOperations)(unsafe.Pointer(&in.Rules)) out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) + out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) return nil } @@ -311,6 +313,7 @@ func autoConvert_admissionregistration_Webhook_To_v1alpha1_Webhook(in *admission } out.Rules = *(*[]v1alpha1.RuleWithOperations)(unsafe.Pointer(&in.Rules)) out.FailurePolicy = (*v1alpha1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) + out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) return nil } diff --git a/pkg/apis/admissionregistration/validation/BUILD b/pkg/apis/admissionregistration/validation/BUILD index ea318376b12..fa9e0a0fe95 100644 --- a/pkg/apis/admissionregistration/validation/BUILD +++ b/pkg/apis/admissionregistration/validation/BUILD @@ -24,6 +24,7 @@ go_library( deps = [ "//pkg/apis/admissionregistration:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/validation:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", diff --git a/pkg/apis/admissionregistration/zz_generated.deepcopy.go b/pkg/apis/admissionregistration/zz_generated.deepcopy.go index 7ed312b3c33..72a8945f7f8 100644 --- a/pkg/apis/admissionregistration/zz_generated.deepcopy.go +++ b/pkg/apis/admissionregistration/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package admissionregistration import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -340,6 +341,15 @@ func (in *Webhook) DeepCopyInto(out *Webhook) { **out = **in } } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + if *in == nil { + *out = nil + } else { + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + } return } diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go b/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go index 9fa3761f812..95e5979a260 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go +++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go @@ -44,6 +44,8 @@ import proto "github.com/gogo/protobuf/proto" import fmt "fmt" import math "math" +import k8s_io_apimachinery_pkg_apis_meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + import strings "strings" import reflect "reflect" @@ -571,6 +573,16 @@ func (m *Webhook) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintGenerated(dAtA, i, uint64(len(*m.FailurePolicy))) i += copy(dAtA[i:], *m.FailurePolicy) } + if m.NamespaceSelector != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.NamespaceSelector.Size())) + n9, err := m.NamespaceSelector.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n9 + } return i, nil } @@ -592,11 +604,11 @@ func (m *WebhookClientConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.Service.Size())) - n9, err := m.Service.MarshalTo(dAtA[i:]) + n10, err := m.Service.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n9 + i += n10 if m.CABundle != nil { dAtA[i] = 0x12 i++ @@ -800,6 +812,10 @@ func (m *Webhook) Size() (n int) { l = len(*m.FailurePolicy) n += 1 + l + sovGenerated(uint64(l)) } + if m.NamespaceSelector != nil { + l = m.NamespaceSelector.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -950,6 +966,7 @@ func (this *Webhook) String() string { `ClientConfig:` + strings.Replace(strings.Replace(this.ClientConfig.String(), "WebhookClientConfig", "WebhookClientConfig", 1), `&`, ``, 1) + `,`, `Rules:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Rules), "RuleWithOperations", "RuleWithOperations", 1), `&`, ``, 1) + `,`, `FailurePolicy:` + valueToStringGenerated(this.FailurePolicy) + `,`, + `NamespaceSelector:` + strings.Replace(fmt.Sprintf("%v", this.NamespaceSelector), "LabelSelector", "k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector", 1) + `,`, `}`, }, "") return s @@ -2253,6 +2270,39 @@ func (m *Webhook) Unmarshal(dAtA []byte) error { s := FailurePolicyType(dAtA[iNdEx:postIndex]) m.FailurePolicy = &s iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamespaceSelector", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NamespaceSelector == nil { + m.NamespaceSelector = &k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector{} + } + if err := m.NamespaceSelector.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -2524,62 +2574,65 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 912 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x56, 0xbf, 0x6f, 0x23, 0x45, - 0x14, 0xce, 0xc6, 0x8e, 0x62, 0x8f, 0x6d, 0xdd, 0xdd, 0x40, 0x61, 0x45, 0xa7, 0xb5, 0xe5, 0x02, - 0xe5, 0x84, 0xd8, 0x25, 0x01, 0x9d, 0x90, 0x10, 0xa0, 0x6c, 0x24, 0x4e, 0x96, 0x72, 0x77, 0x61, - 0x08, 0x77, 0x12, 0xa2, 0x60, 0xbc, 0x7e, 0x5e, 0x0f, 0xde, 0x5f, 0x9a, 0x99, 0x35, 0x84, 0x8a, - 0x86, 0x1e, 0x89, 0x7f, 0x82, 0x3f, 0x25, 0xe5, 0x89, 0x02, 0x52, 0x59, 0x64, 0x91, 0x28, 0x29, - 0x28, 0xaf, 0x42, 0xb3, 0xbf, 0x7d, 0x89, 0x43, 0x4c, 0x91, 0xe2, 0x3a, 0xcf, 0x7b, 0xf3, 0x7d, - 0xef, 0xfb, 0x66, 0xe6, 0x3d, 0x2f, 0x22, 0xb3, 0x0f, 0x84, 0xc1, 0x02, 0x73, 0x16, 0x8d, 0x80, - 0xfb, 0x20, 0x41, 0x98, 0x73, 0xf0, 0xc7, 0x01, 0x37, 0xb3, 0x04, 0x0d, 0x99, 0x49, 0xc7, 0x1e, - 0x13, 0x82, 0x05, 0x3e, 0x07, 0x87, 0x09, 0xc9, 0xa9, 0x64, 0x81, 0x6f, 0xce, 0xf7, 0xa8, 0x1b, - 0x4e, 0xe9, 0x9e, 0xe9, 0x80, 0x0f, 0x9c, 0x4a, 0x18, 0x1b, 0x21, 0x0f, 0x64, 0x80, 0x1f, 0xa4, - 0x50, 0x83, 0x86, 0xcc, 0xb8, 0x12, 0x6a, 0xe4, 0xd0, 0x9d, 0x77, 0x1c, 0x26, 0xa7, 0xd1, 0xc8, - 0xb0, 0x03, 0xcf, 0x74, 0x02, 0x27, 0x30, 0x13, 0x86, 0x51, 0x34, 0x49, 0x56, 0xc9, 0x22, 0xf9, - 0x95, 0x32, 0xef, 0xbc, 0x5f, 0x8a, 0xf2, 0xa8, 0x3d, 0x65, 0x3e, 0xf0, 0x53, 0x33, 0x9c, 0x39, - 0x2a, 0x20, 0x4c, 0x0f, 0x24, 0x35, 0xe7, 0x97, 0xf4, 0xec, 0x98, 0xab, 0x50, 0x3c, 0xf2, 0x25, - 0xf3, 0xe0, 0x12, 0xe0, 0xe1, 0x7f, 0x01, 0x84, 0x3d, 0x05, 0x8f, 0x5e, 0xc2, 0xbd, 0xb7, 0x0a, - 0x17, 0x49, 0xe6, 0x9a, 0xcc, 0x97, 0x42, 0xf2, 0x57, 0x41, 0x83, 0x1f, 0x35, 0xd4, 0x1a, 0xfa, - 0x4c, 0x32, 0xea, 0xb2, 0xef, 0x81, 0xe3, 0x3e, 0xaa, 0xfb, 0xd4, 0x83, 0xae, 0xd6, 0xd7, 0x76, - 0x9b, 0x56, 0xfb, 0x6c, 0xd1, 0xdb, 0x88, 0x17, 0xbd, 0xfa, 0x13, 0xea, 0x01, 0x49, 0x32, 0xf8, - 0x04, 0x6d, 0xf1, 0xc8, 0x05, 0xd1, 0xdd, 0xec, 0xd7, 0x76, 0x5b, 0xfb, 0xa6, 0x71, 0xe3, 0xf3, - 0x36, 0x48, 0xe4, 0x82, 0xd5, 0xc9, 0x38, 0xb7, 0xd4, 0x4a, 0x90, 0x94, 0x6c, 0xf0, 0xb7, 0x86, - 0xba, 0x15, 0x1d, 0x87, 0x81, 0x3f, 0x61, 0x4e, 0x94, 0x12, 0xe0, 0xaf, 0x51, 0x43, 0x9d, 0xee, - 0x98, 0x4a, 0x9a, 0x08, 0x6b, 0xed, 0xbf, 0x5b, 0xa9, 0x5a, 0x98, 0x35, 0xc2, 0x99, 0xa3, 0x02, - 0xc2, 0x50, 0xbb, 0x8d, 0xf9, 0x9e, 0xf1, 0x74, 0xf4, 0x0d, 0xd8, 0xf2, 0x31, 0x48, 0x6a, 0xe1, - 0xac, 0x2c, 0x2a, 0x63, 0xa4, 0x60, 0xc5, 0x21, 0x6a, 0xb3, 0xb2, 0x7a, 0xee, 0xed, 0xe1, 0x1a, - 0xde, 0x2a, 0xe2, 0xad, 0x37, 0xb3, 0x5a, 0xed, 0x4a, 0x50, 0x90, 0xa5, 0x0a, 0x83, 0xbf, 0x34, - 0x74, 0x7f, 0x95, 0xe1, 0x23, 0x26, 0x24, 0xfe, 0xea, 0x92, 0x69, 0xe3, 0x66, 0xa6, 0x15, 0x3a, - 0xb1, 0x7c, 0x37, 0x93, 0xd1, 0xc8, 0x23, 0x15, 0xc3, 0x53, 0xb4, 0xc5, 0x24, 0x78, 0xb9, 0xd3, - 0xc3, 0xff, 0xe7, 0x74, 0x49, 0x75, 0x79, 0xb3, 0x43, 0xc5, 0x4c, 0xd2, 0x02, 0x83, 0xdf, 0x34, - 0x74, 0xff, 0x71, 0x24, 0xa9, 0x64, 0xbe, 0xf3, 0x1c, 0x46, 0xd3, 0x20, 0x98, 0xdd, 0xf6, 0xed, - 0x9e, 0xa0, 0x46, 0x56, 0x39, 0xf7, 0xbb, 0xbf, 0x86, 0xdf, 0x0c, 0x6a, 0xd5, 0x55, 0x0d, 0xd2, - 0xf8, 0x36, 0x63, 0x52, 0x4f, 0xb6, 0x7f, 0x9d, 0xb1, 0x5b, 0xb8, 0x45, 0x77, 0xf9, 0x16, 0x1f, - 0xad, 0xe1, 0xea, 0x3a, 0xe5, 0x2b, 0x6e, 0xf2, 0x67, 0x0d, 0xd5, 0x55, 0xd3, 0xe2, 0xb7, 0x51, - 0x93, 0x86, 0xec, 0x11, 0x0f, 0xa2, 0x50, 0x74, 0xb5, 0x7e, 0x6d, 0xb7, 0x69, 0x75, 0xe2, 0x45, - 0xaf, 0x79, 0x70, 0x3c, 0x4c, 0x83, 0xa4, 0xcc, 0xe3, 0x3d, 0xd4, 0xa2, 0x21, 0x7b, 0x06, 0x5c, - 0x69, 0x49, 0x95, 0x36, 0xad, 0x3b, 0xf1, 0xa2, 0xd7, 0x3a, 0x38, 0x1e, 0xe6, 0x61, 0x52, 0xdd, - 0xa3, 0xf8, 0x39, 0x88, 0x20, 0xe2, 0x36, 0x88, 0x6e, 0xad, 0xe4, 0x27, 0x79, 0x90, 0x94, 0xf9, - 0xc1, 0x2f, 0x1a, 0xc2, 0x4a, 0xd5, 0x73, 0x26, 0xa7, 0x4f, 0x43, 0x48, 0x1d, 0x08, 0xfc, 0x09, - 0x42, 0x41, 0xb1, 0xca, 0x44, 0xf6, 0x92, 0x17, 0x52, 0x44, 0x5f, 0x2e, 0x7a, 0x9d, 0x62, 0x75, - 0x72, 0x1a, 0x02, 0xa9, 0x40, 0xf0, 0x67, 0xa8, 0xae, 0x46, 0x53, 0x77, 0x33, 0xb9, 0xb5, 0xb5, - 0xc7, 0x5c, 0x31, 0x3a, 0xd5, 0x8a, 0x24, 0x54, 0x03, 0x40, 0x77, 0x3f, 0x07, 0x3e, 0x67, 0x36, - 0x10, 0x98, 0x00, 0x07, 0xdf, 0x06, 0x6c, 0xa2, 0xa6, 0x1a, 0xab, 0x22, 0xa4, 0x76, 0x3e, 0x75, - 0xef, 0x65, 0xd0, 0xe6, 0x93, 0x3c, 0x41, 0xca, 0x3d, 0xc5, 0x84, 0xde, 0x5c, 0x35, 0xa1, 0x07, - 0xe7, 0x1a, 0xd2, 0x9f, 0x51, 0x97, 0x8d, 0x5f, 0xbf, 0x9e, 0xfb, 0x47, 0x43, 0x83, 0xeb, 0xad, - 0xdd, 0x42, 0xd7, 0xf9, 0xcb, 0x5d, 0x37, 0x5c, 0xc3, 0xd7, 0xf5, 0xda, 0x57, 0xf4, 0xdd, 0xef, - 0x9b, 0x68, 0x3b, 0xdb, 0x7e, 0x83, 0xff, 0xe7, 0xef, 0x50, 0xdb, 0x76, 0x19, 0xf8, 0x32, 0xa5, - 0xce, 0xde, 0xef, 0xc7, 0xeb, 0x1f, 0xfe, 0x61, 0x85, 0xa5, 0xfc, 0x4b, 0xab, 0x46, 0xc9, 0x52, - 0x25, 0x3c, 0xca, 0xbf, 0x0c, 0x6a, 0xc9, 0xb9, 0x7c, 0xb4, 0x66, 0xcb, 0x2c, 0x37, 0xf0, 0xd5, - 0xdf, 0x09, 0xf8, 0x08, 0x75, 0x26, 0x94, 0xb9, 0x11, 0x87, 0xe3, 0xc0, 0x65, 0xf6, 0x69, 0xb7, - 0x9e, 0x1c, 0xc4, 0x5b, 0xf1, 0xa2, 0xd7, 0xf9, 0xb4, 0x9a, 0x78, 0xb9, 0xe8, 0xdd, 0x5b, 0x0a, - 0x24, 0x0d, 0xbe, 0x0c, 0x1e, 0xfc, 0xaa, 0xa1, 0x37, 0xae, 0x70, 0x8b, 0x27, 0x68, 0x5b, 0xa4, - 0x8d, 0x9a, 0x3d, 0x9f, 0x0f, 0xd7, 0xf0, 0xf2, 0x6a, 0x8b, 0x5b, 0x77, 0x32, 0x27, 0xdb, 0x79, - 0x26, 0x27, 0xc7, 0xbb, 0xa8, 0x61, 0x53, 0x2b, 0xf2, 0xc7, 0xd9, 0x9c, 0x69, 0x5b, 0x6d, 0xf5, - 0xe6, 0x0e, 0x0f, 0xd2, 0x18, 0x29, 0xb2, 0xf8, 0x01, 0xda, 0x8e, 0xb8, 0x7b, 0x4c, 0xe5, 0xb4, - 0x5b, 0x4b, 0x1c, 0x17, 0xa4, 0x5f, 0x90, 0x23, 0x15, 0x26, 0x79, 0xde, 0x32, 0xce, 0x2e, 0xf4, - 0x8d, 0x17, 0x17, 0xfa, 0xc6, 0xf9, 0x85, 0xbe, 0xf1, 0x43, 0xac, 0x6b, 0x67, 0xb1, 0xae, 0xbd, - 0x88, 0x75, 0xed, 0x3c, 0xd6, 0xb5, 0x3f, 0x62, 0x5d, 0xfb, 0xe9, 0x4f, 0x7d, 0xe3, 0xcb, 0x46, - 0xae, 0xf7, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf2, 0x3a, 0x64, 0xed, 0x85, 0x0b, 0x00, 0x00, + // 960 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x56, 0x4f, 0x6f, 0x23, 0xc5, + 0x13, 0xf5, 0xc4, 0x8e, 0x62, 0xb7, 0x6d, 0xed, 0xa6, 0x7f, 0x3f, 0x24, 0x2b, 0x5a, 0x8d, 0xad, + 0x39, 0xa0, 0xac, 0x10, 0x33, 0x24, 0x8b, 0x56, 0x48, 0x08, 0x50, 0x26, 0x12, 0x2b, 0x4b, 0xd9, + 0x6c, 0xe8, 0x0d, 0xbb, 0x12, 0xe2, 0x40, 0x7b, 0x5c, 0xb6, 0x1b, 0x8f, 0x67, 0x46, 0xdd, 0x3d, + 0x66, 0xc3, 0x89, 0x0b, 0x77, 0x24, 0xbe, 0x04, 0x1f, 0x25, 0xc7, 0x85, 0x03, 0xca, 0xc9, 0x22, + 0x83, 0xc4, 0x91, 0x03, 0xc7, 0x3d, 0xa1, 0x9e, 0x7f, 0x1e, 0xc7, 0x71, 0x88, 0x39, 0xe4, 0xc0, + 0xcd, 0x5d, 0xd5, 0xef, 0x55, 0xbd, 0xaa, 0xae, 0xf2, 0x20, 0x32, 0xfe, 0x40, 0x98, 0xcc, 0xb7, + 0xc6, 0x61, 0x0f, 0xb8, 0x07, 0x12, 0x84, 0x35, 0x05, 0xaf, 0xef, 0x73, 0x2b, 0x75, 0xd0, 0x80, + 0x59, 0xb4, 0x3f, 0x61, 0x42, 0x30, 0xdf, 0xe3, 0x30, 0x64, 0x42, 0x72, 0x2a, 0x99, 0xef, 0x59, + 0xd3, 0x3d, 0xea, 0x06, 0x23, 0xba, 0x67, 0x0d, 0xc1, 0x03, 0x4e, 0x25, 0xf4, 0xcd, 0x80, 0xfb, + 0xd2, 0xc7, 0x0f, 0x13, 0xa8, 0x49, 0x03, 0x66, 0x5e, 0x0b, 0x35, 0x33, 0xe8, 0xce, 0xbb, 0x43, + 0x26, 0x47, 0x61, 0xcf, 0x74, 0xfc, 0x89, 0x35, 0xf4, 0x87, 0xbe, 0x15, 0x33, 0xf4, 0xc2, 0x41, + 0x7c, 0x8a, 0x0f, 0xf1, 0xaf, 0x84, 0x79, 0xc7, 0x28, 0x24, 0xe5, 0xf8, 0x1c, 0xac, 0xe9, 0x52, + 0xf4, 0x9d, 0xf7, 0xe7, 0x77, 0x26, 0xd4, 0x19, 0x31, 0x0f, 0xf8, 0x99, 0x15, 0x8c, 0x87, 0xca, + 0x20, 0xac, 0x09, 0x48, 0x7a, 0x1d, 0xca, 0x5a, 0x85, 0xe2, 0xa1, 0x27, 0xd9, 0x04, 0x96, 0x00, + 0x8f, 0xff, 0x09, 0x20, 0x9c, 0x11, 0x4c, 0xe8, 0x12, 0xee, 0xd1, 0x2a, 0x5c, 0x28, 0x99, 0x6b, + 0x31, 0x4f, 0x0a, 0xc9, 0xaf, 0x82, 0x8c, 0xef, 0x35, 0x54, 0xef, 0x7a, 0x4c, 0x32, 0xea, 0xb2, + 0x6f, 0x81, 0xe3, 0x0e, 0xaa, 0x78, 0x74, 0x02, 0x2d, 0xad, 0xa3, 0xed, 0xd6, 0xec, 0xc6, 0xf9, + 0xac, 0x5d, 0x8a, 0x66, 0xed, 0xca, 0x31, 0x9d, 0x00, 0x89, 0x3d, 0xf8, 0x14, 0x6d, 0xf2, 0xd0, + 0x05, 0xd1, 0xda, 0xe8, 0x94, 0x77, 0xeb, 0xfb, 0x96, 0x79, 0xeb, 0x9e, 0x98, 0x24, 0x74, 0xc1, + 0x6e, 0xa6, 0x9c, 0x9b, 0xea, 0x24, 0x48, 0x42, 0x66, 0xfc, 0xa9, 0xa1, 0x56, 0x21, 0x8f, 0x43, + 0xdf, 0x1b, 0xb0, 0x61, 0x98, 0x10, 0xe0, 0xaf, 0x50, 0x55, 0x55, 0xb7, 0x4f, 0x25, 0x8d, 0x13, + 0xab, 0xef, 0xbf, 0x57, 0x88, 0x9a, 0x8b, 0x35, 0x83, 0xf1, 0x50, 0x19, 0x84, 0xa9, 0x6e, 0x9b, + 0xd3, 0x3d, 0xf3, 0x59, 0xef, 0x6b, 0x70, 0xe4, 0x53, 0x90, 0xd4, 0xc6, 0x69, 0x58, 0x34, 0xb7, + 0x91, 0x9c, 0x15, 0x07, 0xa8, 0xc1, 0xe6, 0xd1, 0x33, 0x6d, 0x8f, 0xd7, 0xd0, 0x56, 0x48, 0xde, + 0xfe, 0x7f, 0x1a, 0xab, 0x51, 0x30, 0x0a, 0xb2, 0x10, 0xc1, 0xf8, 0x43, 0x43, 0x0f, 0x56, 0x09, + 0x3e, 0x62, 0x42, 0xe2, 0x2f, 0x97, 0x44, 0x9b, 0xb7, 0x13, 0xad, 0xd0, 0xb1, 0xe4, 0xfb, 0x69, + 0x1a, 0xd5, 0xcc, 0x52, 0x10, 0x3c, 0x42, 0x9b, 0x4c, 0xc2, 0x24, 0x53, 0x7a, 0xf8, 0xef, 0x94, + 0x2e, 0x64, 0x3d, 0xef, 0x6c, 0x57, 0x31, 0x93, 0x24, 0x80, 0xf1, 0xab, 0x86, 0x1e, 0x3c, 0x0d, + 0x25, 0x95, 0xcc, 0x1b, 0xbe, 0x84, 0xde, 0xc8, 0xf7, 0xc7, 0x77, 0xdd, 0xdd, 0x53, 0x54, 0x4d, + 0x23, 0x67, 0x7a, 0xf7, 0xd7, 0xd0, 0x9b, 0x42, 0xed, 0x8a, 0x8a, 0x41, 0xaa, 0xdf, 0xa4, 0x4c, + 0xea, 0xc9, 0x76, 0x6e, 0x12, 0x76, 0x07, 0x5d, 0x74, 0x17, 0xbb, 0xf8, 0x64, 0x0d, 0x55, 0x37, + 0x65, 0xbe, 0xa2, 0x93, 0x3f, 0x6a, 0xa8, 0xa2, 0x86, 0x16, 0xbf, 0x83, 0x6a, 0x34, 0x60, 0x4f, + 0xb8, 0x1f, 0x06, 0xa2, 0xa5, 0x75, 0xca, 0xbb, 0x35, 0xbb, 0x19, 0xcd, 0xda, 0xb5, 0x83, 0x93, + 0x6e, 0x62, 0x24, 0x73, 0x3f, 0xde, 0x43, 0x75, 0x1a, 0xb0, 0x17, 0xc0, 0x55, 0x2e, 0x49, 0xa6, + 0x35, 0xfb, 0x5e, 0x34, 0x6b, 0xd7, 0x0f, 0x4e, 0xba, 0x99, 0x99, 0x14, 0xef, 0x28, 0x7e, 0x0e, + 0xc2, 0x0f, 0xb9, 0x03, 0xa2, 0x55, 0x9e, 0xf3, 0x93, 0xcc, 0x48, 0xe6, 0x7e, 0xe3, 0x27, 0x0d, + 0x61, 0x95, 0xd5, 0x4b, 0x26, 0x47, 0xcf, 0x02, 0x48, 0x14, 0x08, 0xfc, 0x09, 0x42, 0x7e, 0x7e, + 0x4a, 0x93, 0x6c, 0xc7, 0x2f, 0x24, 0xb7, 0xbe, 0x99, 0xb5, 0x9b, 0xf9, 0xe9, 0xf4, 0x2c, 0x00, + 0x52, 0x80, 0xe0, 0xcf, 0x50, 0x45, 0xad, 0xa6, 0xd6, 0x46, 0xdc, 0xb5, 0xb5, 0xd7, 0x5c, 0xbe, + 0x3a, 0xd5, 0x89, 0xc4, 0x54, 0x06, 0xa0, 0xfb, 0xcf, 0x81, 0x4f, 0x99, 0x03, 0x04, 0x06, 0xc0, + 0xc1, 0x73, 0x00, 0x5b, 0xa8, 0xa6, 0xd6, 0xaa, 0x08, 0xa8, 0x93, 0x6d, 0xdd, 0xed, 0x14, 0x5a, + 0x3b, 0xce, 0x1c, 0x64, 0x7e, 0x27, 0xdf, 0xd0, 0x1b, 0xab, 0x36, 0xb4, 0x71, 0xa1, 0x21, 0xfd, + 0x05, 0x75, 0x59, 0xff, 0xbf, 0x37, 0x73, 0x7f, 0x69, 0xc8, 0xb8, 0x59, 0xda, 0x1d, 0x4c, 0x9d, + 0xb7, 0x38, 0x75, 0xdd, 0x35, 0x74, 0xdd, 0x9c, 0xfb, 0x8a, 0xb9, 0xfb, 0xb9, 0x8c, 0xb6, 0xd2, + 0xeb, 0xb7, 0xf8, 0x7f, 0x7e, 0x85, 0x1a, 0x8e, 0xcb, 0xc0, 0x93, 0x09, 0x75, 0xfa, 0x7e, 0x3f, + 0x5e, 0xbf, 0xf8, 0x87, 0x05, 0x96, 0xf9, 0x5f, 0x5a, 0xd1, 0x4a, 0x16, 0x22, 0xe1, 0x5e, 0xf6, + 0x65, 0x50, 0x8e, 0xeb, 0xf2, 0xd1, 0x9a, 0x23, 0xb3, 0x38, 0xc0, 0xd7, 0x7f, 0x27, 0xe0, 0x23, + 0xd4, 0x1c, 0x50, 0xe6, 0x86, 0x1c, 0x4e, 0x7c, 0x97, 0x39, 0x67, 0xad, 0x4a, 0x5c, 0x88, 0xb7, + 0xa3, 0x59, 0xbb, 0xf9, 0x69, 0xd1, 0xf1, 0x66, 0xd6, 0xde, 0x5e, 0x30, 0xc4, 0x03, 0xbe, 0x08, + 0xc6, 0xaf, 0xd0, 0x76, 0x3e, 0x58, 0xcf, 0xc1, 0x05, 0x47, 0xfa, 0xbc, 0xb5, 0x19, 0x17, 0xec, + 0xd1, 0x2d, 0x1f, 0x0c, 0xed, 0x81, 0x9b, 0x41, 0xed, 0xb7, 0xa2, 0x59, 0x7b, 0xfb, 0xf8, 0x2a, + 0x23, 0x59, 0x0e, 0x62, 0xfc, 0xa2, 0xa1, 0xff, 0x5d, 0x53, 0x67, 0x3c, 0x40, 0x5b, 0x22, 0x59, + 0x11, 0xe9, 0xc3, 0xfd, 0x70, 0x8d, 0x2a, 0x5e, 0x5d, 0x2e, 0xf6, 0xbd, 0xb4, 0x86, 0x5b, 0x99, + 0x27, 0x23, 0xc7, 0xbb, 0xa8, 0xea, 0x50, 0x3b, 0xf4, 0xfa, 0xe9, 0x86, 0x6b, 0xd8, 0x0d, 0xf5, + 0xda, 0x0f, 0x0f, 0x12, 0x1b, 0xc9, 0xbd, 0xf8, 0x21, 0xda, 0x0a, 0xb9, 0x7b, 0x42, 0xe5, 0xa8, + 0x55, 0x8e, 0x6b, 0x9d, 0x93, 0x7e, 0x4e, 0x8e, 0x94, 0x99, 0x64, 0x7e, 0xdb, 0x3c, 0xbf, 0xd4, + 0x4b, 0xaf, 0x2f, 0xf5, 0xd2, 0xc5, 0xa5, 0x5e, 0xfa, 0x2e, 0xd2, 0xb5, 0xf3, 0x48, 0xd7, 0x5e, + 0x47, 0xba, 0x76, 0x11, 0xe9, 0xda, 0x6f, 0x91, 0xae, 0xfd, 0xf0, 0xbb, 0x5e, 0xfa, 0xa2, 0x9a, + 0xe5, 0xfb, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x37, 0xaf, 0xd5, 0xfa, 0x23, 0x0c, 0x00, 0x00, } diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.proto b/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.proto index 8b006b16190..56f503a0369 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.proto +++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.proto @@ -21,6 +21,7 @@ syntax = 'proto2'; package k8s.io.api.admissionregistration.v1alpha1; +import "k8s.io/api/core/v1/generated.proto"; import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto"; @@ -200,6 +201,52 @@ message Webhook { // allowed values are Ignore or Fail. Defaults to Ignore. // +optional optional string failurePolicy = 4; + + // NamespaceSelector decides whether to run the webhook on an object based + // on whether the namespace for that object matches the selector. If the + // object itself is a namespace, the matching is performed on + // object.metadata.labels. If the object is other cluster scoped resource, + // it is not subjected to the webhook. + // + // For example, to run the webhook on any objects whose namespace is not + // associated with "runlevel" of "0" or "1"; you will set the selector as + // follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "runlevel", + // "operator": "NotIn", + // "values": [ + // "0", + // "1" + // ] + // } + // ] + // } + // + // If instead you want to only run the webhook on any objects whose + // namespace is associated with the "environment" of "prod" or "staging"; + // you will set the selector as follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "environment", + // "operator": "In", + // "values": [ + // "prod", + // "staging" + // ] + // } + // ] + // } + // + // See + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // for more examples of label selectors. + // + // Default to the empty LabelSelector, which matches everything. + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector namespaceSelector = 5; } // WebhookClientConfig contains the information to make a TLS diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go index 851c8e55481..bcb9b5a6f98 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go @@ -128,11 +128,12 @@ func (ValidatingWebhookConfigurationList) SwaggerDoc() map[string]string { } var map_Webhook = map[string]string{ - "": "Webhook describes an admission webhook and the resources and operations it applies to.", - "name": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", - "clientConfig": "ClientConfig defines how to communicate with the hook. Required", - "rules": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule.", - "failurePolicy": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", + "": "Webhook describes an admission webhook and the resources and operations it applies to.", + "name": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", + "clientConfig": "ClientConfig defines how to communicate with the hook. Required", + "rules": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule.", + "failurePolicy": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", + "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is other cluster scoped resource, it is not subjected to the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", } func (Webhook) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go index ecbab3ebbcb..c3db08be353 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha1 import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -340,6 +341,15 @@ func (in *Webhook) DeepCopyInto(out *Webhook) { **out = **in } } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + if *in == nil { + *out = nil + } else { + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + } return } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD index 13a80fd3f91..4d2a9ac4086 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD @@ -20,7 +20,9 @@ go_library( "//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library", "//vendor/k8s.io/api/authentication/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", @@ -29,7 +31,9 @@ go_library( "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/configuration:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library", + "//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//vendor/k8s.io/client-go/listers/core/v1:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library", @@ -54,6 +58,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/api/equality: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/labels: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/util/diff:go_default_library",