mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-10-22 06:59:03 +00:00
Make CSINodeInfo and CSIDriver Core APIs
This PR is the first step to transition CSINodeInfo and CSIDriver CRD's to in-tree APIs. It adds them to the existing API group “storage.k8s.io” as core storage APIs.
This commit is contained in:
@@ -85,6 +85,8 @@ filegroup(
|
||||
"//pkg/registry/scheduling/rest:all-srcs",
|
||||
"//pkg/registry/settings/podpreset:all-srcs",
|
||||
"//pkg/registry/settings/rest:all-srcs",
|
||||
"//pkg/registry/storage/csidriver:all-srcs",
|
||||
"//pkg/registry/storage/csinode:all-srcs",
|
||||
"//pkg/registry/storage/rest:all-srcs",
|
||||
"//pkg/registry/storage/storageclass:all-srcs",
|
||||
"//pkg/registry/storage/volumeattachment:all-srcs",
|
||||
|
48
pkg/registry/storage/csidriver/BUILD
Normal file
48
pkg/registry/storage/csidriver/BUILD
Normal file
@@ -0,0 +1,48 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"strategy.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/storage/csidriver",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/apis/storage/validation:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/registry/storage/csidriver/storage:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["strategy_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1: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",
|
||||
],
|
||||
)
|
19
pkg/registry/storage/csidriver/doc.go
Normal file
19
pkg/registry/storage/csidriver/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package csidriver provides Registry interface and its REST
|
||||
// implementation for storing csidriver api objects.
|
||||
package csidriver
|
48
pkg/registry/storage/csidriver/storage/BUILD
Normal file
48
pkg/registry/storage/csidriver/storage/BUILD
Normal file
@@ -0,0 +1,48 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/storage/csidriver/storage",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/registry/storage/csidriver: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",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_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/v1beta1: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/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/storage/etcd/testing:go_default_library",
|
||||
],
|
||||
)
|
57
pkg/registry/storage/csidriver/storage/storage.go
Normal file
57
pkg/registry/storage/csidriver/storage/storage.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
storageapi "k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/storage/csidriver"
|
||||
)
|
||||
|
||||
// CSIDriverStorage includes storage for CSIDrivers and all subresources
|
||||
type CSIDriverStorage struct {
|
||||
CSIDriver *REST
|
||||
}
|
||||
|
||||
// REST object that will work for CSIDrivers
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewStorage returns a RESTStorage object that will work against CSIDrivers
|
||||
func NewStorage(optsGetter generic.RESTOptionsGetter) *CSIDriverStorage {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &storageapi.CSIDriver{} },
|
||||
NewListFunc: func() runtime.Object { return &storageapi.CSIDriverList{} },
|
||||
DefaultQualifiedResource: storageapi.Resource("csidrivers"),
|
||||
|
||||
CreateStrategy: csidriver.Strategy,
|
||||
UpdateStrategy: csidriver.Strategy,
|
||||
DeleteStrategy: csidriver.Strategy,
|
||||
ReturnDeletedObject: true,
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
|
||||
return &CSIDriverStorage{
|
||||
CSIDriver: &REST{store},
|
||||
}
|
||||
}
|
179
pkg/registry/storage/csidriver/storage/storage_test.go
Normal file
179
pkg/registry/storage/csidriver/storage/storage_test.go
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
|
||||
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/apiserver/pkg/registry/generic"
|
||||
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
|
||||
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) {
|
||||
etcdStorage, server := registrytest.NewEtcdStorage(t, storageapi.GroupName)
|
||||
restOptions := generic.RESTOptions{
|
||||
StorageConfig: etcdStorage,
|
||||
Decorator: generic.UndecoratedStorage,
|
||||
DeleteCollectionWorkers: 1,
|
||||
ResourcePrefix: "csidrivers",
|
||||
}
|
||||
csiDriverStorage := NewStorage(restOptions)
|
||||
return csiDriverStorage.CSIDriver, server
|
||||
}
|
||||
|
||||
func validNewCSIDriver(name string) *storageapi.CSIDriver {
|
||||
attachRequired := true
|
||||
podInfoOnMount := true
|
||||
return &storageapi.CSIDriver{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: storageapi.CSIDriverSpec{
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: &podInfoOnMount,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
csiDriver := validNewCSIDriver("foo")
|
||||
csiDriver.ObjectMeta = metav1.ObjectMeta{GenerateName: "foo"}
|
||||
attachNotRequired := false
|
||||
notPodInfoOnMount := false
|
||||
test.TestCreate(
|
||||
// valid
|
||||
csiDriver,
|
||||
// invalid
|
||||
&storageapi.CSIDriver{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "*BadName!"},
|
||||
Spec: storageapi.CSIDriverSpec{
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
notPodInfoOnMount := false
|
||||
|
||||
test.TestUpdate(
|
||||
// valid
|
||||
validNewCSIDriver("foo"),
|
||||
//invalid update
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*storageapi.CSIDriver)
|
||||
object.Spec.PodInfoOnMount = ¬PodInfoOnMount
|
||||
return object
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope().ReturnDeletedObject()
|
||||
test.TestDelete(validNewCSIDriver("foo"))
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestGet(validNewCSIDriver("foo"))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestList(validNewCSIDriver("foo"))
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestWatch(
|
||||
validNewCSIDriver("foo"),
|
||||
// matching labels
|
||||
[]labels.Set{},
|
||||
// not matching labels
|
||||
[]labels.Set{
|
||||
{"foo": "bar"},
|
||||
},
|
||||
// matching fields
|
||||
[]fields.Set{
|
||||
{"metadata.name": "foo"},
|
||||
},
|
||||
// not matching fields
|
||||
[]fields.Set{
|
||||
{"metadata.name": "bar"},
|
||||
},
|
||||
)
|
||||
}
|
78
pkg/registry/storage/csidriver/strategy.go
Normal file
78
pkg/registry/storage/csidriver/strategy.go
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csidriver
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/apis/storage/validation"
|
||||
)
|
||||
|
||||
// csiDriverStrategy implements behavior for CSIDriver objects
|
||||
type csiDriverStrategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// Strategy is the default logic that applies when creating and updating
|
||||
// CSIDriver objects via the REST API.
|
||||
var Strategy = csiDriverStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
func (csiDriverStrategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
|
||||
func (csiDriverStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
}
|
||||
|
||||
func (csiDriverStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||
csiDriver := obj.(*storage.CSIDriver)
|
||||
|
||||
errs := validation.ValidateCSIDriver(csiDriver)
|
||||
errs = append(errs, validation.ValidateCSIDriver(csiDriver)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (csiDriverStrategy) Canonicalize(obj runtime.Object) {
|
||||
}
|
||||
|
||||
func (csiDriverStrategy) AllowCreateOnUpdate() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a CSIDriver
|
||||
func (csiDriverStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
}
|
||||
|
||||
func (csiDriverStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newCSIDriverObj := obj.(*storage.CSIDriver)
|
||||
oldCSIDriverObj := old.(*storage.CSIDriver)
|
||||
errorList := validation.ValidateCSIDriver(newCSIDriverObj)
|
||||
return append(errorList, validation.ValidateCSIDriverUpdate(newCSIDriverObj, oldCSIDriverObj)...)
|
||||
}
|
||||
|
||||
func (csiDriverStrategy) AllowUnconditionalUpdate() bool {
|
||||
return false
|
||||
}
|
155
pkg/registry/storage/csidriver/strategy_test.go
Normal file
155
pkg/registry/storage/csidriver/strategy_test.go
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csidriver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
)
|
||||
|
||||
func getValidCSIDriver(name string) *storage.CSIDriver {
|
||||
attachRequired := true
|
||||
podInfoOnMount := true
|
||||
return &storage.CSIDriver{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: &podInfoOnMount,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSIDriverStrategy(t *testing.T) {
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
|
||||
APIGroup: "storage.k8s.io",
|
||||
APIVersion: "v1beta1",
|
||||
Resource: "csidrivers",
|
||||
})
|
||||
if Strategy.NamespaceScoped() {
|
||||
t.Errorf("CSIDriver must not be namespace scoped")
|
||||
}
|
||||
if Strategy.AllowCreateOnUpdate() {
|
||||
t.Errorf("CSIDriver should not allow create on update")
|
||||
}
|
||||
|
||||
csiDriver := getValidCSIDriver("valid-csidriver")
|
||||
|
||||
Strategy.PrepareForCreate(ctx, csiDriver)
|
||||
|
||||
errs := Strategy.Validate(ctx, csiDriver)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("unexpected error validating %v", errs)
|
||||
}
|
||||
|
||||
// Update of spec is disallowed
|
||||
newCSIDriver := csiDriver.DeepCopy()
|
||||
attachNotRequired := false
|
||||
newCSIDriver.Spec.AttachRequired = &attachNotRequired
|
||||
|
||||
Strategy.PrepareForUpdate(ctx, newCSIDriver, csiDriver)
|
||||
|
||||
errs = Strategy.ValidateUpdate(ctx, newCSIDriver, csiDriver)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("Expected a validation error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSIDriverValidation(t *testing.T) {
|
||||
attachRequired := true
|
||||
notAttachRequired := false
|
||||
podInfoOnMount := true
|
||||
notPodInfoOnMount := false
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
csiDriver *storage.CSIDriver
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"valid csidriver",
|
||||
getValidCSIDriver("foo"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"true PodInfoOnMount and AttachRequired",
|
||||
&storage.CSIDriver{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: &podInfoOnMount,
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"false PodInfoOnMount and AttachRequired",
|
||||
&storage.CSIDriver{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: ¬AttachRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid driver name",
|
||||
&storage.CSIDriver{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "*foo#",
|
||||
},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: &podInfoOnMount,
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
testValidation := func(csiDriver *storage.CSIDriver, apiVersion string) field.ErrorList {
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
|
||||
APIGroup: "storage.k8s.io",
|
||||
APIVersion: "v1beta1",
|
||||
Resource: "csidrivers",
|
||||
})
|
||||
return Strategy.Validate(ctx, csiDriver)
|
||||
}
|
||||
|
||||
betaErr := testValidation(test.csiDriver, "v1beta1")
|
||||
if len(betaErr) > 0 && !test.expectError {
|
||||
t.Errorf("Validation of v1beta1 object failed: %+v", betaErr)
|
||||
}
|
||||
if len(betaErr) == 0 && test.expectError {
|
||||
t.Errorf("Validation of v1beta1 object unexpectedly succeeded")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
48
pkg/registry/storage/csinode/BUILD
Normal file
48
pkg/registry/storage/csinode/BUILD
Normal file
@@ -0,0 +1,48 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"strategy.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/storage/csinode",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/apis/storage/validation:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/registry/storage/csinode/storage:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["strategy_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1: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",
|
||||
],
|
||||
)
|
19
pkg/registry/storage/csinode/doc.go
Normal file
19
pkg/registry/storage/csinode/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package csinode provides Registry interface and its REST
|
||||
// implementation for storing csinode api objects.
|
||||
package csinode
|
48
pkg/registry/storage/csinode/storage/BUILD
Normal file
48
pkg/registry/storage/csinode/storage/BUILD
Normal file
@@ -0,0 +1,48 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/storage/csinode/storage",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/registry/storage/csinode: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",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_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/v1beta1: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/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/storage/etcd/testing:go_default_library",
|
||||
],
|
||||
)
|
57
pkg/registry/storage/csinode/storage/storage.go
Normal file
57
pkg/registry/storage/csinode/storage/storage.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
storageapi "k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/storage/csinode"
|
||||
)
|
||||
|
||||
// CSINodeStorage includes storage for CSINodes and all subresources
|
||||
type CSINodeStorage struct {
|
||||
CSINode *REST
|
||||
}
|
||||
|
||||
// REST object that will work for CSINodes
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewStorage returns a RESTStorage object that will work against CSINodes
|
||||
func NewStorage(optsGetter generic.RESTOptionsGetter) *CSINodeStorage {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &storageapi.CSINode{} },
|
||||
NewListFunc: func() runtime.Object { return &storageapi.CSINodeList{} },
|
||||
DefaultQualifiedResource: storageapi.Resource("csinodes"),
|
||||
|
||||
CreateStrategy: csinode.Strategy,
|
||||
UpdateStrategy: csinode.Strategy,
|
||||
DeleteStrategy: csinode.Strategy,
|
||||
ReturnDeletedObject: true,
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
|
||||
return &CSINodeStorage{
|
||||
CSINode: &REST{store},
|
||||
}
|
||||
}
|
190
pkg/registry/storage/csinode/storage/storage_test.go
Normal file
190
pkg/registry/storage/csinode/storage/storage_test.go
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
|
||||
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/apiserver/pkg/registry/generic"
|
||||
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
|
||||
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) {
|
||||
etcdStorage, server := registrytest.NewEtcdStorage(t, storageapi.GroupName)
|
||||
restOptions := generic.RESTOptions{
|
||||
StorageConfig: etcdStorage,
|
||||
Decorator: generic.UndecoratedStorage,
|
||||
DeleteCollectionWorkers: 1,
|
||||
ResourcePrefix: "csinodes",
|
||||
}
|
||||
csiNodeStorage := NewStorage(restOptions)
|
||||
return csiNodeStorage.CSINode, server
|
||||
}
|
||||
|
||||
func validNewCSINode(name string) *storageapi.CSINode {
|
||||
return &storageapi.CSINode{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: storageapi.CSINodeSpec{
|
||||
Drivers: []storageapi.CSINodeDriver{
|
||||
{
|
||||
Name: "valid-driver-name",
|
||||
NodeID: "valid-node",
|
||||
TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
csiNode := validNewCSINode("foo")
|
||||
csiNode.ObjectMeta = metav1.ObjectMeta{GenerateName: "foo"}
|
||||
test.TestCreate(
|
||||
// valid
|
||||
csiNode,
|
||||
// invalid
|
||||
&storageapi.CSINode{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "*BadName!"},
|
||||
Spec: storageapi.CSINodeSpec{
|
||||
Drivers: []storageapi.CSINodeDriver{
|
||||
{
|
||||
Name: "invalid-name-!@#$%^&*()",
|
||||
NodeID: "invalid-node-!@#$%^&*()",
|
||||
TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
|
||||
test.TestUpdate(
|
||||
// valid
|
||||
validNewCSINode("foo"),
|
||||
// we allow status field to be set in v1beta1
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*storageapi.CSINode)
|
||||
//object.Status = *getCSINodeStatus()
|
||||
return object
|
||||
},
|
||||
//invalid update
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*storageapi.CSINode)
|
||||
object.Spec.Drivers[0].Name = "invalid-name-!@#$%^&*()"
|
||||
return object
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope().ReturnDeletedObject()
|
||||
test.TestDelete(validNewCSINode("foo"))
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestGet(validNewCSINode("foo"))
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestList(validNewCSINode("foo"))
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
if *testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
|
||||
// skip the test for all versions exception v1beta1
|
||||
return
|
||||
}
|
||||
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestWatch(
|
||||
validNewCSINode("foo"),
|
||||
// matching labels
|
||||
[]labels.Set{},
|
||||
// not matching labels
|
||||
[]labels.Set{
|
||||
{"foo": "bar"},
|
||||
},
|
||||
// matching fields
|
||||
[]fields.Set{
|
||||
{"metadata.name": "foo"},
|
||||
},
|
||||
// not matching fields
|
||||
[]fields.Set{
|
||||
{"metadata.name": "bar"},
|
||||
},
|
||||
)
|
||||
}
|
78
pkg/registry/storage/csinode/strategy.go
Normal file
78
pkg/registry/storage/csinode/strategy.go
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csinode
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/apis/storage/validation"
|
||||
)
|
||||
|
||||
// csiNodeStrategy implements behavior for CSINode objects
|
||||
type csiNodeStrategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// Strategy is the default logic that applies when creating and updating
|
||||
// CSINode objects via the REST API.
|
||||
var Strategy = csiNodeStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
func (csiNodeStrategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
|
||||
func (csiNodeStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
}
|
||||
|
||||
func (csiNodeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||
csiNode := obj.(*storage.CSINode)
|
||||
|
||||
errs := validation.ValidateCSINode(csiNode)
|
||||
errs = append(errs, validation.ValidateCSINode(csiNode)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (csiNodeStrategy) Canonicalize(obj runtime.Object) {
|
||||
}
|
||||
|
||||
func (csiNodeStrategy) AllowCreateOnUpdate() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a CSINode
|
||||
func (csiNodeStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
}
|
||||
|
||||
func (csiNodeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newCSINodeObj := obj.(*storage.CSINode)
|
||||
oldCSINodeObj := old.(*storage.CSINode)
|
||||
errorList := validation.ValidateCSINode(newCSINodeObj)
|
||||
return append(errorList, validation.ValidateCSINodeUpdate(newCSINodeObj, oldCSINodeObj)...)
|
||||
}
|
||||
|
||||
func (csiNodeStrategy) AllowUnconditionalUpdate() bool {
|
||||
return false
|
||||
}
|
167
pkg/registry/storage/csinode/strategy_test.go
Normal file
167
pkg/registry/storage/csinode/strategy_test.go
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csinode
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
)
|
||||
|
||||
func getValidCSINode(name string) *storage.CSINode {
|
||||
return &storage.CSINode{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: storage.CSINodeSpec{
|
||||
Drivers: []storage.CSINodeDriver{
|
||||
{
|
||||
Name: "valid-driver-name",
|
||||
NodeID: "valid-node",
|
||||
TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSINodeStrategy(t *testing.T) {
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
|
||||
APIGroup: "storage.k8s.io",
|
||||
APIVersion: "v1beta1",
|
||||
Resource: "csinodes",
|
||||
})
|
||||
if Strategy.NamespaceScoped() {
|
||||
t.Errorf("CSINode must not be namespace scoped")
|
||||
}
|
||||
if Strategy.AllowCreateOnUpdate() {
|
||||
t.Errorf("CSINode should not allow create on update")
|
||||
}
|
||||
|
||||
csiNode := getValidCSINode("valid-csinode")
|
||||
|
||||
Strategy.PrepareForCreate(ctx, csiNode)
|
||||
|
||||
errs := Strategy.Validate(ctx, csiNode)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("unexpected error validating %v", errs)
|
||||
}
|
||||
|
||||
// Update of spec is allowed
|
||||
newCSINode := csiNode.DeepCopy()
|
||||
newCSINode.Spec.Drivers[0].NodeID = "valid-node-2"
|
||||
|
||||
Strategy.PrepareForUpdate(ctx, newCSINode, csiNode)
|
||||
|
||||
errs = Strategy.ValidateUpdate(ctx, newCSINode, csiNode)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected validation error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSINodeValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
csiNode *storage.CSINode
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"valid csinode",
|
||||
getValidCSINode("foo"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid driver name",
|
||||
&storage.CSINode{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: storage.CSINodeSpec{
|
||||
Drivers: []storage.CSINodeDriver{
|
||||
{
|
||||
Name: "$csi-driver@",
|
||||
NodeID: "valid-node",
|
||||
TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"empty node id",
|
||||
&storage.CSINode{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: storage.CSINodeSpec{
|
||||
Drivers: []storage.CSINodeDriver{
|
||||
{
|
||||
Name: "valid-driver-name",
|
||||
NodeID: "",
|
||||
TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid topology keys",
|
||||
&storage.CSINode{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: storage.CSINodeSpec{
|
||||
Drivers: []storage.CSINodeDriver{
|
||||
{
|
||||
Name: "valid-driver-name",
|
||||
NodeID: "valid-node",
|
||||
TopologyKeys: []string{"company.com/zone1", ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
testValidation := func(csiNode *storage.CSINode, apiVersion string) field.ErrorList {
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
|
||||
APIGroup: "storage.k8s.io",
|
||||
APIVersion: "v1beta1",
|
||||
Resource: "csinodes",
|
||||
})
|
||||
return Strategy.Validate(ctx, csiNode)
|
||||
}
|
||||
|
||||
betaErr := testValidation(test.csiNode, "v1beta1")
|
||||
if len(betaErr) > 0 && !test.expectError {
|
||||
t.Errorf("Validation of v1beta1 object failed: %+v", betaErr)
|
||||
}
|
||||
if len(betaErr) == 0 && test.expectError {
|
||||
t.Errorf("Validation of v1beta1 object unexpectedly succeeded")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -12,6 +12,9 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/registry/storage/csidriver/storage:go_default_library",
|
||||
"//pkg/registry/storage/csinode/storage:go_default_library",
|
||||
"//pkg/registry/storage/storageclass/storage:go_default_library",
|
||||
"//pkg/registry/storage/volumeattachment/storage:go_default_library",
|
||||
"//staging/src/k8s.io/api/storage/v1:go_default_library",
|
||||
@@ -21,6 +24,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/storage:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@@ -24,8 +24,12 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
storageapi "k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
csidriverstore "k8s.io/kubernetes/pkg/registry/storage/csidriver/storage"
|
||||
csinodestore "k8s.io/kubernetes/pkg/registry/storage/csinode/storage"
|
||||
storageclassstore "k8s.io/kubernetes/pkg/registry/storage/storageclass/storage"
|
||||
volumeattachmentstore "k8s.io/kubernetes/pkg/registry/storage/volumeattachment/storage"
|
||||
)
|
||||
@@ -70,6 +74,18 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag
|
||||
volumeAttachmentStorage := volumeattachmentstore.NewStorage(restOptionsGetter)
|
||||
storage["volumeattachments"] = volumeAttachmentStorage.VolumeAttachment
|
||||
|
||||
// register csinodes if CSINodeInfo feature gate is enabled
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CSINodeInfo) {
|
||||
csiNodeStorage := csinodestore.NewStorage(restOptionsGetter)
|
||||
storage["csinodes"] = csiNodeStorage.CSINode
|
||||
}
|
||||
|
||||
// register csidrivers if CSIDriverRegistry feature gate is enabled
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CSIDriverRegistry) {
|
||||
csiDriverStorage := csidriverstore.NewStorage(restOptionsGetter)
|
||||
storage["csidrivers"] = csiDriverStorage.CSIDriver
|
||||
}
|
||||
|
||||
return storage
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user