slowWebhookCleanup() }) <ins> ginkgo.It("patching/updating a validating webhook should work", func() { client := f.ClientSet admissionClient := client.AdmissionregistrationV1() ginkgo.By("Creating a validating webhook configuration") hook, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ Name: f.UniqueName, }, Webhooks: []admissionregistrationv1.ValidatingWebhook{ newDenyConfigMapWebhookFixture(f, context), }, }) framework.ExpectNoError(err, "Creating validating webhook configuration") defer func() { err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(hook.Name, nil) framework.ExpectNoError(err, "Deleting validating webhook configuration") }() ginkgo.By("Creating a configMap that does not comply to the validation webhook rules") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedNonCompliantConfigMap(string(uuid.NewUUID()), f) _, err = client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err == nil { err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") return false, nil } if !strings.Contains(err.Error(), "denied") { return false, err } return true, nil }) ginkgo.By("Updating a validating webhook configuration's rules to not include the create operation") err = retry.RetryOnConflict(retry.DefaultRetry, func() error { h, err := admissionClient.ValidatingWebhookConfigurations().Get(f.UniqueName, metav1.GetOptions{}) framework.ExpectNoError(err, "Getting validating webhook configuration") h.Webhooks[0].Rules[0].Operations = []admissionregistrationv1.OperationType{admissionregistrationv1.Update} _, err = admissionClient.ValidatingWebhookConfigurations().Update(h) return err }) framework.ExpectNoError(err, "Updating validating webhook configuration") ginkgo.By("Creating a configMap that does not comply to the validation webhook rules") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedNonCompliantConfigMap(string(uuid.NewUUID()), f) _, err = client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err != nil { if !strings.Contains(err.Error(), "denied") { return false, err } return false, nil } err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") return true, nil }) framework.ExpectNoError(err, "Waiting for configMap in namespace %s to be allowed creation since webhook was updated to not validate create", f.Namespace.Name) ginkgo.By("Patching a validating webhook configuration's rules to include the create operation") hook, err = admissionClient.ValidatingWebhookConfigurations().Patch( f.UniqueName, types.JSONPatchType, []byte(`[{"op": "replace", "path": "/webhooks/0/rules/0/operations", "value": ["CREATE"]}]`)) framework.ExpectNoError(err, "Patching validating webhook configuration") ginkgo.By("Creating a configMap that does not comply to the validation webhook rules") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedNonCompliantConfigMap(string(uuid.NewUUID()), f) _, err = client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err == nil { err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") return false, nil } if !strings.Contains(err.Error(), "denied") { return false, err } return true, nil }) framework.ExpectNoError(err, "Waiting for configMap in namespace %s to be denied creation by validating webhook", f.Namespace.Name) }) ginkgo.It("patching/updating a mutating webhook should work", func() { client := f.ClientSet admissionClient := client.AdmissionregistrationV1() ginkgo.By("Creating a mutating webhook configuration") hook, err := createMutatingWebhookConfiguration(f, &admissionregistrationv1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ Name: f.UniqueName, }, Webhooks: []admissionregistrationv1.MutatingWebhook{ newMutateConfigMapWebhookFixture(f, context, 1), }, }) framework.ExpectNoError(err, "Creating mutating webhook configuration") defer func() { err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(hook.Name, nil) framework.ExpectNoError(err, "Deleting mutating webhook configuration") }() hook, err = admissionClient.MutatingWebhookConfigurations().Get(f.UniqueName, metav1.GetOptions{}) framework.ExpectNoError(err, "Getting mutating webhook configuration") ginkgo.By("Updating a mutating webhook configuration's rules to not include the create operation") hook.Webhooks[0].Rules[0].Operations = []admissionregistrationv1.OperationType{admissionregistrationv1.Update} hook, err = admissionClient.MutatingWebhookConfigurations().Update(hook) framework.ExpectNoError(err, "Updating mutating webhook configuration") ginkgo.By("Creating a configMap that should not be mutated") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedToBeMutatedConfigMap(string(uuid.NewUUID()), f) created, err := client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err != nil { return false, err } err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") _, ok := created.Data["mutation-stage-1"] return !ok, nil }) framework.ExpectNoError(err, "Waiting for configMap in namespace %s this is not mutated", f.Namespace.Name) ginkgo.By("Patching a mutating webhook configuration's rules to include the create operation") hook, err = admissionClient.MutatingWebhookConfigurations().Patch( f.UniqueName, types.JSONPatchType, []byte(`[{"op": "replace", "path": "/webhooks/0/rules/0/operations", "value": ["CREATE"]}]`)) framework.ExpectNoError(err, "Patching mutating webhook configuration") ginkgo.By("Creating a configMap that should be mutated") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedToBeMutatedConfigMap(string(uuid.NewUUID()), f) created, err := client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err != nil { return false, err } err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") _, ok := created.Data["mutation-stage-1"] return ok, nil }) framework.ExpectNoError(err, "Waiting for configMap in namespace %s to be mutated", f.Namespace.Name) }) ginkgo.It("listing validating webhooks should work", func() { testListSize := 10 testUUID := string(uuid.NewUUID()) for i := 0; i < testListSize; i++ { name := fmt.Sprintf("%s-%d", f.UniqueName, i) _, err := createValidatingWebhookConfiguration(f, &admissionregistrationv1.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ Name:   name, Labels: map[string]string{"e2e-list-test-uuid": testUUID}, }, Webhooks: []admissionregistrationv1.ValidatingWebhook{ newDenyConfigMapWebhookFixture(f, context), }, }) framework.ExpectNoError(err, "Creating validating webhook configuration") } selectorListOpts := metav1.ListOptions{LabelSelector: "e2e-list-test-uuid=" + testUUID} ginkgo.By("Listing all of the created validation webhooks") list, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().List(selectorListOpts) framework.ExpectNoError(err, "Listing validating webhook configurations") framework.ExpectEqual(len(list.Items), testListSize) ginkgo.By("Creating a configMap that does not comply to the validation webhook rules") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedNonCompliantConfigMap(string(uuid.NewUUID()), f) _, err = client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err == nil { err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") return false, nil } if !strings.Contains(err.Error(), "denied") { return false, err } return true, nil }) framework.ExpectNoError(err, "Waiting for configMap in namespace %s to be denied creation by validating webhook", f.Namespace.Name) ginkgo.By("Deleting the collection of validation webhooks") err = client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().DeleteCollection(nil, selectorListOpts) framework.ExpectNoError(err, "Deleting collection of validating webhook configurations") ginkgo.By("Creating a configMap that does not comply to the validation webhook rules") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedNonCompliantConfigMap(string(uuid.NewUUID()), f) _, err = client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err != nil { if !strings.Contains(err.Error(), "denied") { return false, err } return false, nil } err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") return true, nil }) framework.ExpectNoError(err, "Waiting for configMap in namespace %s to be allowed creation since there are no webhooks", f.Namespace.Name) }) ginkgo.It("listing mutating webhooks should work", func() { testListSize := 10 testUUID := string(uuid.NewUUID()) for i := 0; i < testListSize; i++ { name := fmt.Sprintf("%s-%d", f.UniqueName, i) _, err := createMutatingWebhookConfiguration(f, &admissionregistrationv1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ Name:   name, Labels: map[string]string{"e2e-list-test-uuid": testUUID}, }, Webhooks: []admissionregistrationv1.MutatingWebhook{ newMutateConfigMapWebhookFixture(f, context, 1), }, }) framework.ExpectNoError(err, "Creating mutating webhook configuration") } selectorListOpts := metav1.ListOptions{LabelSelector: "e2e-list-test-uuid=" + testUUID} ginkgo.By("Listing all of the created validation webhooks") list, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().List(selectorListOpts) framework.ExpectNoError(err, "Listing mutating webhook configurations") framework.ExpectEqual(len(list.Items), testListSize) ginkgo.By("Creating a configMap that should be mutated") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedToBeMutatedConfigMap(string(uuid.NewUUID()), f) created, err := client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err != nil { return false, err } err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") _, ok := created.Data["mutation-stage-1"] return ok, nil }) framework.ExpectNoError(err, "Waiting for configMap in namespace %s to be mutated", f.Namespace.Name) ginkgo.By("Deleting the collection of validation webhooks") err = client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().DeleteCollection(nil, selectorListOpts) framework.ExpectNoError(err, "Deleting collection of mutating webhook configurations") ginkgo.By("Creating a configMap that should not be mutated") err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { cm := namedToBeMutatedConfigMap(string(uuid.NewUUID()), f) created, err := client.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm) if err != nil { return false, err } err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(cm.Name, nil) framework.ExpectNoError(err, "Deleting successfully created configMap") _, ok := created.Data["mutation-stage-1"] return !ok, nil }) framework.ExpectNoError(err, "Waiting for configMap in namespace %s this is not mutated", f.Namespace.Name) }) </ins> // TODO: add more e2e tests for mutating webhooks // 1. mutating webhook that mutates pod // 2. mutating webhook that sends empty patch