Change:
says "If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.". can you elucidate if I am assuming this does something different than reality
This is garbage collection finalizer field, not the namespace finalizer field that that new field was inspired from On Thursday, September 8, 2016, Tim Hockin wrote:
I don't parse that - is this not a general-purpose "refcount" thing? It seems to work that way, except not as documented
This is a general purpose field and it should work like Tim thinks it should work. can you take a look at this when you don't have anything pressing for 1.4?
The namespace finalizer should switch to using this field if it is currently doing something else.
I'm adding validation rule to prevent adding finalizers if deletionTimestamps is set. I'm still investigating why it can't be removed. This bug is strange.
Actually I guess it's a bug. It uses patch, and patch doesn't work on list of simple type, is a list of string.
Yep, it's a bug. I used to remove one finalizer and it worked as expected. I think this is a known bug of , we don't have a way to specify the mergekey of a list of simple type.
Is there a bug open for this? Frankly, if is going to fail for things like this, we should not be offering it as a feature, or it should use PUT rather than PATCH , Chao Xu wrote:
Created cannot use PUT, because it's doing a three way merge (current state, last configuration, current configuration).
These are intended for automated use, not human use, so kubectl edit not working is just unfortunate, not a blocker. Although it's really unfortunate that PATCH is so broken. , Chao Xu wrote:
Why does need to be 3-way? I don't think that is the semantic a user would expect from an edit operation AT ALL. It certainly surprised ME. WRT automated vs human - if PATCH doesn't work (worse - says it succeeded but didn't) for some class of fields, then PATCH doesn't work, and we should not support it. Full stop. It's going to end up causing someone an outage. These are not necessarily related to this bug, but I'd call them pretty serious flaws. , Daniel Smith wrote:
Discussed in real life. PATCH works, it's just that there are certain operations you cannot express using the strategicmerge patch syntax. I was wrong. is building . So perhaps it uses PATCH over PUT due to the efficiency.
FWIW, I printed out the patch generated by when I removed one finalizer. The patch is empty, i.e., "{}".
, Chao Xu wrote: Well, no, if there are operations that can not be represented, it really DOESN'T work. I still think it should not use PUT, but I bet Brian can prove me wrong.

Discussion:
allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionTimestamp"), newMeta.DeletionTimestamp, "field is immutable; may only be changed via deletion")) } <ins> // 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"))...) } </ins> // 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"))

return allErrs } <ins> 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 } </ins> func validateVolumes(volumes []api.Volume, fldPath *field.Path) (sets.String, field.ErrorList) { allErrs := field.ErrorList{}

} } <ins> 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) } } } </ins> func TestValidateObjectMetaUpdatePreventsDeletionFieldMutation(t *testing.T) { now := unversioned.NewTime(time.Unix(1000, 0).UTC()) later := unversioned.NewTime(time.Unix(2000, 0).UTC())