Skip to content

Commit

Permalink
Add volumeattachment status subresource
Browse files Browse the repository at this point in the history
  • Loading branch information
msau42 authored and gnufied committed Nov 14, 2018
1 parent e95b188 commit 498cd61
Show file tree
Hide file tree
Showing 13 changed files with 4,681 additions and 315 deletions.
1,054 changes: 1,046 additions & 8 deletions api/openapi-spec/swagger.json

Large diffs are not rendered by default.

1,030 changes: 1,030 additions & 0 deletions api/swagger-spec/storage.k8s.io_v1.json

Large diffs are not rendered by default.

706 changes: 505 additions & 201 deletions docs/api-reference/storage.k8s.io/v1/definitions.html

Large diffs are not rendered by default.

1,791 changes: 1,777 additions & 14 deletions docs/api-reference/storage.k8s.io/v1/operations.html

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pkg/apis/storage/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ func ValidateVolumeAttachmentUpdate(new, old *storage.VolumeAttachment) field.Er
allErrs := ValidateVolumeAttachment(new)

// Spec is read-only
// If this ever relaxes in the future, make sure to increment the Generation number in PrepareForUpdate
if !apiequality.Semantic.DeepEqual(old.Spec, new.Spec) {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec"), new.Spec, "field is immutable"))
}
Expand Down
23 changes: 13 additions & 10 deletions pkg/registry/storage/rest/storage_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
storage := map[string]rest.Storage{}
// volumeattachments
volumeAttachmentStorage := volumeattachmentstore.NewREST(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage
volumeAttachmentStorage := volumeattachmentstore.NewStorage(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage.VolumeAttachment

return storage
}
Expand All @@ -67,21 +67,24 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag
storage["storageclasses"] = storageClassStorage

// volumeattachments
volumeAttachmentStorage := volumeattachmentstore.NewREST(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage
volumeAttachmentStorage := volumeattachmentstore.NewStorage(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage.VolumeAttachment

return storage
}

func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
storage := map[string]rest.Storage{}
// storageclasses
storageClassStorage := storageclassstore.NewREST(restOptionsGetter)
storage["storageclasses"] = storageClassStorage
volumeAttachmentStorage := volumeattachmentstore.NewStorage(restOptionsGetter)

// volumeattachments
volumeAttachmentStorage := volumeattachmentstore.NewREST(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage
storage := map[string]rest.Storage{
// storageclasses
"storageclasses": storageClassStorage,

// volumeattachments
"volumeattachments": volumeAttachmentStorage.VolumeAttachment,
"volumeattachments/status": volumeAttachmentStorage.Status,
}

return storage
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/registry/storage/volumeattachment/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ go_library(
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/apis/storage/validation:go_default_library",
"//staging/src/k8s.io/api/storage/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
Expand All @@ -24,7 +27,9 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/apis/storage:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
],
)
Expand Down
9 changes: 6 additions & 3 deletions pkg/registry/storage/volumeattachment/storage/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ go_library(
deps = [
"//pkg/apis/storage:go_default_library",
"//pkg/registry/storage/volumeattachment:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)

Expand All @@ -19,17 +21,18 @@ go_test(
srcs = ["storage_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/api/testapi:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//staging/src/k8s.io/api/storage/v1alpha1:go_default_library",
"//staging/src/k8s.io/api/storage/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)
Expand Down
46 changes: 42 additions & 4 deletions pkg/registry/storage/volumeattachment/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,30 @@ limitations under the License.
package storage

import (
"context"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
storageapi "k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/registry/storage/volumeattachment"
)

// REST object that will work against persistent volumes.
// VolumeAttachmentStorage includes storage for VolumeAttachments and all subresources
type VolumeAttachmentStorage struct {
VolumeAttachment *REST
Status *StatusREST
}

// REST object that will work for VolumeAttachments
type REST struct {
*genericregistry.Store
}

// NewREST returns a RESTStorage object that will work against persistent volumes.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
// NewStorage returns a RESTStorage object that will work against VolumeAttachments
func NewStorage(optsGetter generic.RESTOptionsGetter) *VolumeAttachmentStorage {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &storageapi.VolumeAttachment{} },
NewListFunc: func() runtime.Object { return &storageapi.VolumeAttachmentList{} },
Expand All @@ -46,5 +56,33 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
panic(err) // TODO: Propagate error up
}

return &REST{store}
statusStore := *store
statusStore.UpdateStrategy = volumeattachment.StatusStrategy

return &VolumeAttachmentStorage{
VolumeAttachment: &REST{store},
Status: &StatusREST{store: &statusStore},
}
}

// StatusREST implements the REST endpoint for changing the status of a VolumeAttachment
type StatusREST struct {
store *genericregistry.Store
}

// New creates a new VolumeAttachment resource
func (r *StatusREST) New() runtime.Object {
return &storageapi.VolumeAttachment{}
}

// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}

// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
// subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
}
103 changes: 54 additions & 49 deletions pkg/registry/storage/volumeattachment/storage/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,31 @@ package storage
import (
"testing"

storageapiv1alpha1 "k8s.io/api/storage/v1alpha1"
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
"k8s.io/apiserver/pkg/registry/rest"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/api/testapi"
storageapi "k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/registry/registrytest"
)

func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, storageapi.GroupName)
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "volumeattachments",
}
volumeAttachmentStorage := NewREST(restOptions)
return volumeAttachmentStorage, server
volumeAttachmentStorage := NewStorage(restOptions)
return volumeAttachmentStorage.VolumeAttachment, volumeAttachmentStorage.Status, server
}

