Graceful deletion of resources

This commit adds support to core resources to enable deferred deletion
of resources.  Clients may optionally specify a time period after which
resources must be deleted via an object sent with their DELETE. That
object may define an optional grace period in seconds, or allow the
default "preferred" value for a resource to be used. Once the object
is marked as pending deletion, the deletionTimestamp field will be set
and an etcd TTL will be in place.

Clients should assume resources that have deletionTimestamp set will
be deleted at some point in the future.  Other changes will come later
to enable graceful deletion on a per resource basis.
This commit is contained in:
Clayton Coleman
2015-03-04 22:34:31 -05:00
parent 6f6485909e
commit 428d2263e5
39 changed files with 581 additions and 94 deletions

View File

@@ -65,6 +65,7 @@ func NewREST(h tools.EtcdHelper) (*REST, *BindingREST, *StatusREST) {
store.CreateStrategy = pod.Strategy
store.UpdateStrategy = pod.Strategy
store.AfterUpdate = bindings.AfterUpdate
store.DeleteStrategy = pod.Strategy
store.ReturnDeletedObject = true
store.AfterDelete = bindings.AfterDelete

View File

@@ -130,6 +130,34 @@ func TestCreate(t *testing.T) {
)
}
func TestDelete(t *testing.T) {
fakeEtcdClient, helper := newHelper(t)
storage, _, _ := NewREST(helper)
cache := &fakeCache{statusToReturn: &api.PodStatus{}}
storage = storage.WithPodStatus(cache)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
createFn := func() runtime.Object {
pod := validChangedPod()
fakeEtcdClient.Data["/registry/pods/default/foo"] = tools.EtcdResponseWithError{
R: &etcd.Response{
Node: &etcd.Node{
Value: runtime.EncodeOrDie(latest.Codec, pod),
ModifiedIndex: 1,
},
},
}
return pod
}
gracefulSetFn := func() bool {
if fakeEtcdClient.Data["/registry/pods/default/foo"].R.Node == nil {
return false
}
return fakeEtcdClient.Data["/registry/pods/default/foo"].R.Node.TTL == 30
}
test.TestDeleteNoGraceful(createFn, gracefulSetFn)
}
func expectPod(t *testing.T, out runtime.Object) (*api.Pod, bool) {
pod, ok := out.(*api.Pod)
if !ok || pod == nil {
@@ -688,7 +716,7 @@ func TestDeletePod(t *testing.T) {
cache := &fakeCache{statusToReturn: &api.PodStatus{}}
storage = storage.WithPodStatus(cache)
result, err := storage.Delete(api.NewDefaultContext(), "foo")
result, err := storage.Delete(api.NewDefaultContext(), "foo", nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -1265,7 +1293,7 @@ func TestEtcdDeletePod(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "foo"},
Status: api.PodStatus{Host: "machine"},
}), 0)
_, err := registry.Delete(ctx, "foo")
_, err := registry.Delete(ctx, "foo", api.NewDeleteOptions(0))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -1286,7 +1314,7 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) {
ObjectMeta: api.ObjectMeta{Name: "foo"},
Status: api.PodStatus{Host: "machine"},
}), 0)
_, err := registry.Delete(ctx, "foo")
_, err := registry.Delete(ctx, "foo", api.NewDeleteOptions(0))
if err != nil {
t.Errorf("unexpected error: %v", err)
}

View File

@@ -44,7 +44,7 @@ type Registry interface {
// Storage is an interface for a standard REST Storage backend
// TODO: move me somewhere common
type Storage interface {
apiserver.RESTDeleter
apiserver.RESTGracefulDeleter
apiserver.RESTLister
apiserver.RESTGetter
apiserver.ResourceWatcher
@@ -95,6 +95,6 @@ func (s *storage) UpdatePod(ctx api.Context, pod *api.Pod) error {
}
func (s *storage) DeletePod(ctx api.Context, podID string) error {
_, err := s.Delete(ctx, podID)
_, err := s.Delete(ctx, podID, nil)
return err
}

View File

@@ -72,6 +72,11 @@ func (podStrategy) ValidateUpdate(obj, old runtime.Object) errors.ValidationErro
return validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod))
}
// CheckGracefulDelete allows a pod to be gracefully deleted.
func (podStrategy) CheckGracefulDelete(obj runtime.Object, options *api.DeleteOptions) bool {
return false
}
type podStatusStrategy struct {
podStrategy
}