From 494629ef58c6d01607d610d3e757666356c0b18d Mon Sep 17 00:00:00 2001 From: Michael Gugino Date: Thu, 21 Nov 2019 17:38:01 -0500 Subject: [PATCH] Fix resource version precondition on pod delete Attempting to add ResourceVersion precondition to eviction requests results in a conflict failure. This is due to the fact that we apply a deletion timestamp which mutates the underlying resource. The resource version is then checked again later in the code. This commit removes the ResourceVersion precondition after the object has a deletion timestamp applied. Related-Bug: https://github.com/kubernetes/kubernetes/issues/85485 --- .../pkg/registry/generic/registry/store.go | 9 +++ .../registry/generic/registry/store_test.go | 57 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go index 95d0ad3f484..07ef46cbd35 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go @@ -930,6 +930,15 @@ func (e *Store) Delete(ctx context.Context, name string, deleteValidation rest.V // TODO: remove the check, because we support no-op updates now. if graceful || pendingFinalizers || shouldUpdateFinalizers { err, ignoreNotFound, deleteImmediately, out, lastExisting = e.updateForGracefulDeletionAndFinalizers(ctx, name, key, options, preconditions, deleteValidation, obj) + // Update the preconditions.ResourceVersion if set since we updated the object. + if err == nil && deleteImmediately && preconditions.ResourceVersion != nil { + accessor, err = meta.Accessor(out) + if err != nil { + return out, false, kubeerr.NewInternalError(err) + } + resourceVersion := accessor.GetResourceVersion() + preconditions.ResourceVersion = &resourceVersion + } } // !deleteImmediately covers all cases where err != nil. We keep both to be future-proof. diff --git a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go index 23a31438ff6..ed0cae269bc 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go @@ -687,6 +687,63 @@ func TestStoreDelete(t *testing.T) { } } +func TestStoreGracefulDeleteWithResourceVersion(t *testing.T) { + podA := &example.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "foo"}, + Spec: example.PodSpec{NodeName: "machine"}, + } + + testContext := genericapirequest.WithNamespace(genericapirequest.NewContext(), "test") + destroyFunc, registry := NewTestGenericStoreRegistry(t) + defer destroyFunc() + + defaultDeleteStrategy := testRESTStrategy{scheme, names.SimpleNameGenerator, true, false, true} + registry.DeleteStrategy = testGracefulStrategy{defaultDeleteStrategy} + + // test failure condition + _, _, err := registry.Delete(testContext, podA.Name, rest.ValidateAllObjectFunc, nil) + if !errors.IsNotFound(err) { + t.Errorf("Unexpected error: %v", err) + } + + // create pod + _, err = registry.Create(testContext, podA, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + // try to get a item which should be deleted + obj, err := registry.Get(testContext, podA.Name, &metav1.GetOptions{}) + if errors.IsNotFound(err) { + t.Errorf("Unexpected error: %v", err) + } + + accessor, err := meta.Accessor(obj) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + resourceVersion := accessor.GetResourceVersion() + + options := metav1.NewDeleteOptions(0) + options.Preconditions = &metav1.Preconditions{ResourceVersion: &resourceVersion} + + // delete object + _, wasDeleted, err := registry.Delete(testContext, podA.Name, rest.ValidateAllObjectFunc, options) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if !wasDeleted { + t.Errorf("unexpected, pod %s should have been deleted immediately", podA.Name) + } + + // try to get a item which should be deleted + _, err = registry.Get(testContext, podA.Name, &metav1.GetOptions{}) + if !errors.IsNotFound(err) { + t.Errorf("Unexpected error: %v", err) + } +} + // TestGracefulStoreCanDeleteIfExistingGracePeriodZero tests recovery from // race condition where the graceful delete is unable to complete // in prior operation, but the pod remains with deletion timestamp