Code change:
[x] write e2e tests for all admission operations [x] (for resource APIs) [x] (for discovery docs) [x] switch to v1 admission APIs in e2e - [x] remove skips/time.Sleeps - and promote to conformance tests - AdmissionWebhook should have tests for all stable operations. Those tests should go through the conformance process before being released. This ticket is to help track what operations are yet to be covered by conformance tests. These stable endpoints should be hit by e2e tests tagged with [conformance]. There are tests currently hitting 6 of the corresponding beta endpoints that could be updated to use stable. Tests should be written to exercise the remaining 9 untested stable endpoints as well. These twelve tests could be updated to use the stable API, and subsequently promoted to conformance. / ! ! These will need tests written for them.  untested beta/admissionregistration !
/sig api-machinery
/assign
/area conformance
/assign ^ This is a requirement for graduation to GA.
can we get another apisnoop run? We've coverage for admissionregistration and apiextensions and I'd like to get confirmation from apisnoop that we've got full coverage now.
Keeping this open until tests are actually promoted to conformance
is ready for review. It adds our admission e2e tests to conformance as part of graduation to GA.
is updated currently not hit by any tests, one will need to be updated or written to cover it. Might want to ensure you've promoted one test that hit's each endpoint. !
The PR to do that is at

Related discussion:
text: 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