From c5e1decc1304d9d4507b038284fa1ce9ff9ef729 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Thu, 8 Sep 2016 23:32:32 -0700 Subject: [PATCH] add validation rule to prevent adding finalizers if the object is being deleted --- pkg/api/validation/validation.go | 15 +++++++++++ pkg/api/validation/validation_test.go | 39 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 49d00659b3..e0d777c6b3 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -438,6 +438,11 @@ func ValidateObjectMetaUpdate(newMeta, oldMeta *api.ObjectMeta, fldPath *field.P allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionTimestamp"), newMeta.DeletionTimestamp, "field is immutable; may only be changed via deletion")) } + // Finalizers cannot be added if the object is already being deleted. + if oldMeta.DeletionTimestamp != nil { + allErrs = append(allErrs, ValidateNoNewFinalizers(newMeta.Finalizers, oldMeta.Finalizers, fldPath.Child("finalizers"))...) + } + // Reject updates that don't specify a resource version if len(newMeta.ResourceVersion) == 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceVersion"), newMeta.ResourceVersion, "must be specified for an update")) @@ -461,6 +466,16 @@ func ValidateObjectMetaUpdate(newMeta, oldMeta *api.ObjectMeta, fldPath *field.P return allErrs } +func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fldPath *field.Path) field.ErrorList { + const newFinalizersErrorMsg string = `no new finalizers can be added if the object is being deleted` + allErrs := field.ErrorList{} + extra := sets.NewString(newFinalizers...).Difference(sets.NewString(oldFinalizers...)) + if len(extra) != 0 { + allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("no new finalizers can be added if the object is being deleted, found new finalizers %#v", extra.List()))) + } + return allErrs +} + func validateVolumes(volumes []api.Volume, fldPath *field.Path) (sets.String, field.ErrorList) { allErrs := field.ErrorList{} diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index 1175a960a4..3c7cf3f665 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -248,6 +248,45 @@ func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) { } } +func TestValidateFinalizersUpdate(t *testing.T) { + testcases := map[string]struct { + Old api.ObjectMeta + New api.ObjectMeta + ExpectedErr string + }{ + "invalid adding finalizers": { + Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &unversioned.Time{}, Finalizers: []string{"x/a"}}, + New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &unversioned.Time{}, Finalizers: []string{"x/a", "y/b"}}, + ExpectedErr: "y/b", + }, + "invalid changing finalizers": { + Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &unversioned.Time{}, Finalizers: []string{"x/a"}}, + New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &unversioned.Time{}, Finalizers: []string{"x/b"}}, + ExpectedErr: "x/b", + }, + "valid removing finalizers": { + Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &unversioned.Time{}, Finalizers: []string{"x/a", "y/b"}}, + New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &unversioned.Time{}, Finalizers: []string{"x/a"}}, + ExpectedErr: "", + }, + "valid adding finalizers for objects not being deleted": { + Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a"}}, + New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a", "y/b"}}, + ExpectedErr: "", + }, + } + for name, tc := range testcases { + errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field")) + if len(errs) == 0 { + if len(tc.ExpectedErr) != 0 { + t.Errorf("case: %q, expected error to contain %q", name, tc.ExpectedErr) + } + } else if e, a := tc.ExpectedErr, errs.ToAggregate().Error(); !strings.Contains(a, e) { + t.Errorf("case: %q, expected error to contain %q, got error %q", name, e, a) + } + } +} + func TestValidateObjectMetaUpdatePreventsDeletionFieldMutation(t *testing.T) { now := unversioned.NewTime(time.Unix(1000, 0).UTC()) later := unversioned.NewTime(time.Unix(2000, 0).UTC())