diff --git a/pkg/master/master.go b/pkg/master/master.go index 9a92bf48d82..7a2dd3dc35c 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -366,7 +366,7 @@ func (m *Master) init(c *Config) { resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewStorage(c.EtcdHelper) secretRegistry := secret.NewEtcdRegistry(c.EtcdHelper) persistentVolumeStorage, persistentVolumeStatusStorage := pvetcd.NewStorage(c.EtcdHelper) - persistentVolumeClaimStorage := pvcetcd.NewStorage(c.EtcdHelper) + persistentVolumeClaimStorage, persistentVolumeClaimStatusStorage := pvcetcd.NewStorage(c.EtcdHelper) namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewStorage(c.EtcdHelper) m.namespaceRegistry = namespace.NewRegistry(namespaceStorage) @@ -397,16 +397,17 @@ func (m *Master) init(c *Config) { "nodes": nodeStorage, "events": event.NewStorage(eventRegistry), - "limitRanges": limitrange.NewStorage(limitRangeRegistry), - "resourceQuotas": resourceQuotaStorage, - "resourceQuotas/status": resourceQuotaStatusStorage, - "namespaces": namespaceStorage, - "namespaces/status": namespaceStatusStorage, - "namespaces/finalize": namespaceFinalizeStorage, - "secrets": secret.NewStorage(secretRegistry), - "persistentVolumes": persistentVolumeStorage, - "persistentVolumes/status": persistentVolumeStatusStorage, - "persistentVolumeClaims": persistentVolumeClaimStorage, + "limitRanges": limitrange.NewStorage(limitRangeRegistry), + "resourceQuotas": resourceQuotaStorage, + "resourceQuotas/status": resourceQuotaStatusStorage, + "namespaces": namespaceStorage, + "namespaces/status": namespaceStatusStorage, + "namespaces/finalize": namespaceFinalizeStorage, + "secrets": secret.NewStorage(secretRegistry), + "persistentVolumes": persistentVolumeStorage, + "persistentVolumes/status": persistentVolumeStatusStorage, + "persistentVolumeClaims": persistentVolumeClaimStorage, + "persistentVolumeClaims/status": persistentVolumeClaimStatusStorage, } apiVersions := []string{"v1beta1", "v1beta2"} diff --git a/pkg/registry/persistentvolumeclaim/etcd/etcd.go b/pkg/registry/persistentvolumeclaim/etcd/etcd.go index ada9ce88d89..44184d0a52d 100644 --- a/pkg/registry/persistentvolumeclaim/etcd/etcd.go +++ b/pkg/registry/persistentvolumeclaim/etcd/etcd.go @@ -33,7 +33,7 @@ type REST struct { } // NewREST returns a RESTStorage object that will work against PersistentVolumeClaim objects. -func NewStorage(h tools.EtcdHelper) *REST { +func NewStorage(h tools.EtcdHelper) (*REST, *StatusREST) { prefix := "/registry/persistentvolumeclaims" store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.PersistentVolumeClaim{} }, @@ -59,5 +59,22 @@ func NewStorage(h tools.EtcdHelper) *REST { store.UpdateStrategy = persistentvolumeclaim.Strategy store.ReturnDeletedObject = true - return &REST{store} + statusStore := *store + statusStore.UpdateStrategy = persistentvolumeclaim.StatusStrategy + + return &REST{store}, &StatusREST{store: &statusStore} +} + +// StatusREST implements the REST endpoint for changing the status of a persistentvolumeclaim. +type StatusREST struct { + store *etcdgeneric.Etcd +} + +func (r *StatusREST) New() runtime.Object { + return &api.PersistentVolumeClaim{} +} + +// Update alters the status subset of an object. +func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { + return r.store.Update(ctx, obj) } diff --git a/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go b/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go index d9af475edf1..e3c44ce4ee3 100644 --- a/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go +++ b/pkg/registry/persistentvolumeclaim/etcd/etcd_test.go @@ -37,12 +37,12 @@ type testRegistry struct { *registrytest.GenericRegistry } -func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient, tools.EtcdHelper) { +func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient, tools.EtcdHelper) { fakeEtcdClient := tools.NewFakeEtcdClient(t) fakeEtcdClient.TestIndex = true helper := tools.NewEtcdHelper(fakeEtcdClient, latest.Codec) - storage := NewStorage(helper) - return storage, fakeEtcdClient, helper + storage, statusStorage := NewStorage(helper) + return storage, statusStorage, fakeEtcdClient, helper } func validNewPersistentVolumeClaim(name, ns string) *api.PersistentVolumeClaim { @@ -71,7 +71,7 @@ func validChangedPersistentVolumeClaim() *api.PersistentVolumeClaim { func TestCreate(t *testing.T) { - registry, fakeEtcdClient, _ := newStorage(t) + registry, _, fakeEtcdClient, _ := newStorage(t) test := resttest.New(t, registry, fakeEtcdClient.SetError) pv := validNewPersistentVolumeClaim("foo", api.NamespaceDefault) pv.ObjectMeta = api.ObjectMeta{} @@ -85,9 +85,9 @@ func TestCreate(t *testing.T) { ) } -func TestDelete2(t *testing.T) { +func TestDelete(t *testing.T) { ctx := api.NewDefaultContext() - storage, fakeEtcdClient, _ := newStorage(t) + storage, _, fakeEtcdClient, _ := newStorage(t) test := resttest.New(t, storage, fakeEtcdClient.SetError) pv := validChangedPersistentVolumeClaim() @@ -112,36 +112,9 @@ func TestDelete2(t *testing.T) { test.TestDeleteNoGraceful(createFn, gracefulSetFn) } -func TestDelete(t *testing.T) { - ctx := api.NewDefaultContext() - registry, fakeEtcdClient, _ := newStorage(t) - test := resttest.New(t, registry, fakeEtcdClient.SetError) - - pvc := validChangedPersistentVolumeClaim() - key, _ := registry.KeyFunc(ctx, pvc.Name) - createFn := func() runtime.Object { - fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, pvc), - ModifiedIndex: 1, - }, - }, - } - return pvc - } - gracefulSetFn := func() bool { - if fakeEtcdClient.Data[key].R.Node == nil { - return false - } - return fakeEtcdClient.Data[key].R.Node.TTL == 30 - } - test.TestDeleteNoGraceful(createFn, gracefulSetFn) -} - func TestEtcdListPersistentVolumeClaims(t *testing.T) { ctx := api.NewDefaultContext() - registry, fakeClient, _ := newStorage(t) + registry, _, fakeClient, _ := newStorage(t) key := registry.KeyRootFunc(ctx) fakeClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ @@ -172,7 +145,7 @@ func TestEtcdListPersistentVolumeClaims(t *testing.T) { func TestEtcdGetPersistentVolumeClaims(t *testing.T) { ctx := api.NewDefaultContext() - registry, fakeClient, _ := newStorage(t) + registry, _, fakeClient, _ := newStorage(t) persistentVolume := validNewPersistentVolumeClaim("foo", api.NamespaceDefault) name := persistentVolume.Name key, _ := registry.KeyFunc(ctx, name) @@ -202,7 +175,7 @@ func TestEtcdGetPersistentVolumeClaims(t *testing.T) { func TestListEmptyPersistentVolumeClaimsList(t *testing.T) { ctx := api.NewDefaultContext() - registry, fakeClient, _ := newStorage(t) + registry, _, fakeClient, _ := newStorage(t) fakeClient.ChangeIndex = 1 key := registry.KeyRootFunc(ctx) fakeClient.Data[key] = tools.EtcdResponseWithError{ @@ -225,7 +198,7 @@ func TestListEmptyPersistentVolumeClaimsList(t *testing.T) { func TestListPersistentVolumeClaimsList(t *testing.T) { ctx := api.NewDefaultContext() - registry, fakeClient, _ := newStorage(t) + registry, _, fakeClient, _ := newStorage(t) fakeClient.ChangeIndex = 1 key := registry.KeyRootFunc(ctx) fakeClient.Data[key] = tools.EtcdResponseWithError{ @@ -265,7 +238,7 @@ func TestListPersistentVolumeClaimsList(t *testing.T) { } func TestPersistentVolumeClaimsDecode(t *testing.T) { - registry, _, _ := newStorage(t) + registry, _, _, _ := newStorage(t) expected := validNewPersistentVolumeClaim("foo", api.NamespaceDefault) body, err := latest.Codec.Encode(expected) if err != nil { @@ -284,7 +257,7 @@ func TestPersistentVolumeClaimsDecode(t *testing.T) { func TestEtcdUpdatePersistentVolumeClaims(t *testing.T) { ctx := api.NewDefaultContext() - registry, fakeClient, _ := newStorage(t) + registry, _, fakeClient, _ := newStorage(t) persistentVolume := validChangedPersistentVolumeClaim() key, _ := registry.KeyFunc(ctx, "foo") @@ -313,15 +286,15 @@ func TestEtcdUpdatePersistentVolumeClaims(t *testing.T) { func TestDeletePersistentVolumeClaims(t *testing.T) { ctx := api.NewDefaultContext() - registry, fakeClient, _ := newStorage(t) - persistentVolume := validNewPersistentVolumeClaim("foo", api.NamespaceDefault) - name := persistentVolume.Name + registry, _, fakeClient, _ := newStorage(t) + pvClaim := validNewPersistentVolumeClaim("foo", api.NamespaceDefault) + name := pvClaim.Name key, _ := registry.KeyFunc(ctx, name) fakeClient.ChangeIndex = 1 fakeClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, persistentVolume), + Value: runtime.EncodeOrDie(latest.Codec, pvClaim), ModifiedIndex: 1, CreatedIndex: 1, }, @@ -332,3 +305,41 @@ func TestDeletePersistentVolumeClaims(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } + +func TestEtcdUpdateStatus(t *testing.T) { + storage, statusStorage, fakeClient, helper := newStorage(t) + ctx := api.NewDefaultContext() + fakeClient.TestIndex = true + + key, _ := storage.KeyFunc(ctx, "foo") + pvcStart := validNewPersistentVolumeClaim("foo", api.NamespaceDefault) + fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, pvcStart), 1) + + pvIn := &api.PersistentVolumeClaim{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Namespace: api.NamespaceDefault, + ResourceVersion: "1", + }, + Status: api.PersistentVolumeClaimStatus{ + Phase: api.ClaimBound, + }, + } + + expected := *pvcStart + expected.ResourceVersion = "2" + expected.Labels = pvIn.Labels + expected.Status = pvIn.Status + + _, _, err := statusStorage.Update(ctx, pvIn) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + var pvcOut api.PersistentVolumeClaim + if err := helper.ExtractObj(key, &pvcOut, false); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if !api.Semantic.DeepEqual(expected, pvcOut) { + t.Errorf("unexpected object: %s", util.ObjectDiff(expected, pvcOut)) + } +} diff --git a/pkg/registry/persistentvolumeclaim/rest.go b/pkg/registry/persistentvolumeclaim/rest.go index f7f685e75c0..2d09aa318d3 100644 --- a/pkg/registry/persistentvolumeclaim/rest.go +++ b/pkg/registry/persistentvolumeclaim/rest.go @@ -68,6 +68,23 @@ func (persistentvolumeclaimStrategy) ValidateUpdate(ctx api.Context, obj, old ru return validation.ValidatePersistentVolumeClaimUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim)) } +type persistentvolumeclaimStatusStrategy struct { + persistentvolumeclaimStrategy +} + +var StatusStrategy = persistentvolumeclaimStatusStrategy{Strategy} + +// PrepareForUpdate sets the Spec field which is not allowed to be changed when updating a PV's Status +func (persistentvolumeclaimStatusStrategy) PrepareForUpdate(obj, old runtime.Object) { + newPv := obj.(*api.PersistentVolumeClaim) + oldPv := obj.(*api.PersistentVolumeClaim) + newPv.Spec = oldPv.Spec +} + +func (persistentvolumeclaimStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList { + return validation.ValidatePersistentVolumeClaimStatusUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim)) +} + // MatchPersistentVolumeClaim returns a generic matcher for a given label and field selector. func MatchPersistentVolumeClaim(label labels.Selector, field fields.Selector) generic.Matcher { return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {