From 14730841f3c1cbfe508554a7fd73065fa59392f7 Mon Sep 17 00:00:00 2001 From: Derek Carr Date: Thu, 1 Dec 2016 14:24:50 -0500 Subject: [PATCH] Fix logic in graceful deletion if existing grace period was 0 --- pkg/api/rest/delete.go | 10 +++++- pkg/registry/generic/registry/store_test.go | 37 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/pkg/api/rest/delete.go b/pkg/api/rest/delete.go index d03a86c6b0d..f56fd155db4 100644 --- a/pkg/api/rest/delete.go +++ b/pkg/api/rest/delete.go @@ -81,7 +81,15 @@ func BeforeDelete(strategy RESTDeleteStrategy, ctx api.Context, obj runtime.Obje // if we are already being deleted, we may only shorten the deletion grace period // this means the object was gracefully deleted previously but deletionGracePeriodSeconds was not set, // so we force deletion immediately - if objectMeta.DeletionGracePeriodSeconds == nil { + // IMPORTANT: + // The deletion operation happens in two phases. + // 1. Update to set DeletionGracePeriodSeconds and DeletionTimestamp + // 2. Delete the object from storage. + // If the update succeeds, but the delete fails (network error, internal storage error, etc.), + // a resource was previously left in a state that was non-recoverable. We + // check if the existing stored resource has a grace period as 0 and if so + // attempt to delete immediately in order to recover from this scenario. + if objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds == 0 { return false, false, nil } // only a shorter grace period may be provided by a user diff --git a/pkg/registry/generic/registry/store_test.go b/pkg/registry/generic/registry/store_test.go index 0fd1e77d7a0..62b2affdecd 100644 --- a/pkg/registry/generic/registry/store_test.go +++ b/pkg/registry/generic/registry/store_test.go @@ -616,6 +616,43 @@ func TestStoreDelete(t *testing.T) { } } +// TestGracefulStoreCanDeleteIfExistingGracePeriodZero tests recovery from +// race condition where the graceful delete is unable to complete +// in prior operation, but the pod remains with deletion timestamp +// and grace period set to 0. +func TestGracefulStoreCanDeleteIfExistingGracePeriodZero(t *testing.T) { + deletionTimestamp := unversioned.NewTime(time.Now()) + deletionGracePeriodSeconds := int64(0) + initialGeneration := int64(1) + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Generation: initialGeneration, + DeletionGracePeriodSeconds: &deletionGracePeriodSeconds, + DeletionTimestamp: &deletionTimestamp, + }, + Spec: api.PodSpec{NodeName: "machine"}, + } + + testContext := api.WithNamespace(api.NewContext(), "test") + destroyFunc, registry := NewTestGenericStoreRegistry(t) + registry.EnableGarbageCollection = false + defaultDeleteStrategy := testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true} + registry.DeleteStrategy = testGracefulStrategy{defaultDeleteStrategy} + defer destroyFunc() + + graceful, gracefulPending, err := rest.BeforeDelete(registry.DeleteStrategy, testContext, pod, api.NewDeleteOptions(0)) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if graceful { + t.Fatalf("graceful should be false if object has DeletionTimestamp and DeletionGracePeriodSeconds is 0") + } + if gracefulPending { + t.Fatalf("gracefulPending should be false if object has DeletionTimestamp and DeletionGracePeriodSeconds is 0") + } +} + func TestGracefulStoreHandleFinalizers(t *testing.T) { initialGeneration := int64(1) podWithFinalizer := &api.Pod{