| /* |
| Copyright 2018 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package testing |
| |
| import ( |
| "net/url" |
| "sync" |
| |
| registrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" |
| corev1 "k8s.io/api/core/v1" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" |
| "k8s.io/apimachinery/pkg/runtime" |
| "k8s.io/apimachinery/pkg/runtime/schema" |
| "k8s.io/apiserver/pkg/admission" |
| "k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts" |
| "k8s.io/apiserver/pkg/authentication/user" |
| "k8s.io/client-go/informers" |
| "k8s.io/client-go/kubernetes" |
| fakeclientset "k8s.io/client-go/kubernetes/fake" |
| ) |
| |
| var matchEverythingRules = []registrationv1beta1.RuleWithOperations{{ |
| Operations: []registrationv1beta1.OperationType{registrationv1beta1.OperationAll}, |
| Rule: registrationv1beta1.Rule{ |
| APIGroups: []string{"*"}, |
| APIVersions: []string{"*"}, |
| Resources: []string{"*/*"}, |
| }, |
| }} |
| |
| var sideEffectsUnknown = registrationv1beta1.SideEffectClassUnknown |
| var sideEffectsNone = registrationv1beta1.SideEffectClassNone |
| var sideEffectsSome = registrationv1beta1.SideEffectClassSome |
| var sideEffectsNoneOnDryRun = registrationv1beta1.SideEffectClassNoneOnDryRun |
| |
| // NewFakeDataSource returns a mock client and informer returning the given webhooks. |
| func NewFakeDataSource(name string, webhooks []registrationv1beta1.Webhook, mutating bool, stopCh <-chan struct{}) (clientset kubernetes.Interface, factory informers.SharedInformerFactory) { |
| var objs = []runtime.Object{ |
| &corev1.Namespace{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: name, |
| Labels: map[string]string{ |
| "runlevel": "0", |
| }, |
| }, |
| }, |
| } |
| if mutating { |
| objs = append(objs, ®istrationv1beta1.MutatingWebhookConfiguration{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "test-webhooks", |
| }, |
| Webhooks: webhooks, |
| }) |
| } else { |
| objs = append(objs, ®istrationv1beta1.ValidatingWebhookConfiguration{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "test-webhooks", |
| }, |
| Webhooks: webhooks, |
| }) |
| } |
| |
| client := fakeclientset.NewSimpleClientset(objs...) |
| informerFactory := informers.NewSharedInformerFactory(client, 0) |
| |
| return client, informerFactory |
| } |
| |
| func newAttributesRecord(object metav1.Object, oldObject metav1.Object, kind schema.GroupVersionKind, namespace string, name string, resource string, labels map[string]string, dryRun bool) admission.Attributes { |
| object.SetName(name) |
| object.SetNamespace(namespace) |
| objectLabels := map[string]string{resource + ".name": name} |
| for k, v := range labels { |
| objectLabels[k] = v |
| } |
| object.SetLabels(objectLabels) |
| |
| oldObject.SetName(name) |
| oldObject.SetNamespace(namespace) |
| |
| gvr := kind.GroupVersion().WithResource(resource) |
| subResource := "" |
| userInfo := user.DefaultInfo{ |
| Name: "webhook-test", |
| UID: "webhook-test", |
| } |
| |
| return &FakeAttributes{ |
| Attributes: admission.NewAttributesRecord(object.(runtime.Object), oldObject.(runtime.Object), kind, namespace, name, gvr, subResource, admission.Update, dryRun, &userInfo), |
| } |
| } |
| |
| // FakeAttributes decorate admission.Attributes. It's used to trace the added annotations. |
| type FakeAttributes struct { |
| admission.Attributes |
| annotations map[string]string |
| mutex sync.Mutex |
| } |
| |
| // AddAnnotation adds an annotation key value pair to FakeAttributes |
| func (f *FakeAttributes) AddAnnotation(k, v string) error { |
| f.mutex.Lock() |
| defer f.mutex.Unlock() |
| if err := f.Attributes.AddAnnotation(k, v); err != nil { |
| return err |
| } |
| if f.annotations == nil { |
| f.annotations = make(map[string]string) |
| } |
| f.annotations[k] = v |
| return nil |
| } |
| |
| // GetAnnotations reads annotations from FakeAttributes |
| func (f *FakeAttributes) GetAnnotations() map[string]string { |
| f.mutex.Lock() |
| defer f.mutex.Unlock() |
| return f.annotations |
| } |
| |
| // NewAttribute returns static admission Attributes for testing. |
| func NewAttribute(namespace string, labels map[string]string, dryRun bool) admission.Attributes { |
| // Set up a test object for the call |
| object := corev1.Pod{ |
| TypeMeta: metav1.TypeMeta{ |
| APIVersion: "v1", |
| Kind: "Pod", |
| }, |
| } |
| oldObject := corev1.Pod{} |
| kind := corev1.SchemeGroupVersion.WithKind("Pod") |
| name := "my-pod" |
| |
| return newAttributesRecord(&object, &oldObject, kind, namespace, name, "pod", labels, dryRun) |
| } |
| |
| // NewAttributeUnstructured returns static admission Attributes for testing with custom resources. |
| func NewAttributeUnstructured(namespace string, labels map[string]string, dryRun bool) admission.Attributes { |
| // Set up a test object for the call |
| object := unstructured.Unstructured{} |
| object.SetKind("TestCRD") |
| object.SetAPIVersion("custom.resource/v1") |
| oldObject := unstructured.Unstructured{} |
| oldObject.SetKind("TestCRD") |
| oldObject.SetAPIVersion("custom.resource/v1") |
| kind := object.GroupVersionKind() |
| name := "my-test-crd" |
| |
| return newAttributesRecord(&object, &oldObject, kind, namespace, name, "crd", labels, dryRun) |
| } |
| |
| type urlConfigGenerator struct { |
| baseURL *url.URL |
| } |
| |
| func (c urlConfigGenerator) ccfgURL(urlPath string) registrationv1beta1.WebhookClientConfig { |
| u2 := *c.baseURL |
| u2.Path = urlPath |
| urlString := u2.String() |
| return registrationv1beta1.WebhookClientConfig{ |
| URL: &urlString, |
| CABundle: testcerts.CACert, |
| } |
| } |
| |
| // Test is a webhook test case. |
| type Test struct { |
| Name string |
| Webhooks []registrationv1beta1.Webhook |
| Path string |
| IsCRD bool |
| IsDryRun bool |
| AdditionalLabels map[string]string |
| ExpectLabels map[string]string |
| ExpectAllow bool |
| ErrorContains string |
| ExpectAnnotations map[string]string |
| } |
| |
| // NewNonMutatingTestCases returns test cases with a given base url. |
| // All test cases in NewNonMutatingTestCases have no Patch set in |
| // AdmissionResponse. The test cases are used by both MutatingAdmissionWebhook |
| // and ValidatingAdmissionWebhook. |
| func NewNonMutatingTestCases(url *url.URL) []Test { |
| policyFail := registrationv1beta1.Fail |
| policyIgnore := registrationv1beta1.Ignore |
| ccfgURL := urlConfigGenerator{url}.ccfgURL |
| |
| return []Test{ |
| { |
| Name: "no match", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "nomatch", |
| ClientConfig: ccfgSVC("disallow"), |
| Rules: []registrationv1beta1.RuleWithOperations{{ |
| Operations: []registrationv1beta1.OperationType{registrationv1beta1.Create}, |
| }}, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ExpectAllow: true, |
| }, |
| { |
| Name: "match & allow", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "allow.example.com", |
| ClientConfig: ccfgSVC("allow"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ExpectAllow: true, |
| ExpectAnnotations: map[string]string{"allow.example.com/key1": "value1"}, |
| }, |
| { |
| Name: "match & disallow", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "disallow", |
| ClientConfig: ccfgSVC("disallow"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ErrorContains: "without explanation", |
| }, |
| { |
| Name: "match & disallow ii", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "disallowReason", |
| ClientConfig: ccfgSVC("disallowReason"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| |
| ErrorContains: "you shall not pass", |
| }, |
| { |
| Name: "match & disallow & but allowed because namespaceSelector exempt the ns", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "disallow", |
| ClientConfig: ccfgSVC("disallow"), |
| Rules: newMatchEverythingRules(), |
| NamespaceSelector: &metav1.LabelSelector{ |
| MatchExpressions: []metav1.LabelSelectorRequirement{{ |
| Key: "runlevel", |
| Values: []string{"1"}, |
| Operator: metav1.LabelSelectorOpIn, |
| }}, |
| }, |
| }}, |
| |
| ExpectAllow: true, |
| }, |
| { |
| Name: "match & disallow & but allowed because namespaceSelector exempt the ns ii", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "disallow", |
| ClientConfig: ccfgSVC("disallow"), |
| Rules: newMatchEverythingRules(), |
| NamespaceSelector: &metav1.LabelSelector{ |
| MatchExpressions: []metav1.LabelSelectorRequirement{{ |
| Key: "runlevel", |
| Values: []string{"0"}, |
| Operator: metav1.LabelSelectorOpNotIn, |
| }}, |
| }, |
| }}, |
| ExpectAllow: true, |
| }, |
| { |
| Name: "match & fail (but allow because fail open)", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "internalErr A", |
| ClientConfig: ccfgSVC("internalErr"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyIgnore, |
| }, { |
| Name: "internalErr B", |
| ClientConfig: ccfgSVC("internalErr"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyIgnore, |
| }, { |
| Name: "internalErr C", |
| ClientConfig: ccfgSVC("internalErr"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyIgnore, |
| }}, |
| |
| ExpectAllow: true, |
| }, |
| { |
| Name: "match & fail (but disallow because fail close on nil FailurePolicy)", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "internalErr A", |
| ClientConfig: ccfgSVC("internalErr"), |
| NamespaceSelector: &metav1.LabelSelector{}, |
| Rules: matchEverythingRules, |
| }, { |
| Name: "internalErr B", |
| ClientConfig: ccfgSVC("internalErr"), |
| NamespaceSelector: &metav1.LabelSelector{}, |
| Rules: matchEverythingRules, |
| }, { |
| Name: "internalErr C", |
| ClientConfig: ccfgSVC("internalErr"), |
| NamespaceSelector: &metav1.LabelSelector{}, |
| Rules: matchEverythingRules, |
| }}, |
| ExpectAllow: false, |
| }, |
| { |
| Name: "match & fail (but fail because fail closed)", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "internalErr A", |
| ClientConfig: ccfgSVC("internalErr"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyFail, |
| }, { |
| Name: "internalErr B", |
| ClientConfig: ccfgSVC("internalErr"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyFail, |
| }, { |
| Name: "internalErr C", |
| ClientConfig: ccfgSVC("internalErr"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyFail, |
| }}, |
| ExpectAllow: false, |
| }, |
| { |
| Name: "match & allow (url)", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "allow.example.com", |
| ClientConfig: ccfgURL("allow"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ExpectAllow: true, |
| ExpectAnnotations: map[string]string{"allow.example.com/key1": "value1"}, |
| }, |
| { |
| Name: "match & disallow (url)", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "disallow", |
| ClientConfig: ccfgURL("disallow"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ErrorContains: "without explanation", |
| }, { |
| Name: "absent response and fail open", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "nilResponse", |
| ClientConfig: ccfgURL("nilResponse"), |
| FailurePolicy: &policyIgnore, |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ExpectAllow: true, |
| }, |
| { |
| Name: "absent response and fail closed", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "nilResponse", |
| ClientConfig: ccfgURL("nilResponse"), |
| FailurePolicy: &policyFail, |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ErrorContains: "Webhook response was absent", |
| }, |
| { |
| Name: "no match dry run", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "nomatch", |
| ClientConfig: ccfgSVC("allow"), |
| Rules: []registrationv1beta1.RuleWithOperations{{ |
| Operations: []registrationv1beta1.OperationType{registrationv1beta1.Create}, |
| }}, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| SideEffects: &sideEffectsSome, |
| }}, |
| IsDryRun: true, |
| ExpectAllow: true, |
| }, |
| { |
| Name: "match dry run side effects Unknown", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "allow", |
| ClientConfig: ccfgSVC("allow"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| SideEffects: &sideEffectsUnknown, |
| }}, |
| IsDryRun: true, |
| ErrorContains: "does not support dry run", |
| }, |
| { |
| Name: "match dry run side effects None", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "allow", |
| ClientConfig: ccfgSVC("allow"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| SideEffects: &sideEffectsNone, |
| }}, |
| IsDryRun: true, |
| ExpectAllow: true, |
| ExpectAnnotations: map[string]string{"allow/key1": "value1"}, |
| }, |
| { |
| Name: "match dry run side effects Some", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "allow", |
| ClientConfig: ccfgSVC("allow"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| SideEffects: &sideEffectsSome, |
| }}, |
| IsDryRun: true, |
| ErrorContains: "does not support dry run", |
| }, |
| { |
| Name: "match dry run side effects NoneOnDryRun", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "allow", |
| ClientConfig: ccfgSVC("allow"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| SideEffects: &sideEffectsNoneOnDryRun, |
| }}, |
| IsDryRun: true, |
| ExpectAllow: true, |
| ExpectAnnotations: map[string]string{"allow/key1": "value1"}, |
| }, |
| { |
| Name: "illegal annotation format", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "invalidAnnotation", |
| ClientConfig: ccfgURL("invalidAnnotation"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ExpectAllow: true, |
| }, |
| // No need to test everything with the url case, since only the |
| // connection is different. |
| } |
| } |
| |
| // NewMutatingTestCases returns test cases with a given base url. |
| // All test cases in NewMutatingTestCases have Patch set in |
| // AdmissionResponse. The test cases are only used by both MutatingAdmissionWebhook. |
| func NewMutatingTestCases(url *url.URL) []Test { |
| return []Test{ |
| { |
| Name: "match & remove label", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "removelabel.example.com", |
| ClientConfig: ccfgSVC("removeLabel"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ExpectAllow: true, |
| AdditionalLabels: map[string]string{"remove": "me"}, |
| ExpectLabels: map[string]string{"pod.name": "my-pod"}, |
| ExpectAnnotations: map[string]string{"removelabel.example.com/key1": "value1"}, |
| }, |
| { |
| Name: "match & add label", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "addLabel", |
| ClientConfig: ccfgSVC("addLabel"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ExpectAllow: true, |
| ExpectLabels: map[string]string{"pod.name": "my-pod", "added": "test"}, |
| }, |
| { |
| Name: "match CRD & add label", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "addLabel", |
| ClientConfig: ccfgSVC("addLabel"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| IsCRD: true, |
| ExpectAllow: true, |
| ExpectLabels: map[string]string{"crd.name": "my-test-crd", "added": "test"}, |
| }, |
| { |
| Name: "match CRD & remove label", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "removelabel.example.com", |
| ClientConfig: ccfgSVC("removeLabel"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| IsCRD: true, |
| ExpectAllow: true, |
| AdditionalLabels: map[string]string{"remove": "me"}, |
| ExpectLabels: map[string]string{"crd.name": "my-test-crd"}, |
| ExpectAnnotations: map[string]string{"removelabel.example.com/key1": "value1"}, |
| }, |
| { |
| Name: "match & invalid mutation", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "invalidMutation", |
| ClientConfig: ccfgSVC("invalidMutation"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| }}, |
| ErrorContains: "invalid character", |
| }, |
| { |
| Name: "match & remove label dry run unsupported", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "removeLabel", |
| ClientConfig: ccfgSVC("removeLabel"), |
| Rules: matchEverythingRules, |
| NamespaceSelector: &metav1.LabelSelector{}, |
| SideEffects: &sideEffectsUnknown, |
| }}, |
| IsDryRun: true, |
| ErrorContains: "does not support dry run", |
| }, |
| // No need to test everything with the url case, since only the |
| // connection is different. |
| } |
| } |
| |
| // CachedTest is a test case for the client manager. |
| type CachedTest struct { |
| Name string |
| Webhooks []registrationv1beta1.Webhook |
| ExpectAllow bool |
| ExpectCacheMiss bool |
| } |
| |
| // NewCachedClientTestcases returns a set of client manager test cases. |
| func NewCachedClientTestcases(url *url.URL) []CachedTest { |
| policyIgnore := registrationv1beta1.Ignore |
| ccfgURL := urlConfigGenerator{url}.ccfgURL |
| |
| return []CachedTest{ |
| { |
| Name: "uncached: service webhook, path 'allow'", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "cache1", |
| ClientConfig: ccfgSVC("allow"), |
| Rules: newMatchEverythingRules(), |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyIgnore, |
| }}, |
| ExpectAllow: true, |
| ExpectCacheMiss: true, |
| }, |
| { |
| Name: "uncached: service webhook, path 'internalErr'", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "cache2", |
| ClientConfig: ccfgSVC("internalErr"), |
| Rules: newMatchEverythingRules(), |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyIgnore, |
| }}, |
| ExpectAllow: true, |
| ExpectCacheMiss: true, |
| }, |
| { |
| Name: "cached: service webhook, path 'allow'", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "cache3", |
| ClientConfig: ccfgSVC("allow"), |
| Rules: newMatchEverythingRules(), |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyIgnore, |
| }}, |
| ExpectAllow: true, |
| ExpectCacheMiss: false, |
| }, |
| { |
| Name: "uncached: url webhook, path 'allow'", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "cache4", |
| ClientConfig: ccfgURL("allow"), |
| Rules: newMatchEverythingRules(), |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyIgnore, |
| }}, |
| ExpectAllow: true, |
| ExpectCacheMiss: true, |
| }, |
| { |
| Name: "cached: service webhook, path 'allow'", |
| Webhooks: []registrationv1beta1.Webhook{{ |
| Name: "cache5", |
| ClientConfig: ccfgURL("allow"), |
| Rules: newMatchEverythingRules(), |
| NamespaceSelector: &metav1.LabelSelector{}, |
| FailurePolicy: &policyIgnore, |
| }}, |
| ExpectAllow: true, |
| ExpectCacheMiss: false, |
| }, |
| } |
| } |
| |
| // ccfgSVC returns a client config using the service reference mechanism. |
| func ccfgSVC(urlPath string) registrationv1beta1.WebhookClientConfig { |
| return registrationv1beta1.WebhookClientConfig{ |
| Service: ®istrationv1beta1.ServiceReference{ |
| Name: "webhook-test", |
| Namespace: "default", |
| Path: &urlPath, |
| }, |
| CABundle: testcerts.CACert, |
| } |
| } |
| |
| func newMatchEverythingRules() []registrationv1beta1.RuleWithOperations { |
| return []registrationv1beta1.RuleWithOperations{{ |
| Operations: []registrationv1beta1.OperationType{registrationv1beta1.OperationAll}, |
| Rule: registrationv1beta1.Rule{ |
| APIGroups: []string{"*"}, |
| APIVersions: []string{"*"}, |
| Resources: []string{"*/*"}, |
| }, |
| }} |
| } |