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:
@@ -25,12 +25,11 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/resourcequota"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// rest implements a RESTStorage for resourcequotas against etcd
|
||||
type REST struct {
|
||||
store *etcdgeneric.Etcd
|
||||
*etcdgeneric.Etcd
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against ResourceQuota objects.
|
||||
@@ -63,47 +62,7 @@ func NewREST(h tools.EtcdHelper) (*REST, *StatusREST) {
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = resourcequota.StatusStrategy
|
||||
|
||||
return &REST{store: store}, &StatusREST{store: &statusStore}
|
||||
}
|
||||
|
||||
// New returns a new object
|
||||
func (r *REST) New() runtime.Object {
|
||||
return r.store.NewFunc()
|
||||
}
|
||||
|
||||
// NewList returns a new list object
|
||||
func (r *REST) NewList() runtime.Object {
|
||||
return r.store.NewListFunc()
|
||||
}
|
||||
|
||||
// List obtains a list of resourcequotas with labels that match selector.
|
||||
func (r *REST) List(ctx api.Context, label labels.Selector, field fields.Selector) (runtime.Object, error) {
|
||||
return r.store.List(ctx, label, field)
|
||||
}
|
||||
|
||||
// Watch begins watching for new, changed, or deleted resourcequotas.
|
||||
func (r *REST) Watch(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||
return r.store.Watch(ctx, label, field, resourceVersion)
|
||||
}
|
||||
|
||||
// Get gets a specific resourcequota specified by its ID.
|
||||
func (r *REST) Get(ctx api.Context, name string) (runtime.Object, error) {
|
||||
return r.store.Get(ctx, name)
|
||||
}
|
||||
|
||||
// Create creates a resourcequota based on a specification.
|
||||
func (r *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
|
||||
return r.store.Create(ctx, obj)
|
||||
}
|
||||
|
||||
// Update changes a resourcequota specification.
|
||||
func (r *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
|
||||
return r.store.Update(ctx, obj)
|
||||
}
|
||||
|
||||
// Delete deletes an existing resourcequota specified by its name.
|
||||
func (r *REST) Delete(ctx api.Context, name string) (runtime.Object, error) {
|
||||
return r.store.Delete(ctx, name)
|
||||
return &REST{store}, &StatusREST{store: &statusStore}
|
||||
}
|
||||
|
||||
// StatusREST implements the REST endpoint for changing the status of a resourcequota.
|
||||
|
@@ -340,7 +340,7 @@ func TestDeleteResourceQuota(t *testing.T) {
|
||||
},
|
||||
}
|
||||
storage, _ := NewREST(helper)
|
||||
_, err := storage.Delete(api.NewDefaultContext(), "foo")
|
||||
_, err := storage.Delete(api.NewDefaultContext(), "foo", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -353,8 +353,8 @@ func TestEtcdGetDifferentNamespace(t *testing.T) {
|
||||
ctx1 := api.NewDefaultContext()
|
||||
ctx2 := api.WithNamespace(api.NewContext(), "other")
|
||||
|
||||
key1, _ := registry.store.KeyFunc(ctx1, "foo")
|
||||
key2, _ := registry.store.KeyFunc(ctx2, "foo")
|
||||
key1, _ := registry.KeyFunc(ctx1, "foo")
|
||||
key2, _ := registry.KeyFunc(ctx2, "foo")
|
||||
|
||||
fakeClient.Set(key1, runtime.EncodeOrDie(latest.Codec, &api.ResourceQuota{ObjectMeta: api.ObjectMeta{Namespace: "default", Name: "foo"}}), 0)
|
||||
fakeClient.Set(key2, runtime.EncodeOrDie(latest.Codec, &api.ResourceQuota{ObjectMeta: api.ObjectMeta{Namespace: "other", Name: "foo"}}), 0)
|
||||
@@ -388,7 +388,7 @@ func TestEtcdGetDifferentNamespace(t *testing.T) {
|
||||
func TestEtcdGet(t *testing.T) {
|
||||
registry, _, fakeClient, _ := newStorage(t)
|
||||
ctx := api.NewDefaultContext()
|
||||
key, _ := registry.store.KeyFunc(ctx, "foo")
|
||||
key, _ := registry.KeyFunc(ctx, "foo")
|
||||
fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "foo"}}), 0)
|
||||
obj, err := registry.Get(ctx, "foo")
|
||||
if err != nil {
|
||||
@@ -403,7 +403,7 @@ func TestEtcdGet(t *testing.T) {
|
||||
func TestEtcdGetNotFound(t *testing.T) {
|
||||
registry, _, fakeClient, _ := newStorage(t)
|
||||
ctx := api.NewDefaultContext()
|
||||
key, _ := registry.store.KeyFunc(ctx, "foo")
|
||||
key, _ := registry.KeyFunc(ctx, "foo")
|
||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||
R: &etcd.Response{
|
||||
Node: nil,
|
||||
@@ -431,7 +431,7 @@ func TestEtcdCreateFailsWithoutNamespace(t *testing.T) {
|
||||
func TestEtcdCreateAlreadyExisting(t *testing.T) {
|
||||
registry, _, fakeClient, _ := newStorage(t)
|
||||
ctx := api.NewDefaultContext()
|
||||
key, _ := registry.store.KeyFunc(ctx, "foo")
|
||||
key, _ := registry.KeyFunc(ctx, "foo")
|
||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||
R: &etcd.Response{
|
||||
Node: &etcd.Node{
|
||||
@@ -451,7 +451,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
|
||||
ctx := api.NewDefaultContext()
|
||||
fakeClient.TestIndex = true
|
||||
|
||||
key, _ := registry.store.KeyFunc(ctx, "foo")
|
||||
key, _ := registry.KeyFunc(ctx, "foo")
|
||||
resourcequotaStart := validNewResourceQuota()
|
||||
fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, resourcequotaStart), 1)
|
||||
|
||||
@@ -502,7 +502,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
|
||||
func TestEtcdEmptyList(t *testing.T) {
|
||||
registry, _, fakeClient, _ := newStorage(t)
|
||||
ctx := api.NewDefaultContext()
|
||||
key := registry.store.KeyRootFunc(ctx)
|
||||
key := registry.KeyRootFunc(ctx)
|
||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||
R: &etcd.Response{
|
||||
Node: &etcd.Node{
|
||||
@@ -525,7 +525,7 @@ func TestEtcdEmptyList(t *testing.T) {
|
||||
func TestEtcdListNotFound(t *testing.T) {
|
||||
registry, _, fakeClient, _ := newStorage(t)
|
||||
ctx := api.NewDefaultContext()
|
||||
key := registry.store.KeyRootFunc(ctx)
|
||||
key := registry.KeyRootFunc(ctx)
|
||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||
R: &etcd.Response{},
|
||||
E: tools.EtcdErrorNotFound,
|
||||
@@ -543,7 +543,7 @@ func TestEtcdListNotFound(t *testing.T) {
|
||||
func TestEtcdList(t *testing.T) {
|
||||
registry, _, fakeClient, _ := newStorage(t)
|
||||
ctx := api.NewDefaultContext()
|
||||
key := registry.store.KeyRootFunc(ctx)
|
||||
key := registry.KeyRootFunc(ctx)
|
||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||
R: &etcd.Response{
|
||||
Node: &etcd.Node{
|
||||
|
@@ -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) UpdateResourceQuota(ctx api.Context, pod *api.ResourceQuota) e
|
||||
}
|
||||
|
||||
func (s *storage) DeleteResourceQuota(ctx api.Context, podID string) error {
|
||||
_, err := s.Delete(ctx, podID)
|
||||
_, err := s.Delete(ctx, podID, nil)
|
||||
return err
|
||||
}
|
||||
|
Reference in New Issue
Block a user