diff --git a/plugin/pkg/admission/initialization/initialization.go b/plugin/pkg/admission/initialization/initialization.go index 447753c47b3..b60855c242c 100644 --- a/plugin/pkg/admission/initialization/initialization.go +++ b/plugin/pkg/admission/initialization/initialization.go @@ -208,6 +208,9 @@ func (i *initializer) Admit(a admission.Attributes) (err error) { glog.V(5).Infof("Modifying uninitialized resource %s", a.GetResource()) + if updated != nil && len(updated.Pending) == 0 && updated.Result == nil { + accessor.SetInitializers(nil) + } // because we are called before validation, we need to ensure the update transition is valid. if errs := validation.ValidateInitializersUpdate(updated, existing, initializerFieldPath); len(errs) > 0 { return errors.NewInvalid(a.GetKind().GroupKind(), a.GetName(), errs) diff --git a/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go b/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go index 780c31c476c..5bef9495c60 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go @@ -200,9 +200,6 @@ func ValidateInitializers(initializers *metav1.Initializers, fldPath *field.Path } } allErrs = append(allErrs, validateInitializersResult(initializers.Result, fldPath.Child("result"))...) - if len(initializers.Pending) == 0 && initializers.Result == nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("pending"), nil, "must be non-empty when result is not set")) - } return allErrs } diff --git a/test/e2e/apimachinery/initializers.go b/test/e2e/apimachinery/initializers.go index 6e6ca4806c3..39f02528562 100644 --- a/test/e2e/apimachinery/initializers.go +++ b/test/e2e/apimachinery/initializers.go @@ -262,6 +262,29 @@ var _ = SIGDescribe("Initializers", func() { Expect(err).NotTo(HaveOccurred()) Expect(len(pods.Items)).Should(Equal(1)) }) + + It("will be set to nil if a patch removes the last pending initializer", func() { + ns := f.Namespace.Name + c := f.ClientSet + + podName := "to-be-patch-initialized-pod" + framework.Logf("Creating pod %s", podName) + + // TODO: lower the timeout so that the server responds faster. + _, err := c.CoreV1().Pods(ns).Create(newUninitializedPod(podName)) + if err != nil && !errors.IsTimeout(err) { + framework.Failf("expect err to be timeout error, got %v", err) + } + uninitializedPod, err := c.CoreV1().Pods(ns).Get(podName, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + Expect(uninitializedPod.Initializers).NotTo(BeNil()) + Expect(len(uninitializedPod.Initializers.Pending)).Should(Equal(1)) + + patch := fmt.Sprintf(`{"metadata":{"initializers":{"pending":[{"$patch":"delete","name":"%s"}]}}}`, uninitializedPod.Initializers.Pending[0].Name) + patchedPod, err := c.CoreV1().Pods(ns).Patch(uninitializedPod.Name, types.StrategicMergePatchType, []byte(patch)) + Expect(err).NotTo(HaveOccurred()) + Expect(patchedPod.Initializers).To(BeNil()) + }) }) func newUninitializedPod(podName string) *v1.Pod {