func validNewVolumeAttachment(name string) *storageapi.VolumeAttachment {
Expand All @@ -62,13 +63,7 @@ func validNewVolumeAttachment(name string) *storageapi.VolumeAttachment {
}

func TestCreate(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}

storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
Expand All @@ -93,20 +88,16 @@ func TestCreate(t *testing.T) {
}

func TestUpdate(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}

storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()

test.TestUpdate(
// valid
validNewVolumeAttachment("foo"),
// updateFunc
// we still allow status field to be set in both v1 and v1beta1
// it is just that in v1 the new value does not take effect.
func(obj runtime.Object) runtime.Object {
object := obj.(*storageapi.VolumeAttachment)
object.Status.Attached = true
Expand All @@ -122,55 +113,31 @@ func TestUpdate(t *testing.T) {
}

func TestDelete(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}

storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope().ReturnDeletedObject()
test.TestDelete(validNewVolumeAttachment("foo"))
}

func TestGet(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}

storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestGet(validNewVolumeAttachment("foo"))
}

func TestList(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}

storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestList(validNewVolumeAttachment("foo"))
}

func TestWatch(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}

storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
Expand All @@ -192,3 +159,41 @@ func TestWatch(t *testing.T) {
},
)
}

func TestEtcdStatusUpdate(t *testing.T) {
storage, statusStorage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
ctx := genericapirequest.NewDefaultContext()

attachment := validNewVolumeAttachment("foo")
if _, err := storage.Create(ctx, attachment, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
t.Fatalf("unexpected error: %v", err)
}
obj, err := storage.Get(ctx, attachment.ObjectMeta.Name, &metav1.GetOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}

// update status
attachmentIn := obj.(*storageapi.VolumeAttachment).DeepCopy()
attachmentIn.Status.Attached = true

_, _, err = statusStorage.Update(ctx, attachmentIn.Name, rest.DefaultUpdatedObjectInfo(attachmentIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
if err != nil {
t.Fatalf("Failed to update status: %v", err)
}

// validate object got updated
obj, err = storage.Get(ctx, attachmentIn.ObjectMeta.Name, &metav1.GetOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
attachmentOut := obj.(*storageapi.VolumeAttachment)
if !apiequality.Semantic.DeepEqual(attachmentIn.Spec, attachmentOut.Spec) {
t.Errorf("objects differ: %v", diff.ObjectDiff(attachmentOut.Spec, attachmentIn.Spec))
}
if !apiequality.Semantic.DeepEqual(attachmentIn.Status, attachmentOut.Status) {
t.Errorf("objects differ: %v", diff.ObjectDiff(attachmentOut.Status, attachmentIn.Status))
}
}
Loading

0 comments on commit 498cd61

Please sign in to comment.