Merge pull request #81032 from liggitt/isolate-webhook-e2e

Scope e2e webhooks to avoid cross-test interference
This commit is contained in:
Kubernetes Prow Robot 2019-08-08 06:27:44 -07:00 committed by GitHub
commit 1d83daf8a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -59,20 +59,6 @@ const (
servicePort = 8443 servicePort = 8443
roleBindingName = "webhook-auth-reader" roleBindingName = "webhook-auth-reader"
// The webhook configuration names should not be reused between test instances.
crWebhookConfigName = "e2e-test-webhook-config-cr"
webhookConfigName = "e2e-test-webhook-config"
attachingPodWebhookConfigName = "e2e-test-webhook-config-attaching-pod"
mutatingWebhookConfigName = "e2e-test-mutating-webhook-config"
podMutatingWebhookConfigName = "e2e-test-mutating-webhook-pod"
webhookFailClosedConfigName = "e2e-test-webhook-fail-closed"
validatingWebhookForWebhooksConfigName = "e2e-test-validating-webhook-for-webhooks-config"
mutatingWebhookForWebhooksConfigName = "e2e-test-mutating-webhook-for-webhooks-config"
dummyValidatingWebhookConfigName = "e2e-test-dummy-validating-webhook-config"
dummyMutatingWebhookConfigName = "e2e-test-dummy-mutating-webhook-config"
crdWebhookConfigName = "e2e-test-webhook-config-crd"
slowWebhookConfigName = "e2e-test-webhook-config-slow"
skipNamespaceLabelKey = "skip-webhook-admission" skipNamespaceLabelKey = "skip-webhook-admission"
skipNamespaceLabelValue = "yes" skipNamespaceLabelValue = "yes"
skippedNamespaceName = "exempted-namesapce" skippedNamespaceName = "exempted-namesapce"
@ -111,13 +97,13 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
framework.Skipf("dynamic configuration of webhooks requires the admissionregistration.k8s.io group to be enabled") framework.Skipf("dynamic configuration of webhooks requires the admissionregistration.k8s.io group to be enabled")
} }
// Make sure the namespace created for the test is labeled to be selected by the webhooks
labelNamespace(f, f.Namespace.Name)
ginkgo.By("Setting up server cert") ginkgo.By("Setting up server cert")
context = setupServerCert(namespaceName, serviceName) context = setupServerCert(namespaceName, serviceName)
createAuthReaderRoleBinding(f, namespaceName) createAuthReaderRoleBinding(f, namespaceName)
// 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, imageutils.GetE2EImage(imageutils.Agnhost), context) deployWebhookAndService(f, imageutils.GetE2EImage(imageutils.Agnhost), context)
}) })
@ -126,13 +112,13 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
}) })
ginkgo.It("Should be able to deny pod and configmap creation", func() { ginkgo.It("Should be able to deny pod and configmap creation", func() {
webhookCleanup := registerWebhook(f, context) webhookCleanup := registerWebhook(f, f.UniqueName, context)
defer webhookCleanup() defer webhookCleanup()
testWebhook(f) testWebhook(f)
}) })
ginkgo.It("Should be able to deny attaching pod", func() { ginkgo.It("Should be able to deny attaching pod", func() {
webhookCleanup := registerWebhookForAttachingPod(f, context) webhookCleanup := registerWebhookForAttachingPod(f, f.UniqueName, context)
defer webhookCleanup() defer webhookCleanup()
testAttachingPodWebhook(f) testAttachingPodWebhook(f)
}) })
@ -143,36 +129,36 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
return return
} }
defer testcrd.CleanUp() defer testcrd.CleanUp()
webhookCleanup := registerWebhookForCustomResource(f, context, testcrd) webhookCleanup := registerWebhookForCustomResource(f, f.UniqueName, context, testcrd)
defer webhookCleanup() defer webhookCleanup()
testCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"]) testCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"])
testBlockingCustomResourceDeletion(f, testcrd.Crd, testcrd.DynamicClients["v1"]) testBlockingCustomResourceDeletion(f, testcrd.Crd, testcrd.DynamicClients["v1"])
}) })
ginkgo.It("Should unconditionally reject operations on fail closed webhook", func() { ginkgo.It("Should unconditionally reject operations on fail closed webhook", func() {
webhookCleanup := registerFailClosedWebhook(f, context) webhookCleanup := registerFailClosedWebhook(f, f.UniqueName, context)
defer webhookCleanup() defer webhookCleanup()
testFailClosedWebhook(f) testFailClosedWebhook(f)
}) })
ginkgo.It("Should mutate configmap", func() { ginkgo.It("Should mutate configmap", func() {
webhookCleanup := registerMutatingWebhookForConfigMap(f, context) webhookCleanup := registerMutatingWebhookForConfigMap(f, f.UniqueName, context)
defer webhookCleanup() defer webhookCleanup()
testMutatingConfigMapWebhook(f) testMutatingConfigMapWebhook(f)
}) })
ginkgo.It("Should mutate pod and apply defaults after mutation", func() { ginkgo.It("Should mutate pod and apply defaults after mutation", func() {
webhookCleanup := registerMutatingWebhookForPod(f, context) webhookCleanup := registerMutatingWebhookForPod(f, f.UniqueName, context)
defer webhookCleanup() defer webhookCleanup()
testMutatingPodWebhook(f) testMutatingPodWebhook(f)
}) })
ginkgo.It("Should not be able to mutate or prevent deletion of webhook configuration objects", func() { ginkgo.It("Should not be able to mutate or prevent deletion of webhook configuration objects", func() {
validatingWebhookCleanup := registerValidatingWebhookForWebhookConfigurations(f, context) validatingWebhookCleanup := registerValidatingWebhookForWebhookConfigurations(f, f.UniqueName+"blocking", context)
defer validatingWebhookCleanup() defer validatingWebhookCleanup()
mutatingWebhookCleanup := registerMutatingWebhookForWebhookConfigurations(f, context) mutatingWebhookCleanup := registerMutatingWebhookForWebhookConfigurations(f, f.UniqueName+"blocking", context)
defer mutatingWebhookCleanup() defer mutatingWebhookCleanup()
testWebhooksForWebhookConfigurations(f) testWebhooksForWebhookConfigurations(f, f.UniqueName)
}) })
ginkgo.It("Should mutate custom resource", func() { ginkgo.It("Should mutate custom resource", func() {
@ -181,13 +167,13 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
return return
} }
defer testcrd.CleanUp() defer testcrd.CleanUp()
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd) webhookCleanup := registerMutatingWebhookForCustomResource(f, f.UniqueName, context, testcrd)
defer webhookCleanup() defer webhookCleanup()
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"], false) testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"], false)
}) })
ginkgo.It("Should deny crd creation", func() { ginkgo.It("Should deny crd creation", func() {
crdWebhookCleanup := registerValidatingWebhookForCRD(f, context) crdWebhookCleanup := registerValidatingWebhookForCRD(f, f.UniqueName, context)
defer crdWebhookCleanup() defer crdWebhookCleanup()
testCRDDenyWebhook(f) testCRDDenyWebhook(f)
@ -199,7 +185,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
return return
} }
defer testcrd.CleanUp() defer testcrd.CleanUp()
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd) webhookCleanup := registerMutatingWebhookForCustomResource(f, f.UniqueName, context, testcrd)
defer webhookCleanup() defer webhookCleanup()
testMultiVersionCustomResourceWebhook(f, testcrd) testMultiVersionCustomResourceWebhook(f, testcrd)
}) })
@ -228,7 +214,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
return return
} }
defer testcrd.CleanUp() defer testcrd.CleanUp()
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd) webhookCleanup := registerMutatingWebhookForCustomResource(f, f.UniqueName, context, testcrd)
defer webhookCleanup() defer webhookCleanup()
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"], prune) testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"], prune)
}) })
@ -238,22 +224,22 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
policyIgnore := admissionregistrationv1.Ignore policyIgnore := admissionregistrationv1.Ignore
ginkgo.By("Setting timeout (1s) shorter than webhook latency (5s)") ginkgo.By("Setting timeout (1s) shorter than webhook latency (5s)")
slowWebhookCleanup := registerSlowWebhook(f, context, &policyFail, pointer.Int32Ptr(1)) slowWebhookCleanup := registerSlowWebhook(f, f.UniqueName, context, &policyFail, pointer.Int32Ptr(1))
testSlowWebhookTimeoutFailEarly(f) testSlowWebhookTimeoutFailEarly(f)
slowWebhookCleanup() slowWebhookCleanup()
ginkgo.By("Having no error when timeout is shorter than webhook latency and failure policy is ignore") ginkgo.By("Having no error when timeout is shorter than webhook latency and failure policy is ignore")
slowWebhookCleanup = registerSlowWebhook(f, context, &policyIgnore, pointer.Int32Ptr(1)) slowWebhookCleanup = registerSlowWebhook(f, f.UniqueName, context, &policyIgnore, pointer.Int32Ptr(1))
testSlowWebhookTimeoutNoError(f) testSlowWebhookTimeoutNoError(f)
slowWebhookCleanup() slowWebhookCleanup()
ginkgo.By("Having no error when timeout is longer than webhook latency") ginkgo.By("Having no error when timeout is longer than webhook latency")
slowWebhookCleanup = registerSlowWebhook(f, context, &policyFail, pointer.Int32Ptr(10)) slowWebhookCleanup = registerSlowWebhook(f, f.UniqueName, context, &policyFail, pointer.Int32Ptr(10))
testSlowWebhookTimeoutNoError(f) testSlowWebhookTimeoutNoError(f)
slowWebhookCleanup() slowWebhookCleanup()
ginkgo.By("Having no error when timeout is empty (defaulted to 10s in v1beta1)") ginkgo.By("Having no error when timeout is empty (defaulted to 10s in v1beta1)")
slowWebhookCleanup = registerSlowWebhook(f, context, &policyFail, nil) slowWebhookCleanup = registerSlowWebhook(f, f.UniqueName, context, &policyFail, nil)
testSlowWebhookTimeoutNoError(f) testSlowWebhookTimeoutNoError(f)
slowWebhookCleanup() slowWebhookCleanup()
}) })
@ -414,19 +400,22 @@ func deployWebhookAndService(f *framework.Framework, image string, context *cert
func strPtr(s string) *string { return &s } func strPtr(s string) *string { return &s }
func registerWebhook(f *framework.Framework, context *certContext) func() { func registerWebhook(f *framework.Framework, configName string, context *certContext) func() {
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering the webhook via the AdmissionRegistration API") ginkgo.By("Registering the webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := webhookConfigName
// A webhook that cannot talk to server, with fail-open policy // A webhook that cannot talk to server, with fail-open policy
failOpenHook := failingWebhook(namespace, "fail-open.k8s.io") failOpenHook := failingWebhook(namespace, "fail-open.k8s.io")
policyIgnore := admissionregistrationv1.Ignore policyIgnore := admissionregistrationv1.Ignore
failOpenHook.FailurePolicy = &policyIgnore failOpenHook.FailurePolicy = &policyIgnore
failOpenHook.NamespaceSelector = &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
}
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
_, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1.ValidatingWebhookConfiguration{ _, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -452,6 +441,10 @@ func registerWebhook(f *framework.Framework, context *certContext) func() {
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
{ {
Name: "deny-unwanted-configmap-data.k8s.io", Name: "deny-unwanted-configmap-data.k8s.io",
@ -465,6 +458,7 @@ func registerWebhook(f *framework.Framework, context *certContext) func() {
}}, }},
// The webhook skips the namespace that has label "skip-webhook-admission":"yes" // The webhook skips the namespace that has label "skip-webhook-admission":"yes"
NamespaceSelector: &metav1.LabelSelector{ NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
MatchExpressions: []metav1.LabelSelectorRequirement{ MatchExpressions: []metav1.LabelSelectorRequirement{
{ {
Key: skipNamespaceLabelKey, Key: skipNamespaceLabelKey,
@ -500,15 +494,14 @@ func registerWebhook(f *framework.Framework, context *certContext) func() {
} }
} }
func registerWebhookForAttachingPod(f *framework.Framework, context *certContext) func() { func registerWebhookForAttachingPod(f *framework.Framework, configName string, context *certContext) func() {
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering the webhook via the AdmissionRegistration API") ginkgo.By("Registering the webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := attachingPodWebhookConfigName
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
_, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1.ValidatingWebhookConfiguration{ _, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -534,6 +527,10 @@ func registerWebhookForAttachingPod(f *framework.Framework, context *certContext
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
@ -547,15 +544,14 @@ func registerWebhookForAttachingPod(f *framework.Framework, context *certContext
} }
} }
func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certContext) func() { func registerMutatingWebhookForConfigMap(f *framework.Framework, configName string, context *certContext) func() {
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering the mutating configmap webhook via the AdmissionRegistration API") ginkgo.By("Registering the mutating configmap webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := mutatingWebhookConfigName
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
_, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(&admissionregistrationv1.MutatingWebhookConfiguration{ _, err := createMutatingWebhookConfiguration(f, &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -581,6 +577,10 @@ func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certCo
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
{ {
Name: "adding-configmap-data-stage-2.k8s.io", Name: "adding-configmap-data-stage-2.k8s.io",
@ -603,6 +603,10 @@ func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certCo
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
@ -629,15 +633,14 @@ func testMutatingConfigMapWebhook(f *framework.Framework) {
} }
} }
func registerMutatingWebhookForPod(f *framework.Framework, context *certContext) func() { func registerMutatingWebhookForPod(f *framework.Framework, configName string, context *certContext) func() {
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering the mutating pod webhook via the AdmissionRegistration API") ginkgo.By("Registering the mutating pod webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := podMutatingWebhookConfigName
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
_, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(&admissionregistrationv1.MutatingWebhookConfiguration{ _, err := createMutatingWebhookConfiguration(f, &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -663,6 +666,10 @@ func registerMutatingWebhookForPod(f *framework.Framework, context *certContext)
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
@ -791,6 +798,7 @@ func testWebhook(f *framework.Framework) {
Name: skippedNamespaceName, Name: skippedNamespaceName,
Labels: map[string]string{ Labels: map[string]string{
skipNamespaceLabelKey: skipNamespaceLabelValue, skipNamespaceLabelKey: skipNamespaceLabelValue,
f.UniqueName: "true",
}, },
}}) }})
framework.ExpectNoError(err, "creating namespace %q", skippedNamespaceName) framework.ExpectNoError(err, "creating namespace %q", skippedNamespaceName)
@ -882,17 +890,16 @@ func failingWebhook(namespace, name string) admissionregistrationv1.ValidatingWe
} }
} }
func registerFailClosedWebhook(f *framework.Framework, context *certContext) func() { func registerFailClosedWebhook(f *framework.Framework, configName string, context *certContext) func() {
client := f.ClientSet
ginkgo.By("Registering a webhook that server cannot talk to, with fail closed policy, via the AdmissionRegistration API") ginkgo.By("Registering a webhook that server cannot talk to, with fail closed policy, via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := webhookFailClosedConfigName
// A webhook that cannot talk to server, with fail-closed policy // A webhook that cannot talk to server, with fail-closed policy
policyFail := admissionregistrationv1.Fail policyFail := admissionregistrationv1.Fail
hook := failingWebhook(namespace, "fail-closed.k8s.io") hook := failingWebhook(namespace, "fail-closed.k8s.io")
hook.FailurePolicy = &policyFail hook.FailurePolicy = &policyFail
hook.NamespaceSelector = &metav1.LabelSelector{ hook.NamespaceSelector = &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
MatchExpressions: []metav1.LabelSelectorRequirement{ MatchExpressions: []metav1.LabelSelectorRequirement{
{ {
Key: failNamespaceLabelKey, Key: failNamespaceLabelKey,
@ -902,7 +909,7 @@ func registerFailClosedWebhook(f *framework.Framework, context *certContext) fun
}, },
} }
_, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1.ValidatingWebhookConfiguration{ _, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -928,6 +935,7 @@ func testFailClosedWebhook(f *framework.Framework) {
Name: failNamespaceName, Name: failNamespaceName,
Labels: map[string]string{ Labels: map[string]string{
failNamespaceLabelKey: failNamespaceLabelValue, failNamespaceLabelKey: failNamespaceLabelValue,
f.UniqueName: "true",
}, },
}}) }})
framework.ExpectNoError(err, "creating namespace %q", failNamespaceName) framework.ExpectNoError(err, "creating namespace %q", failNamespaceName)
@ -946,20 +954,19 @@ func testFailClosedWebhook(f *framework.Framework) {
} }
} }
func registerValidatingWebhookForWebhookConfigurations(f *framework.Framework, context *certContext) func() { func registerValidatingWebhookForWebhookConfigurations(f *framework.Framework, configName string, context *certContext) func() {
var err error var err error
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering a validating webhook on ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects, via the AdmissionRegistration API") ginkgo.By("Registering a validating webhook on ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects, via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := validatingWebhookForWebhooksConfigName
failurePolicy := admissionregistrationv1.Fail failurePolicy := admissionregistrationv1.Fail
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
// This webhook denies all requests to Delete validating webhook configuration and // This webhook denies all requests to Delete validating webhook configuration and
// mutating webhook configuration objects. It should never be called, however, because // mutating webhook configuration objects. It should never be called, however, because
// dynamic admission webhooks should not be called on requests involving webhook configuration objects. // dynamic admission webhooks should not be called on requests involving webhook configuration objects.
_, err = client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1.ValidatingWebhookConfiguration{ _, err = createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -989,6 +996,10 @@ func registerValidatingWebhookForWebhookConfigurations(f *framework.Framework, c
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
FailurePolicy: &failurePolicy, FailurePolicy: &failurePolicy,
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
@ -1002,20 +1013,19 @@ func registerValidatingWebhookForWebhookConfigurations(f *framework.Framework, c
} }
} }
func registerMutatingWebhookForWebhookConfigurations(f *framework.Framework, context *certContext) func() { func registerMutatingWebhookForWebhookConfigurations(f *framework.Framework, configName string, context *certContext) func() {
var err error var err error
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering a mutating webhook on ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects, via the AdmissionRegistration API") ginkgo.By("Registering a mutating webhook on ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects, via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := mutatingWebhookForWebhooksConfigName
failurePolicy := admissionregistrationv1.Fail failurePolicy := admissionregistrationv1.Fail
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
// This webhook adds a label to all requests create to validating webhook configuration and // This webhook adds a label to all requests create to validating webhook configuration and
// mutating webhook configuration objects. It should never be called, however, because // mutating webhook configuration objects. It should never be called, however, because
// dynamic admission webhooks should not be called on requests involving webhook configuration objects. // dynamic admission webhooks should not be called on requests involving webhook configuration objects.
_, err = client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(&admissionregistrationv1.MutatingWebhookConfiguration{ _, err = createMutatingWebhookConfiguration(f, &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -1045,6 +1055,10 @@ func registerMutatingWebhookForWebhookConfigurations(f *framework.Framework, con
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
FailurePolicy: &failurePolicy, FailurePolicy: &failurePolicy,
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
@ -1061,7 +1075,7 @@ func registerMutatingWebhookForWebhookConfigurations(f *framework.Framework, con
// This test assumes that the deletion-rejecting webhook defined in // This test assumes that the deletion-rejecting webhook defined in
// registerValidatingWebhookForWebhookConfigurations and the webhook-config-mutating // registerValidatingWebhookForWebhookConfigurations and the webhook-config-mutating
// webhook defined in registerMutatingWebhookForWebhookConfigurations already exist. // webhook defined in registerMutatingWebhookForWebhookConfigurations already exist.
func testWebhooksForWebhookConfigurations(f *framework.Framework) { func testWebhooksForWebhookConfigurations(f *framework.Framework, configName string) {
var err error var err error
client := f.ClientSet client := f.ClientSet
ginkgo.By("Creating a dummy validating-webhook-configuration object") ginkgo.By("Creating a dummy validating-webhook-configuration object")
@ -1070,9 +1084,9 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) {
failurePolicy := admissionregistrationv1.Ignore failurePolicy := admissionregistrationv1.Ignore
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
mutatedValidatingWebhookConfiguration, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1.ValidatingWebhookConfiguration{ mutatedValidatingWebhookConfiguration, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: dummyValidatingWebhookConfigName, Name: configName,
}, },
Webhooks: []admissionregistrationv1.ValidatingWebhook{ Webhooks: []admissionregistrationv1.ValidatingWebhook{
{ {
@ -1102,12 +1116,16 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) {
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
FailurePolicy: &failurePolicy, FailurePolicy: &failurePolicy,
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
framework.ExpectNoError(err, "registering webhook config %s with namespace %s", dummyValidatingWebhookConfigName, namespace) framework.ExpectNoError(err, "registering webhook config %s with namespace %s", configName, namespace)
if mutatedValidatingWebhookConfiguration.ObjectMeta.Labels != nil && mutatedValidatingWebhookConfiguration.ObjectMeta.Labels[addedLabelKey] == addedLabelValue { if mutatedValidatingWebhookConfiguration.ObjectMeta.Labels != nil && mutatedValidatingWebhookConfiguration.ObjectMeta.Labels[addedLabelKey] == addedLabelValue {
e2elog.Failf("expected %s not to be mutated by mutating webhooks but it was", dummyValidatingWebhookConfigName) e2elog.Failf("expected %s not to be mutated by mutating webhooks but it was", configName)
} }
// The webhook configuration is honored in 10s. // The webhook configuration is honored in 10s.
@ -1115,14 +1133,14 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) {
ginkgo.By("Deleting the validating-webhook-configuration, which should be possible to remove") ginkgo.By("Deleting the validating-webhook-configuration, which should be possible to remove")
err = client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(dummyValidatingWebhookConfigName, nil) err = client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(configName, nil)
framework.ExpectNoError(err, "deleting webhook config %s with namespace %s", dummyValidatingWebhookConfigName, namespace) framework.ExpectNoError(err, "deleting webhook config %s with namespace %s", configName, namespace)
ginkgo.By("Creating a dummy mutating-webhook-configuration object") ginkgo.By("Creating a dummy mutating-webhook-configuration object")
mutatedMutatingWebhookConfiguration, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(&admissionregistrationv1.MutatingWebhookConfiguration{ mutatedMutatingWebhookConfiguration, err := createMutatingWebhookConfiguration(f, &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: dummyMutatingWebhookConfigName, Name: configName,
}, },
Webhooks: []admissionregistrationv1.MutatingWebhook{ Webhooks: []admissionregistrationv1.MutatingWebhook{
{ {
@ -1152,12 +1170,16 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) {
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
FailurePolicy: &failurePolicy, FailurePolicy: &failurePolicy,
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
framework.ExpectNoError(err, "registering webhook config %s with namespace %s", dummyMutatingWebhookConfigName, namespace) framework.ExpectNoError(err, "registering webhook config %s with namespace %s", configName, namespace)
if mutatedMutatingWebhookConfiguration.ObjectMeta.Labels != nil && mutatedMutatingWebhookConfiguration.ObjectMeta.Labels[addedLabelKey] == addedLabelValue { if mutatedMutatingWebhookConfiguration.ObjectMeta.Labels != nil && mutatedMutatingWebhookConfiguration.ObjectMeta.Labels[addedLabelKey] == addedLabelValue {
e2elog.Failf("expected %s not to be mutated by mutating webhooks but it was", dummyMutatingWebhookConfigName) e2elog.Failf("expected %s not to be mutated by mutating webhooks but it was", configName)
} }
// The webhook configuration is honored in 10s. // The webhook configuration is honored in 10s.
@ -1165,8 +1187,8 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) {
ginkgo.By("Deleting the mutating-webhook-configuration, which should be possible to remove") ginkgo.By("Deleting the mutating-webhook-configuration, which should be possible to remove")
err = client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(dummyMutatingWebhookConfigName, nil) err = client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(configName, nil)
framework.ExpectNoError(err, "deleting webhook config %s with namespace %s", dummyMutatingWebhookConfigName, namespace) framework.ExpectNoError(err, "deleting webhook config %s with namespace %s", configName, namespace)
} }
func createNamespace(f *framework.Framework, ns *v1.Namespace) error { func createNamespace(f *framework.Framework, ns *v1.Namespace) error {
@ -1324,15 +1346,14 @@ func cleanWebhookTest(client clientset.Interface, namespaceName string) {
_ = client.RbacV1().RoleBindings("kube-system").Delete(roleBindingName, nil) _ = client.RbacV1().RoleBindings("kube-system").Delete(roleBindingName, nil)
} }
func registerWebhookForCustomResource(f *framework.Framework, context *certContext, testcrd *crd.TestCrd) func() { func registerWebhookForCustomResource(f *framework.Framework, configName string, context *certContext, testcrd *crd.TestCrd) func() {
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering the custom resource webhook via the AdmissionRegistration API") ginkgo.By("Registering the custom resource webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := crWebhookConfigName
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
_, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1.ValidatingWebhookConfiguration{ _, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -1358,6 +1379,10 @@ func registerWebhookForCustomResource(f *framework.Framework, context *certConte
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
@ -1370,15 +1395,14 @@ func registerWebhookForCustomResource(f *framework.Framework, context *certConte
} }
} }
func registerMutatingWebhookForCustomResource(f *framework.Framework, context *certContext, testcrd *crd.TestCrd) func() { func registerMutatingWebhookForCustomResource(f *framework.Framework, configName string, context *certContext, testcrd *crd.TestCrd) func() {
client := f.ClientSet client := f.ClientSet
ginkgo.By(fmt.Sprintf("Registering the mutating webhook for custom resource %s via the AdmissionRegistration API", testcrd.Crd.Name)) ginkgo.By(fmt.Sprintf("Registering the mutating webhook for custom resource %s via the AdmissionRegistration API", testcrd.Crd.Name))
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := f.UniqueName
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
_, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(&admissionregistrationv1.MutatingWebhookConfiguration{ _, err := createMutatingWebhookConfiguration(f, &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -1404,6 +1428,10 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, context *c
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
{ {
Name: "mutate-custom-resource-data-stage-2.k8s.io", Name: "mutate-custom-resource-data-stage-2.k8s.io",
@ -1426,6 +1454,10 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, context *c
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
@ -1566,19 +1598,18 @@ func testMultiVersionCustomResourceWebhook(f *framework.Framework, testcrd *crd.
framework.ExpectNoError(err, "failed to patch custom resource %s in namespace: %s", crName, f.Namespace.Name) framework.ExpectNoError(err, "failed to patch custom resource %s in namespace: %s", crName, f.Namespace.Name)
} }
func registerValidatingWebhookForCRD(f *framework.Framework, context *certContext) func() { func registerValidatingWebhookForCRD(f *framework.Framework, configName string, context *certContext) func() {
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering the crd webhook via the AdmissionRegistration API") ginkgo.By("Registering the crd webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name namespace := f.Namespace.Name
configName := crdWebhookConfigName
sideEffectsNone := admissionregistrationv1.SideEffectClassNone sideEffectsNone := admissionregistrationv1.SideEffectClassNone
// This webhook will deny the creation of CustomResourceDefinitions which have the // This webhook will deny the creation of CustomResourceDefinitions which have the
// label "webhook-e2e-test":"webhook-disallow" // label "webhook-e2e-test":"webhook-disallow"
// NOTE: Because tests are run in parallel and in an unpredictable order, it is critical // NOTE: Because tests are run in parallel and in an unpredictable order, it is critical
// that no other test attempts to create CRD with that label. // that no other test attempts to create CRD with that label.
_, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1.ValidatingWebhookConfiguration{ _, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -1604,6 +1635,10 @@ func registerValidatingWebhookForCRD(f *framework.Framework, context *certContex
}, },
SideEffects: &sideEffectsNone, SideEffects: &sideEffectsNone,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
// Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{f.UniqueName: "true"},
},
}, },
}, },
}) })
@ -1669,13 +1704,8 @@ func testCRDDenyWebhook(f *framework.Framework) {
} }
} }
func registerSlowWebhook(f *framework.Framework, context *certContext, policy *admissionregistrationv1.FailurePolicyType, timeout *int32) func() { func labelNamespace(f *framework.Framework, namespace string) {
client := f.ClientSet client := f.ClientSet
ginkgo.By("Registering slow webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name
configName := slowWebhookConfigName
sideEffectsNone := admissionregistrationv1.SideEffectClassNone
// Add a unique label to the namespace // Add a unique label to the namespace
ns, err := client.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{}) ns, err := client.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{})
@ -1683,11 +1713,19 @@ func registerSlowWebhook(f *framework.Framework, context *certContext, policy *a
if ns.Labels == nil { if ns.Labels == nil {
ns.Labels = map[string]string{} ns.Labels = map[string]string{}
} }
ns.Labels[slowWebhookConfigName] = namespace ns.Labels[f.UniqueName] = "true"
_, err = client.CoreV1().Namespaces().Update(ns) _, err = client.CoreV1().Namespaces().Update(ns)
framework.ExpectNoError(err, "error labeling namespace %s", namespace) framework.ExpectNoError(err, "error labeling namespace %s", namespace)
}
_, err = client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1.ValidatingWebhookConfiguration{ func registerSlowWebhook(f *framework.Framework, configName string, context *certContext, policy *admissionregistrationv1.FailurePolicyType, timeout *int32) func() {
client := f.ClientSet
ginkgo.By("Registering slow webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name
sideEffectsNone := admissionregistrationv1.SideEffectClassNone
_, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configName, Name: configName,
}, },
@ -1713,7 +1751,7 @@ func registerSlowWebhook(f *framework.Framework, context *certContext, policy *a
}, },
// Scope the webhook to just this namespace // Scope the webhook to just this namespace
NamespaceSelector: &metav1.LabelSelector{ NamespaceSelector: &metav1.LabelSelector{
MatchLabels: ns.Labels, MatchLabels: map[string]string{f.UniqueName: "true"},
}, },
FailurePolicy: policy, FailurePolicy: policy,
TimeoutSeconds: timeout, TimeoutSeconds: timeout,
@ -1783,3 +1821,33 @@ func servedAPIVersions(crd *apiextensionsv1beta1.CustomResourceDefinition) []str
} }
return ret return ret
} }
// createValidatingWebhookConfiguration ensures the webhook config scopes object or namespace selection
// to avoid interfering with other tests, then creates the config.
func createValidatingWebhookConfiguration(f *framework.Framework, config *admissionregistrationv1.ValidatingWebhookConfiguration) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
for _, webhook := range config.Webhooks {
if webhook.NamespaceSelector != nil && webhook.NamespaceSelector.MatchLabels[f.UniqueName] == "true" {
continue
}
if webhook.ObjectSelector != nil && webhook.ObjectSelector.MatchLabels[f.UniqueName] == "true" {
continue
}
e2elog.Failf(`webhook %s in config %s has no namespace or object selector with %s="true", and can interfere with other tests`, webhook.Name, config.Name, f.UniqueName)
}
return f.ClientSet.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(config)
}
// createMutatingWebhookConfiguration ensures the webhook config scopes object or namespace selection
// to avoid interfering with other tests, then creates the config.
func createMutatingWebhookConfiguration(f *framework.Framework, config *admissionregistrationv1.MutatingWebhookConfiguration) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
for _, webhook := range config.Webhooks {
if webhook.NamespaceSelector != nil && webhook.NamespaceSelector.MatchLabels[f.UniqueName] == "true" {
continue
}
if webhook.ObjectSelector != nil && webhook.ObjectSelector.MatchLabels[f.UniqueName] == "true" {
continue
}
e2elog.Failf(`webhook %s in config %s has no namespace or object selector with %s="true", and can interfere with other tests`, webhook.Name, config.Name, f.UniqueName)
}
return f.ClientSet.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(config)
}