diff --git a/cmd/libs/go2idl/client-gen/main.go b/cmd/libs/go2idl/client-gen/main.go index 7e38ea7019d..51f2a9ae5d8 100644 --- a/cmd/libs/go2idl/client-gen/main.go +++ b/cmd/libs/go2idl/client-gen/main.go @@ -42,6 +42,7 @@ var ( "certificates/", "extensions/", "rbac/", + "storage/", }, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/,rbac/\"") includedTypesOverrides = flag.StringSlice("included-types-overrides", []string{}, "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient=true in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient=true will be used for other group versions.") basePath = flag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group. Default to \"k8s.io/kubernetes/pkg/apis\"") diff --git a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go index cec560097d5..76768ad4a6b 100644 --- a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go +++ b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go @@ -78,6 +78,7 @@ func New() *Generator { `k8s.io/kubernetes/federation/apis/federation/v1beta1`, `k8s.io/kubernetes/pkg/apis/certificates/v1alpha1`, `k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1`, + `k8s.io/kubernetes/pkg/apis/storage/v1beta1`, }, ","), DropEmbeddedFields: "k8s.io/kubernetes/pkg/api/unversioned.TypeMeta", } diff --git a/hack/make-rules/test-cmd.sh b/hack/make-rules/test-cmd.sh index e6a824790fd..e77b06dfe9e 100755 --- a/hack/make-rules/test-cmd.sh +++ b/hack/make-rules/test-cmd.sh @@ -2388,7 +2388,7 @@ __EOF__ kubectl create -f - "${kube_flags[@]}" << __EOF__ { "kind": "StorageClass", - "apiVersion": "extensions/v1beta1", + "apiVersion": "storage.k8s.io/v1beta1", "metadata": { "name": "storage-class-name" }, diff --git a/hack/make-rules/test-integration.sh b/hack/make-rules/test-integration.sh index ad7df722525..c550a27dba1 100755 --- a/hack/make-rules/test-integration.sh +++ b/hack/make-rules/test-integration.sh @@ -27,7 +27,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh" # KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1"} # FIXME: due to current implementation of a test client (see: pkg/api/testapi/testapi.go) # ONLY the last version is tested in each group. -KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,apps/v1alpha1,policy/v1alpha1,extensions/v1beta1,rbac.authorization.k8s.io/v1alpha1,certificates.k8s.io/v1alpha1"} +KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,apps/v1alpha1,policy/v1alpha1,extensions/v1beta1,rbac.authorization.k8s.io/v1alpha1,certificates.k8s.io/v1alpha1,storage.k8s.io/v1beta1"} # Give integration tests longer to run # TODO: allow a larger value to be passed in diff --git a/hack/make-rules/test.sh b/hack/make-rules/test.sh index 4c30b877df5..3484991c7dc 100755 --- a/hack/make-rules/test.sh +++ b/hack/make-rules/test.sh @@ -66,7 +66,7 @@ KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-} # "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3" # FIXME: due to current implementation of a test client (see: pkg/api/testapi/testapi.go) # ONLY the last version is tested in each group. -KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,batch/v2alpha1,certificates.k8s.io/v1alpha1,extensions/v1beta1,federation/v1beta1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1,imagepolicy.k8s.io/v1alpha1"} +KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,batch/v2alpha1,certificates.k8s.io/v1alpha1,extensions/v1beta1,federation/v1beta1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1,imagepolicy.k8s.io/v1alpha1,storage.k8s.io/v1beta1"} # once we have multiple group supports # Create a junit-style XML test report in this directory if set. KUBE_JUNIT_REPORT_DIR=${KUBE_JUNIT_REPORT_DIR:-} diff --git a/hack/update-generated-swagger-docs.sh b/hack/update-generated-swagger-docs.sh index af56c9de929..6bac92e66ef 100755 --- a/hack/update-generated-swagger-docs.sh +++ b/hack/update-generated-swagger-docs.sh @@ -28,7 +28,7 @@ source "${KUBE_ROOT}/hack/lib/swagger.sh" kube::golang::setup_env -GROUP_VERSIONS=(unversioned v1 authentication/v1beta1 authorization/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac/v1alpha1 certificates/v1alpha1) +GROUP_VERSIONS=(unversioned v1 authentication/v1beta1 authorization/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac/v1alpha1 storage/v1beta1 certificates/v1alpha1) # To avoid compile errors, remove the currently existing files. for group_version in "${GROUP_VERSIONS[@]}"; do rm -f "pkg/$(kube::util::group-version-to-pkg-path "${group_version}")/types_swagger_doc_generated.go" diff --git a/hack/update-swagger-spec.sh b/hack/update-swagger-spec.sh index 46f690184e7..486fbe6de07 100755 --- a/hack/update-swagger-spec.sh +++ b/hack/update-swagger-spec.sh @@ -73,7 +73,7 @@ APISERVER_PID=$! kube::util::wait_for_url "${API_HOST}:${API_PORT}/healthz" "apiserver: " SWAGGER_API_PATH="${API_HOST}:${API_PORT}/swaggerapi/" -DEFAULT_GROUP_VERSIONS="v1 apps/v1alpha1 authentication.k8s.io/v1beta1 authorization.k8s.io/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 certificates.k8s.io/v1alpha1 policy/v1alpha1 rbac.authorization.k8s.io/v1alpha1" +DEFAULT_GROUP_VERSIONS="v1 apps/v1alpha1 authentication.k8s.io/v1beta1 authorization.k8s.io/v1beta1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 certificates.k8s.io/v1alpha1 policy/v1alpha1 rbac.authorization.k8s.io/v1alpha1 storage.k8s.io/v1beta1" VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS} kube::log::status "Updating " ${SWAGGER_ROOT_DIR} diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index 0ad8b08c678..fb33e1f0362 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -37,6 +37,7 @@ import ( "k8s.io/kubernetes/pkg/apis/imagepolicy" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime/serializer/recognizer" @@ -53,6 +54,7 @@ import ( _ "k8s.io/kubernetes/pkg/apis/imagepolicy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" + _ "k8s.io/kubernetes/pkg/apis/storage/install" ) var ( @@ -66,6 +68,7 @@ var ( Federation TestGroup Rbac TestGroup Certificates TestGroup + Storage TestGroup ImagePolicy TestGroup serializer runtime.SerializerInfo @@ -218,6 +221,15 @@ func init() { externalTypes: api.Scheme.KnownTypes(externalGroupVersion), } } + if _, ok := Groups[storage.GroupName]; !ok { + externalGroupVersion := unversioned.GroupVersion{Group: storage.GroupName, Version: registered.GroupOrDie(storage.GroupName).GroupVersion.Version} + Groups[storage.GroupName] = TestGroup{ + externalGroupVersion: externalGroupVersion, + internalGroupVersion: storage.SchemeGroupVersion, + internalTypes: api.Scheme.KnownTypes(storage.SchemeGroupVersion), + externalTypes: api.Scheme.KnownTypes(externalGroupVersion), + } + } if _, ok := Groups[certificates.GroupName]; !ok { externalGroupVersion := unversioned.GroupVersion{Group: certificates.GroupName, Version: registered.GroupOrDie(certificates.GroupName).GroupVersion.Version} Groups[certificates.GroupName] = TestGroup{ @@ -247,6 +259,7 @@ func init() { Extensions = Groups[extensions.GroupName] Federation = Groups[federation.GroupName] Rbac = Groups[rbac.GroupName] + Storage = Groups[storage.GroupName] ImagePolicy = Groups[imagepolicy.GroupName] } diff --git a/pkg/apis/extensions/register.go b/pkg/apis/extensions/register.go index fca5b32b6ed..31fc20a8ae1 100644 --- a/pkg/apis/extensions/register.go +++ b/pkg/apis/extensions/register.go @@ -76,8 +76,6 @@ func addKnownTypes(scheme *runtime.Scheme) error { &PodSecurityPolicyList{}, &NetworkPolicy{}, &NetworkPolicyList{}, - &StorageClass{}, - &StorageClassList{}, ) return nil } diff --git a/pkg/apis/extensions/types.go b/pkg/apis/extensions/types.go index cd02c3bb16c..59e87164dbd 100644 --- a/pkg/apis/extensions/types.go +++ b/pkg/apis/extensions/types.go @@ -911,41 +911,3 @@ type NetworkPolicyList struct { Items []NetworkPolicy `json:"items"` } - -// +genclient=true -// +nonNamespaced=true - -// StorageClass describes a named "class" of storage offered in a cluster. -// Different classes might map to quality-of-service levels, or to backup policies, -// or to arbitrary policies determined by the cluster administrators. Kubernetes -// itself is unopinionated about what classes represent. This concept is sometimes -// called "profiles" in other storage systems. -// The name of a StorageClass object is significant, and is how users can request a particular class. -type StorageClass struct { - unversioned.TypeMeta `json:",inline"` - api.ObjectMeta `json:"metadata,omitempty"` - - // provisioner is the driver expected to handle this StorageClass. - // This is an optionally-prefixed name, like a label key. - // For example: "kubernetes.io/gce-pd" or "kubernetes.io/aws-ebs". - // This value may not be empty. - Provisioner string `json:"provisioner"` - - // parameters holds parameters for the provisioner. - // These values are opaque to the system and are passed directly - // to the provisioner. The only validation done on keys is that they are - // not empty. The maximum number of parameters is - // 512, with a cumulative max size of 256K - Parameters map[string]string `json:"parameters,omitempty"` -} - -// StorageClassList is a collection of storage classes. -type StorageClassList struct { - unversioned.TypeMeta `json:",inline"` - // Standard list metadata - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - unversioned.ListMeta `json:"metadata,omitempty"` - - // Items is the list of StorageClasses - Items []StorageClass `json:"items"` -} diff --git a/pkg/apis/extensions/v1beta1/register.go b/pkg/apis/extensions/v1beta1/register.go index bc67a7f2128..c98235d43d6 100644 --- a/pkg/apis/extensions/v1beta1/register.go +++ b/pkg/apis/extensions/v1beta1/register.go @@ -62,8 +62,6 @@ func addKnownTypes(scheme *runtime.Scheme) error { &PodSecurityPolicyList{}, &NetworkPolicy{}, &NetworkPolicyList{}, - &StorageClass{}, - &StorageClassList{}, ) // Add the watch version that applies versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/pkg/apis/extensions/v1beta1/types.go b/pkg/apis/extensions/v1beta1/types.go index b422c7329b7..f3f10820e16 100644 --- a/pkg/apis/extensions/v1beta1/types.go +++ b/pkg/apis/extensions/v1beta1/types.go @@ -1199,36 +1199,3 @@ type NetworkPolicyList struct { // Items is a list of schema objects. Items []NetworkPolicy `json:"items" protobuf:"bytes,2,rep,name=items"` } - -// +genclient=true -// +nonNamespaced=true - -// StorageClass describes the parameters for a class of storage for -// which PersistentVolumes can be dynamically provisioned. -// -// StorageClasses are non-namespaced; the name of the storage class -// according to etcd is in ObjectMeta.Name. -type StorageClass struct { - unversioned.TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - v1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // Provisioner indicates the type of the provisioner. - Provisioner string `json:"provisioner" protobuf:"bytes,2,opt,name=provisioner"` - - // Parameters holds the parameters for the provisioner that should - // create volumes of this storage class. - Parameters map[string]string `json:"parameters,omitempty" protobuf:"bytes,3,rep,name=parameters"` -} - -// StorageClassList is a collection of storage classes. -type StorageClassList struct { - unversioned.TypeMeta `json:",inline"` - // Standard list metadata - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - unversioned.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // Items is the list of StorageClasses - Items []StorageClass `json:"items" protobuf:"bytes,2,rep,name=items"` -} diff --git a/pkg/apis/extensions/validation/validation.go b/pkg/apis/extensions/validation/validation.go index 062f1b94925..61fda57c0de 100644 --- a/pkg/apis/extensions/validation/validation.go +++ b/pkg/apis/extensions/validation/validation.go @@ -822,65 +822,3 @@ func ValidateNetworkPolicyUpdate(update, old *extensions.NetworkPolicy) field.Er } return allErrs } - -// ValidateStorageClass validates a StorageClass. -func ValidateStorageClass(storageClass *extensions.StorageClass) field.ErrorList { - allErrs := apivalidation.ValidateObjectMeta(&storageClass.ObjectMeta, false, apivalidation.NameIsDNSSubdomain, field.NewPath("metadata")) - allErrs = append(allErrs, validateProvisioner(storageClass.Provisioner, field.NewPath("provisioner"))...) - allErrs = append(allErrs, validateParameters(storageClass.Parameters, field.NewPath("parameters"))...) - - return allErrs -} - -// ValidateStorageClassUpdate tests if an update to StorageClass is valid. -func ValidateStorageClassUpdate(storageClass, oldStorageClass *extensions.StorageClass) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&storageClass.ObjectMeta, &oldStorageClass.ObjectMeta, field.NewPath("metadata")) - if !reflect.DeepEqual(oldStorageClass.Parameters, storageClass.Parameters) { - allErrs = append(allErrs, field.Forbidden(field.NewPath("parameters"), "updates to parameters are forbidden.")) - } - - if strings.Compare(storageClass.Provisioner, oldStorageClass.Provisioner) != 0 { - allErrs = append(allErrs, field.Forbidden(field.NewPath("provisioner"), "updates to provisioner are forbidden.")) - } - return allErrs -} - -// validateProvisioner tests if provisioner is a valid qualified name. -func validateProvisioner(provisioner string, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if len(provisioner) == 0 { - allErrs = append(allErrs, field.Required(fldPath, provisioner)) - } - if len(provisioner) > 0 { - for _, msg := range validation.IsQualifiedName(strings.ToLower(provisioner)) { - allErrs = append(allErrs, field.Invalid(fldPath, provisioner, msg)) - } - } - return allErrs -} - -const maxProvisionerParameterSize = 256 * (1 << 10) // 256 kB -const maxProvisionerParameterLen = 512 - -// validateParameters tests that keys are qualified names and that provisionerParameter are < 256kB. -func validateParameters(params map[string]string, fldPath *field.Path) field.ErrorList { - var totalSize int64 - allErrs := field.ErrorList{} - - if len(params) > maxProvisionerParameterLen { - allErrs = append(allErrs, field.TooLong(fldPath, "Provisioner Parameters exceeded max allowed", maxProvisionerParameterLen)) - return allErrs - } - - for k, v := range params { - if len(k) < 1 { - allErrs = append(allErrs, field.Invalid(fldPath, k, "field can not be empty.")) - } - totalSize += (int64)(len(k)) + (int64)(len(v)) - } - - if totalSize > maxProvisionerParameterSize { - allErrs = append(allErrs, field.TooLong(fldPath, "", maxProvisionerParameterSize)) - } - return allErrs -} diff --git a/pkg/apis/extensions/validation/validation_test.go b/pkg/apis/extensions/validation/validation_test.go index 467f7bc1012..eb43ee7e9a3 100644 --- a/pkg/apis/extensions/validation/validation_test.go +++ b/pkg/apis/extensions/validation/validation_test.go @@ -2133,80 +2133,3 @@ func newBool(val bool) *bool { *p = val return p } - -func TestValidateStorageClass(t *testing.T) { - successCases := []extensions.StorageClass{ - { - // empty parameters - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Provisioner: "kubernetes.io/foo-provisioner", - Parameters: map[string]string{}, - }, - { - // nil parameters - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Provisioner: "kubernetes.io/foo-provisioner", - }, - { - // some parameters - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Provisioner: "kubernetes.io/foo-provisioner", - Parameters: map[string]string{ - "kubernetes.io/foo-parameter": "free/form/string", - "foo-parameter": "free-form-string", - "foo-parameter2": "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}", - }, - }, - } - - // Success cases are expected to pass validation. - for k, v := range successCases { - if errs := ValidateStorageClass(&v); len(errs) != 0 { - t.Errorf("Expected success for %d, got %v", k, errs) - } - } - - // generate a map longer than maxProvisionerParameterSize - longParameters := make(map[string]string) - totalSize := 0 - for totalSize < maxProvisionerParameterSize { - k := fmt.Sprintf("param/%d", totalSize) - v := fmt.Sprintf("value-%d", totalSize) - longParameters[k] = v - totalSize = totalSize + len(k) + len(v) - } - - errorCases := map[string]extensions.StorageClass{ - "namespace is present": { - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, - Provisioner: "kubernetes.io/foo-provisioner", - }, - "invalid provisioner": { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Provisioner: "kubernetes.io/invalid/provisioner", - }, - "invalid empty parameter name": { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Provisioner: "kubernetes.io/foo", - Parameters: map[string]string{ - "": "value", - }, - }, - "provisioner: Required value": { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Provisioner: "", - }, - "too long parameters": { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Provisioner: "kubernetes.io/foo", - Parameters: longParameters, - }, - } - - // Error cases are not expected to pass validation. - for testName, storageClass := range errorCases { - if errs := ValidateStorageClass(&storageClass); len(errs) == 0 { - t.Errorf("Expected failure for test: %s", testName) - } - } -} diff --git a/pkg/apis/storage/doc.go b/pkg/apis/storage/doc.go new file mode 100644 index 00000000000..51dbb344c74 --- /dev/null +++ b/pkg/apis/storage/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2016 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. +*/ + +// +k8s:deepcopy-gen=package,register +// +groupName=storage.k8s.io +package storage // import "k8s.io/kubernetes/pkg/apis/storage" diff --git a/pkg/apis/storage/install/install.go b/pkg/apis/storage/install/install.go new file mode 100644 index 00000000000..52801618bc6 --- /dev/null +++ b/pkg/apis/storage/install/install.go @@ -0,0 +1,137 @@ +/* +Copyright 2016 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 install installs the experimental API group, making it available as +// an option to all of the API encoding/decoding machinery. +package install + +import ( + "fmt" + + "github.com/golang/glog" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/storage" + "k8s.io/kubernetes/pkg/apis/storage/v1beta1" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/sets" +) + +const importPrefix = "k8s.io/kubernetes/pkg/apis/storage" + +var accessor = meta.NewAccessor() + +// availableVersions lists all known external versions for this group from most preferred to least preferred +var availableVersions = []unversioned.GroupVersion{v1beta1.SchemeGroupVersion} + +func init() { + registered.RegisterVersions(availableVersions) + externalVersions := []unversioned.GroupVersion{} + for _, v := range availableVersions { + if registered.IsAllowedVersion(v) { + externalVersions = append(externalVersions, v) + } + } + if len(externalVersions) == 0 { + glog.V(4).Infof("No version is registered for group %v", storage.GroupName) + return + } + + if err := registered.EnableVersions(externalVersions...); err != nil { + glog.V(4).Infof("%v", err) + return + } + if err := enableVersions(externalVersions); err != nil { + glog.V(4).Infof("%v", err) + return + } +} + +// TODO: enableVersions should be centralized rather than spread in each API +// group. +// We can combine registered.RegisterVersions, registered.EnableVersions and +// registered.RegisterGroup once we have moved enableVersions there. +func enableVersions(externalVersions []unversioned.GroupVersion) error { + addVersionsToScheme(externalVersions...) + preferredExternalVersion := externalVersions[0] + + groupMeta := apimachinery.GroupMeta{ + GroupVersion: preferredExternalVersion, + GroupVersions: externalVersions, + RESTMapper: newRESTMapper(externalVersions), + SelfLinker: runtime.SelfLinker(accessor), + InterfacesFor: interfacesFor, + } + + if err := registered.RegisterGroup(groupMeta); err != nil { + return err + } + api.RegisterRESTMapper(groupMeta.RESTMapper) + return nil +} + +func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { + // the list of kinds that are scoped at the root of the api hierarchy + // if a kind is not enumerated here, it is assumed to have a namespace scope + rootScoped := sets.NewString( + "StorageClass", + ) + + ignoredKinds := sets.NewString() + + return api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) +} + +// interfacesFor returns the default Codec and ResourceVersioner for a given version +// string, or an error if the version is not known. +func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { + switch version { + case v1beta1.SchemeGroupVersion: + return &meta.VersionInterfaces{ + ObjectConvertor: api.Scheme, + MetadataAccessor: accessor, + }, nil + default: + g, _ := registered.Group(storage.GroupName) + return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions) + } +} + +func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) { + // add the internal version to Scheme + if err := storage.AddToScheme(api.Scheme); err != nil { + // Programmer error, detect immediately + panic(err) + } + // add the enabled external versions to Scheme + for _, v := range externalVersions { + if !registered.IsEnabledVersion(v) { + glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v) + continue + } + switch v { + case v1beta1.SchemeGroupVersion: + if err := v1beta1.AddToScheme(api.Scheme); err != nil { + // Programmer error, detect immediately + panic(err) + } + } + } +} diff --git a/pkg/apis/storage/register.go b/pkg/apis/storage/register.go new file mode 100644 index 00000000000..2da8d17c2c8 --- /dev/null +++ b/pkg/apis/storage/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2015 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/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" +) + +// GroupName is the group name use in this package +const GroupName = "storage.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Kind takes an unqualified kind and returns a Group qualified GroupKind +func Kind(kind string) unversioned.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) unversioned.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &api.ListOptions{}, + &api.DeleteOptions{}, + &api.ExportOptions{}, + + &StorageClass{}, + &StorageClassList{}, + ) + return nil +} diff --git a/pkg/apis/storage/types.go b/pkg/apis/storage/types.go new file mode 100644 index 00000000000..be0831a6bf7 --- /dev/null +++ b/pkg/apis/storage/types.go @@ -0,0 +1,60 @@ +/* +Copyright 2016 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/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" +) + +// +genclient=true +// +nonNamespaced=true + +// StorageClass describes a named "class" of storage offered in a cluster. +// Different classes might map to quality-of-service levels, or to backup policies, +// or to arbitrary policies determined by the cluster administrators. Kubernetes +// itself is unopinionated about what classes represent. This concept is sometimes +// called "profiles" in other storage systems. +// The name of a StorageClass object is significant, and is how users can request a particular class. +type StorageClass struct { + unversioned.TypeMeta `json:",inline"` + api.ObjectMeta `json:"metadata,omitempty"` + + // provisioner is the driver expected to handle this StorageClass. + // This is an optionally-prefixed name, like a label key. + // For example: "kubernetes.io/gce-pd" or "kubernetes.io/aws-ebs". + // This value may not be empty. + Provisioner string `json:"provisioner"` + + // parameters holds parameters for the provisioner. + // These values are opaque to the system and are passed directly + // to the provisioner. The only validation done on keys is that they are + // not empty. The maximum number of parameters is + // 512, with a cumulative max size of 256K + Parameters map[string]string `json:"parameters,omitempty"` +} + +// StorageClassList is a collection of storage classes. +type StorageClassList struct { + unversioned.TypeMeta `json:",inline"` + // Standard list metadata + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + unversioned.ListMeta `json:"metadata,omitempty"` + + // Items is the list of StorageClasses + Items []StorageClass `json:"items"` +} diff --git a/pkg/apis/storage/v1beta1/doc.go b/pkg/apis/storage/v1beta1/doc.go new file mode 100644 index 00000000000..13d14635deb --- /dev/null +++ b/pkg/apis/storage/v1beta1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2016 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. +*/ + +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/storage +// +groupName=storage.k8s.io +package v1beta1 // import "k8s.io/kubernetes/pkg/apis/storage/v1beta1" diff --git a/pkg/apis/storage/v1beta1/register.go b/pkg/apis/storage/v1beta1/register.go new file mode 100644 index 00000000000..e850a634d3a --- /dev/null +++ b/pkg/apis/storage/v1beta1/register.go @@ -0,0 +1,50 @@ +/* +Copyright 2015 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 v1beta1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" + versionedwatch "k8s.io/kubernetes/pkg/watch/versioned" +) + +// GroupName is the group name use in this package +const GroupName = "storage.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1beta1"} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &v1.ListOptions{}, + &v1.DeleteOptions{}, + &v1.ExportOptions{}, + + &StorageClass{}, + &StorageClassList{}, + ) + + versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/storage/v1beta1/types.go b/pkg/apis/storage/v1beta1/types.go new file mode 100644 index 00000000000..a0c4a6d6edf --- /dev/null +++ b/pkg/apis/storage/v1beta1/types.go @@ -0,0 +1,55 @@ +/* +Copyright 2016 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 v1beta1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" +) + +// +genclient=true +// +nonNamespaced=true + +// StorageClass describes the parameters for a class of storage for +// which PersistentVolumes can be dynamically provisioned. +// +// StorageClasses are non-namespaced; the name of the storage class +// according to etcd is in ObjectMeta.Name. +type StorageClass struct { + unversioned.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + v1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Provisioner indicates the type of the provisioner. + Provisioner string `json:"provisioner" protobuf:"bytes,2,opt,name=provisioner"` + + // Parameters holds the parameters for the provisioner that should + // create volumes of this storage class. + Parameters map[string]string `json:"parameters,omitempty" protobuf:"bytes,3,rep,name=parameters"` +} + +// StorageClassList is a collection of storage classes. +type StorageClassList struct { + unversioned.TypeMeta `json:",inline"` + // Standard list metadata + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + unversioned.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Items is the list of StorageClasses + Items []StorageClass `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/pkg/apis/storage/validation/validation.go b/pkg/apis/storage/validation/validation.go new file mode 100644 index 00000000000..03ee847bcf4 --- /dev/null +++ b/pkg/apis/storage/validation/validation.go @@ -0,0 +1,89 @@ +/* +Copyright 2015 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 validation + +import ( + "reflect" + "strings" + + apivalidation "k8s.io/kubernetes/pkg/api/validation" + "k8s.io/kubernetes/pkg/apis/storage" + "k8s.io/kubernetes/pkg/util/validation" + "k8s.io/kubernetes/pkg/util/validation/field" +) + +// ValidateStorageClass validates a StorageClass. +func ValidateStorageClass(storageClass *storage.StorageClass) field.ErrorList { + allErrs := apivalidation.ValidateObjectMeta(&storageClass.ObjectMeta, false, apivalidation.NameIsDNSSubdomain, field.NewPath("metadata")) + allErrs = append(allErrs, validateProvisioner(storageClass.Provisioner, field.NewPath("provisioner"))...) + allErrs = append(allErrs, validateParameters(storageClass.Parameters, field.NewPath("parameters"))...) + + return allErrs +} + +// ValidateStorageClassUpdate tests if an update to StorageClass is valid. +func ValidateStorageClassUpdate(storageClass, oldStorageClass *storage.StorageClass) field.ErrorList { + allErrs := apivalidation.ValidateObjectMetaUpdate(&storageClass.ObjectMeta, &oldStorageClass.ObjectMeta, field.NewPath("metadata")) + if !reflect.DeepEqual(oldStorageClass.Parameters, storageClass.Parameters) { + allErrs = append(allErrs, field.Forbidden(field.NewPath("parameters"), "updates to parameters are forbidden.")) + } + + if strings.Compare(storageClass.Provisioner, oldStorageClass.Provisioner) != 0 { + allErrs = append(allErrs, field.Forbidden(field.NewPath("provisioner"), "updates to provisioner are forbidden.")) + } + return allErrs +} + +// validateProvisioner tests if provisioner is a valid qualified name. +func validateProvisioner(provisioner string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if len(provisioner) == 0 { + allErrs = append(allErrs, field.Required(fldPath, provisioner)) + } + if len(provisioner) > 0 { + for _, msg := range validation.IsQualifiedName(strings.ToLower(provisioner)) { + allErrs = append(allErrs, field.Invalid(fldPath, provisioner, msg)) + } + } + return allErrs +} + +const maxProvisionerParameterSize = 256 * (1 << 10) // 256 kB +const maxProvisionerParameterLen = 512 + +// validateParameters tests that keys are qualified names and that provisionerParameter are < 256kB. +func validateParameters(params map[string]string, fldPath *field.Path) field.ErrorList { + var totalSize int64 + allErrs := field.ErrorList{} + + if len(params) > maxProvisionerParameterLen { + allErrs = append(allErrs, field.TooLong(fldPath, "Provisioner Parameters exceeded max allowed", maxProvisionerParameterLen)) + return allErrs + } + + for k, v := range params { + if len(k) < 1 { + allErrs = append(allErrs, field.Invalid(fldPath, k, "field can not be empty.")) + } + totalSize += (int64)(len(k)) + (int64)(len(v)) + } + + if totalSize > maxProvisionerParameterSize { + allErrs = append(allErrs, field.TooLong(fldPath, "", maxProvisionerParameterSize)) + } + return allErrs +} diff --git a/pkg/apis/storage/validation/validation_test.go b/pkg/apis/storage/validation/validation_test.go new file mode 100644 index 00000000000..96e0764c6a9 --- /dev/null +++ b/pkg/apis/storage/validation/validation_test.go @@ -0,0 +1,102 @@ +/* +Copyright 2016 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 validation + +import ( + "fmt" + "testing" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/storage" +) + +func TestValidateStorageClass(t *testing.T) { + successCases := []storage.StorageClass{ + { + // empty parameters + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Provisioner: "kubernetes.io/foo-provisioner", + Parameters: map[string]string{}, + }, + { + // nil parameters + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Provisioner: "kubernetes.io/foo-provisioner", + }, + { + // some parameters + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Provisioner: "kubernetes.io/foo-provisioner", + Parameters: map[string]string{ + "kubernetes.io/foo-parameter": "free/form/string", + "foo-parameter": "free-form-string", + "foo-parameter2": "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}", + }, + }, + } + + // Success cases are expected to pass validation. + for k, v := range successCases { + if errs := ValidateStorageClass(&v); len(errs) != 0 { + t.Errorf("Expected success for %d, got %v", k, errs) + } + } + + // generate a map longer than maxProvisionerParameterSize + longParameters := make(map[string]string) + totalSize := 0 + for totalSize < maxProvisionerParameterSize { + k := fmt.Sprintf("param/%d", totalSize) + v := fmt.Sprintf("value-%d", totalSize) + longParameters[k] = v + totalSize = totalSize + len(k) + len(v) + } + + errorCases := map[string]storage.StorageClass{ + "namespace is present": { + ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, + Provisioner: "kubernetes.io/foo-provisioner", + }, + "invalid provisioner": { + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Provisioner: "kubernetes.io/invalid/provisioner", + }, + "invalid empty parameter name": { + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Provisioner: "kubernetes.io/foo", + Parameters: map[string]string{ + "": "value", + }, + }, + "provisioner: Required value": { + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Provisioner: "", + }, + "too long parameters": { + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Provisioner: "kubernetes.io/foo", + Parameters: longParameters, + }, + } + + // Error cases are not expected to pass validation. + for testName, storageClass := range errorCases { + if errs := ValidateStorageClass(&storageClass); len(errs) == 0 { + t.Errorf("Expected failure for test: %s", testName) + } + } +} diff --git a/pkg/client/unversioned/adapters/internalclientset/clientset_adaption.go b/pkg/client/unversioned/adapters/internalclientset/clientset_adaption.go index 5b01b4c053d..b9b348d7777 100644 --- a/pkg/client/unversioned/adapters/internalclientset/clientset_adaption.go +++ b/pkg/client/unversioned/adapters/internalclientset/clientset_adaption.go @@ -24,6 +24,7 @@ import ( unversionedbatch "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/unversioned" unversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned" unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned" + unversionedstorage "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/storage/unversioned" "k8s.io/kubernetes/pkg/client/typed/discovery" "k8s.io/kubernetes/pkg/client/unversioned" ) @@ -68,6 +69,11 @@ func FromUnversionedClient(c *unversioned.Client) *internalclientset.Clientset { } else { clientset.DiscoveryClient = discovery.NewDiscoveryClient(nil) } + if c != nil && c.StorageClient != nil { + clientset.StorageClient = unversionedstorage.New(c.StorageClient.RESTClient) + } else { + clientset.StorageClient = unversionedstorage.New(nil) + } return &clientset } diff --git a/pkg/client/unversioned/client.go b/pkg/client/unversioned/client.go index d70c644a52e..0e23248d4d2 100644 --- a/pkg/client/unversioned/client.go +++ b/pkg/client/unversioned/client.go @@ -53,6 +53,7 @@ type Interface interface { Rbac() RbacInterface Discovery() discovery.DiscoveryInterface Certificates() CertificatesInterface + Storage() StorageInterface } func (c *Client) ReplicationControllers(namespace string) ReplicationControllerInterface { @@ -131,6 +132,7 @@ type Client struct { *RbacClient *discovery.DiscoveryClient *CertificatesClient + *StorageClient } // IsTimeout tests if this is a timeout error in the underlying transport. @@ -194,3 +196,7 @@ func (c *Client) Discovery() discovery.DiscoveryInterface { func (c *Client) Certificates() CertificatesInterface { return c.CertificatesClient } + +func (c *Client) Storage() StorageInterface { + return c.StorageClient +} diff --git a/pkg/client/unversioned/extensions.go b/pkg/client/unversioned/extensions.go index 231ef293011..f538055a0f6 100644 --- a/pkg/client/unversioned/extensions.go +++ b/pkg/client/unversioned/extensions.go @@ -35,7 +35,6 @@ type ExtensionsInterface interface { ThirdPartyResourceNamespacer ReplicaSetsNamespacer PodSecurityPoliciesInterface - StorageClassesInterface } // ExtensionsClient is used to interact with experimental Kubernetes features. @@ -81,10 +80,6 @@ func (c *ExtensionsClient) ReplicaSets(namespace string) ReplicaSetInterface { return newReplicaSets(c, namespace) } -func (c *ExtensionsClient) StorageClasses() StorageClassInterface { - return newStorageClasses(c) -} - // NewExtensions creates a new ExtensionsClient for the given config. This client // provides access to experimental Kubernetes features. // Features of Extensions group are not supported and may be changed or removed in diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index 2d5caf32207..b157e10d317 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -31,6 +31,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/typed/discovery" "k8s.io/kubernetes/pkg/util/sets" @@ -143,6 +144,15 @@ func New(c *restclient.Config) (*Client, error) { } } + var storageClient *StorageClient + if registered.IsRegistered(storage.GroupName) { + storageConfig := *c + storageClient, err = NewStorage(&storageConfig) + if err != nil { + return nil, err + } + } + return &Client{ RESTClient: client, AppsClient: appsClient, @@ -155,6 +165,7 @@ func New(c *restclient.Config) (*Client, error) { ExtensionsClient: extensionsClient, PolicyClient: policyClient, RbacClient: rbacClient, + StorageClient: storageClient, }, nil } diff --git a/pkg/client/unversioned/import_known_versions.go b/pkg/client/unversioned/import_known_versions.go index 2dee333b075..85dc5bb10d5 100644 --- a/pkg/client/unversioned/import_known_versions.go +++ b/pkg/client/unversioned/import_known_versions.go @@ -32,6 +32,7 @@ import ( _ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" + _ "k8s.io/kubernetes/pkg/apis/storage/install" ) func init() { diff --git a/pkg/client/unversioned/storage.go b/pkg/client/unversioned/storage.go new file mode 100644 index 00000000000..3b8d2b76f57 --- /dev/null +++ b/pkg/client/unversioned/storage.go @@ -0,0 +1,77 @@ +/* +Copyright 2016 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 unversioned + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/storage" + "k8s.io/kubernetes/pkg/client/restclient" +) + +type StorageInterface interface { + StorageClassesInterface +} + +// StorageClient is used to interact with Kubernetes storage features. +type StorageClient struct { + *restclient.RESTClient +} + +func (c *StorageClient) StorageClasses() StorageClassInterface { + return newStorageClasses(c) +} + +func NewStorage(c *restclient.Config) (*StorageClient, error) { + config := *c + if err := setStorageDefaults(&config); err != nil { + return nil, err + } + client, err := restclient.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &StorageClient{client}, nil +} + +func NewStorageOrDie(c *restclient.Config) *StorageClient { + client, err := NewStorage(c) + if err != nil { + panic(err) + } + return client +} + +func setStorageDefaults(config *restclient.Config) error { + // if storage group is not registered, return an error + g, err := registered.Group(storage.GroupName) + if err != nil { + return err + } + config.APIPath = defaultAPIPath + if config.UserAgent == "" { + config.UserAgent = restclient.DefaultKubernetesUserAgent() + } + // TODO: Unconditionally set the config.Version, until we fix the config. + //if config.Version == "" { + copyGroupVersion := g.GroupVersion + config.GroupVersion = ©GroupVersion + //} + + config.NegotiatedSerializer = api.Codecs + return nil +} diff --git a/pkg/client/unversioned/storageclasses.go b/pkg/client/unversioned/storageclasses.go index 7c5c0b4e72d..509e540a66a 100644 --- a/pkg/client/unversioned/storageclasses.go +++ b/pkg/client/unversioned/storageclasses.go @@ -18,7 +18,7 @@ package unversioned import ( "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/watch" ) @@ -28,25 +28,25 @@ type StorageClassesInterface interface { // StorageClassInterface has methods to work with StorageClass resources. type StorageClassInterface interface { - List(opts api.ListOptions) (*extensions.StorageClassList, error) - Get(name string) (*extensions.StorageClass, error) - Create(storageClass *extensions.StorageClass) (*extensions.StorageClass, error) - Update(storageClass *extensions.StorageClass) (*extensions.StorageClass, error) + List(opts api.ListOptions) (*storage.StorageClassList, error) + Get(name string) (*storage.StorageClass, error) + Create(storageClass *storage.StorageClass) (*storage.StorageClass, error) + Update(storageClass *storage.StorageClass) (*storage.StorageClass, error) Delete(name string) error Watch(opts api.ListOptions) (watch.Interface, error) } // storageClasses implements StorageClassInterface type storageClasses struct { - client *ExtensionsClient + client *StorageClient } -func newStorageClasses(c *ExtensionsClient) *storageClasses { +func newStorageClasses(c *StorageClient) *storageClasses { return &storageClasses{c} } -func (c *storageClasses) List(opts api.ListOptions) (result *extensions.StorageClassList, err error) { - result = &extensions.StorageClassList{} +func (c *storageClasses) List(opts api.ListOptions) (result *storage.StorageClassList, err error) { + result = &storage.StorageClassList{} err = c.client.Get(). Resource("storageclasses"). VersionedParams(&opts, api.ParameterCodec). @@ -56,20 +56,20 @@ func (c *storageClasses) List(opts api.ListOptions) (result *extensions.StorageC return result, err } -func (c *storageClasses) Get(name string) (result *extensions.StorageClass, err error) { - result = &extensions.StorageClass{} +func (c *storageClasses) Get(name string) (result *storage.StorageClass, err error) { + result = &storage.StorageClass{} err = c.client.Get().Resource("storageClasses").Name(name).Do().Into(result) return } -func (c *storageClasses) Create(storageClass *extensions.StorageClass) (result *extensions.StorageClass, err error) { - result = &extensions.StorageClass{} +func (c *storageClasses) Create(storageClass *storage.StorageClass) (result *storage.StorageClass, err error) { + result = &storage.StorageClass{} err = c.client.Post().Resource("storageClasses").Body(storageClass).Do().Into(result) return } -func (c *storageClasses) Update(storageClass *extensions.StorageClass) (result *extensions.StorageClass, err error) { - result = &extensions.StorageClass{} +func (c *storageClasses) Update(storageClass *storage.StorageClass) (result *storage.StorageClass, err error) { + result = &storage.StorageClass{} err = c.client.Put().Resource("storageClasses").Name(storageClass.Name).Body(storageClass).Do().Into(result) return } diff --git a/pkg/client/unversioned/storageclasses_test.go b/pkg/client/unversioned/storageclasses_test.go index 955de099908..27a85a4b396 100644 --- a/pkg/client/unversioned/storageclasses_test.go +++ b/pkg/client/unversioned/storageclasses_test.go @@ -21,7 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" ) @@ -33,11 +33,11 @@ func TestListStorageClasses(t *testing.T) { c := &simple.Client{ Request: simple.Request{ Method: "GET", - Path: testapi.Extensions.ResourcePath(getStorageClassResourceName(), "", ""), + Path: testapi.Storage.ResourcePath(getStorageClassResourceName(), "", ""), }, Response: simple.Response{StatusCode: 200, - Body: &extensions.StorageClassList{ - Items: []extensions.StorageClass{ + Body: &storage.StorageClassList{ + Items: []storage.StorageClass{ { ObjectMeta: api.ObjectMeta{ Name: "foo", @@ -52,16 +52,16 @@ func TestListStorageClasses(t *testing.T) { }, }, } - receivedSCList, err := c.Setup(t).Extensions().StorageClasses().List(api.ListOptions{}) + receivedSCList, err := c.Setup(t).Storage().StorageClasses().List(api.ListOptions{}) c.Validate(t, receivedSCList, err) } func TestGetStorageClass(t *testing.T) { c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Extensions.ResourcePath(getStorageClassResourceName(), "", "foo"), Query: simple.BuildQueryValues(nil)}, + Request: simple.Request{Method: "GET", Path: testapi.Storage.ResourcePath(getStorageClassResourceName(), "", "foo"), Query: simple.BuildQueryValues(nil)}, Response: simple.Response{ StatusCode: 200, - Body: &extensions.StorageClass{ + Body: &storage.StorageClass{ ObjectMeta: api.ObjectMeta{ Name: "foo", Labels: map[string]string{ @@ -73,13 +73,13 @@ func TestGetStorageClass(t *testing.T) { }, }, } - receivedSC, err := c.Setup(t).Extensions().StorageClasses().Get("foo") + receivedSC, err := c.Setup(t).Storage().StorageClasses().Get("foo") c.Validate(t, receivedSC, err) } func TestGetStorageClassWithNoName(t *testing.T) { c := &simple.Client{Error: true} - receivedSC, err := c.Setup(t).Extensions().StorageClasses().Get("") + receivedSC, err := c.Setup(t).Storage().StorageClasses().Get("") if (err != nil) && (err.Error() != simple.NameRequiredError) { t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) } @@ -88,15 +88,15 @@ func TestGetStorageClassWithNoName(t *testing.T) { } func TestUpdateStorageClass(t *testing.T) { - requestSC := &extensions.StorageClass{ + requestSC := &storage.StorageClass{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Provisioner: "aaa", } c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getStorageClassResourceName(), "", "foo"), Query: simple.BuildQueryValues(nil)}, + Request: simple.Request{Method: "PUT", Path: testapi.Storage.ResourcePath(getStorageClassResourceName(), "", "foo"), Query: simple.BuildQueryValues(nil)}, Response: simple.Response{ StatusCode: 200, - Body: &extensions.StorageClass{ + Body: &storage.StorageClass{ ObjectMeta: api.ObjectMeta{ Name: "foo", Labels: map[string]string{ @@ -108,29 +108,29 @@ func TestUpdateStorageClass(t *testing.T) { }, }, } - receivedSC, err := c.Setup(t).Extensions().StorageClasses().Update(requestSC) + receivedSC, err := c.Setup(t).Storage().StorageClasses().Update(requestSC) c.Validate(t, receivedSC, err) } func TestDeleteStorageClass(t *testing.T) { c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Extensions.ResourcePath(getStorageClassResourceName(), "", "foo"), Query: simple.BuildQueryValues(nil)}, + Request: simple.Request{Method: "DELETE", Path: testapi.Storage.ResourcePath(getStorageClassResourceName(), "", "foo"), Query: simple.BuildQueryValues(nil)}, Response: simple.Response{StatusCode: 200}, } - err := c.Setup(t).Extensions().StorageClasses().Delete("foo") + err := c.Setup(t).Storage().StorageClasses().Delete("foo") c.Validate(t, nil, err) } func TestCreateStorageClass(t *testing.T) { - requestSC := &extensions.StorageClass{ + requestSC := &storage.StorageClass{ ObjectMeta: api.ObjectMeta{Name: "foo"}, Provisioner: "aaa", } c := &simple.Client{ - Request: simple.Request{Method: "POST", Path: testapi.Extensions.ResourcePath(getStorageClassResourceName(), "", ""), Body: requestSC, Query: simple.BuildQueryValues(nil)}, + Request: simple.Request{Method: "POST", Path: testapi.Storage.ResourcePath(getStorageClassResourceName(), "", ""), Body: requestSC, Query: simple.BuildQueryValues(nil)}, Response: simple.Response{ StatusCode: 200, - Body: &extensions.StorageClass{ + Body: &storage.StorageClass{ ObjectMeta: api.ObjectMeta{ Name: "foo", Labels: map[string]string{ @@ -142,6 +142,6 @@ func TestCreateStorageClass(t *testing.T) { }, }, } - receivedSC, err := c.Setup(t).Extensions().StorageClasses().Create(requestSC) + receivedSC, err := c.Setup(t).Storage().StorageClasses().Create(requestSC) c.Validate(t, receivedSC, err) } diff --git a/pkg/client/unversioned/testclient/fake_storage_classes.go b/pkg/client/unversioned/testclient/fake_storage_classes.go index 0a6bb65f4b4..ce19dcc276e 100644 --- a/pkg/client/unversioned/testclient/fake_storage_classes.go +++ b/pkg/client/unversioned/testclient/fake_storage_classes.go @@ -18,7 +18,7 @@ package testclient import ( "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" kclientlib "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/watch" ) @@ -26,46 +26,46 @@ import ( // FakeStorageClasses implements StorageClassInterface. Meant to be embedded into a struct to get a default // implementation. This makes faking out just the method you want to test easier. type FakeStorageClasses struct { - Fake *FakeExperimental + Fake *FakeStorage } // Ensure statically that FakeStorageClasses implements StorageClassInterface. var _ kclientlib.StorageClassInterface = &FakeStorageClasses{} -func (c *FakeStorageClasses) Get(name string) (*extensions.StorageClass, error) { - obj, err := c.Fake.Invokes(NewGetAction("storageclasses", "", name), &extensions.StorageClass{}) +func (c *FakeStorageClasses) Get(name string) (*storage.StorageClass, error) { + obj, err := c.Fake.Invokes(NewGetAction("storageclasses", "", name), &storage.StorageClass{}) if obj == nil { return nil, err } - return obj.(*extensions.StorageClass), err + return obj.(*storage.StorageClass), err } -func (c *FakeStorageClasses) List(opts api.ListOptions) (*extensions.StorageClassList, error) { - obj, err := c.Fake.Invokes(NewListAction("storageclasses", "", opts), &extensions.StorageClassList{}) +func (c *FakeStorageClasses) List(opts api.ListOptions) (*storage.StorageClassList, error) { + obj, err := c.Fake.Invokes(NewListAction("storageclasses", "", opts), &storage.StorageClassList{}) if obj == nil { return nil, err } - return obj.(*extensions.StorageClassList), err + return obj.(*storage.StorageClassList), err } -func (c *FakeStorageClasses) Create(np *extensions.StorageClass) (*extensions.StorageClass, error) { - obj, err := c.Fake.Invokes(NewCreateAction("storageclasses", "", np), &extensions.StorageClass{}) +func (c *FakeStorageClasses) Create(np *storage.StorageClass) (*storage.StorageClass, error) { + obj, err := c.Fake.Invokes(NewCreateAction("storageclasses", "", np), &storage.StorageClass{}) if obj == nil { return nil, err } - return obj.(*extensions.StorageClass), err + return obj.(*storage.StorageClass), err } -func (c *FakeStorageClasses) Update(np *extensions.StorageClass) (*extensions.StorageClass, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("storageclasses", "", np), &extensions.StorageClass{}) +func (c *FakeStorageClasses) Update(np *storage.StorageClass) (*storage.StorageClass, error) { + obj, err := c.Fake.Invokes(NewUpdateAction("storageclasses", "", np), &storage.StorageClass{}) if obj == nil { return nil, err } - return obj.(*extensions.StorageClass), err + return obj.(*storage.StorageClass), err } func (c *FakeStorageClasses) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("storageclasses", "", name), &extensions.StorageClass{}) + _, err := c.Fake.Invokes(NewDeleteAction("storageclasses", "", name), &storage.StorageClass{}) return err } diff --git a/pkg/client/unversioned/testclient/simple/simple_testclient.go b/pkg/client/unversioned/testclient/simple/simple_testclient.go index 6efdf0da528..4c065c5f5f7 100644 --- a/pkg/client/unversioned/testclient/simple/simple_testclient.go +++ b/pkg/client/unversioned/testclient/simple/simple_testclient.go @@ -105,6 +105,10 @@ func (c *Client) Setup(t *testing.T) *Client { Host: c.server.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Rbac.GroupVersion()}, }) + c.StorageClient = client.NewStorageOrDie(&restclient.Config{ + Host: c.server.URL, + ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Storage.GroupVersion()}, + }) c.Clientset = clientset.NewForConfigOrDie(&restclient.Config{Host: c.server.URL}) } diff --git a/pkg/client/unversioned/testclient/testclient.go b/pkg/client/unversioned/testclient/testclient.go index 418a726b7e3..02b483118ce 100644 --- a/pkg/client/unversioned/testclient/testclient.go +++ b/pkg/client/unversioned/testclient/testclient.go @@ -317,6 +317,10 @@ func (c *Fake) Rbac() client.RbacInterface { return &FakeRbac{Fake: c} } +func (c *Fake) Storage() client.StorageInterface { + return &FakeStorage{Fake: c} +} + func (c *Fake) Authentication() client.AuthenticationInterface { return &FakeAuthentication{Fake: c} } @@ -444,10 +448,6 @@ func (c *FakeExperimental) NetworkPolicies(namespace string) client.NetworkPolic return &FakeNetworkPolicies{Fake: c, Namespace: namespace} } -func (c *FakeExperimental) StorageClasses() client.StorageClassInterface { - return &FakeStorageClasses{Fake: c} -} - func NewSimpleFakeRbac(objects ...runtime.Object) *FakeRbac { return &FakeRbac{Fake: NewSimpleFake(objects...)} } @@ -472,6 +472,18 @@ func (c *FakeRbac) ClusterRoleBindings() client.ClusterRoleBindingInterface { return &FakeClusterRoleBindings{Fake: c} } +func NewSimpleFakeStorage(objects ...runtime.Object) *FakeStorage { + return &FakeStorage{Fake: NewSimpleFake(objects...)} +} + +type FakeStorage struct { + *Fake +} + +func (c *FakeStorage) StorageClasses() client.StorageClassInterface { + return &FakeStorageClasses{Fake: c} +} + type FakeDiscovery struct { *Fake } diff --git a/pkg/controller/volume/persistentvolume/binder_test.go b/pkg/controller/volume/persistentvolume/binder_test.go index b868fe62c5f..854e4b1d042 100644 --- a/pkg/controller/volume/persistentvolume/binder_test.go +++ b/pkg/controller/volume/persistentvolume/binder_test.go @@ -20,7 +20,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" ) // Test single call to syncClaim and syncVolume methods. @@ -503,7 +503,7 @@ func TestSync(t *testing.T) { noevents, noerrors, testSyncClaim, }, } - runSyncTests(t, tests, []*extensions.StorageClass{}) + runSyncTests(t, tests, []*storage.StorageClass{}) } // Test multiple calls to syncClaim/syncVolume and periodic sync of all @@ -550,5 +550,5 @@ func TestMultiSync(t *testing.T) { }, } - runMultisyncTests(t, tests, []*extensions.StorageClass{}, "") + runMultisyncTests(t, tests, []*storage.StorageClass{}, "") } diff --git a/pkg/controller/volume/persistentvolume/controller.go b/pkg/controller/volume/persistentvolume/controller.go index 44f3edfc3ab..2231bd34902 100644 --- a/pkg/controller/volume/persistentvolume/controller.go +++ b/pkg/controller/volume/persistentvolume/controller.go @@ -23,7 +23,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/cache" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/record" @@ -1366,7 +1366,7 @@ func (ctrl *PersistentVolumeController) scheduleOperation(operationName string, } } -func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *api.PersistentVolumeClaim) (vol.ProvisionableVolumePlugin, *extensions.StorageClass, error) { +func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *api.PersistentVolumeClaim) (vol.ProvisionableVolumePlugin, *storage.StorageClass, error) { // TODO: remove this alpha behavior in 1.5 alpha := hasAnnotation(claim.ObjectMeta, annAlphaClass) beta := hasAnnotation(claim.ObjectMeta, annClass) @@ -1391,7 +1391,7 @@ func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *api.Persi if !found { return nil, nil, fmt.Errorf("StorageClass %q not found", claimClass) } - class, ok := classObj.(*extensions.StorageClass) + class, ok := classObj.(*storage.StorageClass) if !ok { return nil, nil, fmt.Errorf("Cannot convert object to StorageClass: %+v", classObj) } @@ -1407,13 +1407,13 @@ func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *api.Persi // findAlphaProvisionablePlugin returns a volume plugin compatible with // Kubernetes 1.3. // TODO: remove in Kubernetes 1.5 -func (ctrl *PersistentVolumeController) findAlphaProvisionablePlugin() (vol.ProvisionableVolumePlugin, *extensions.StorageClass, error) { +func (ctrl *PersistentVolumeController) findAlphaProvisionablePlugin() (vol.ProvisionableVolumePlugin, *storage.StorageClass, error) { if ctrl.alphaProvisioner == nil { return nil, nil, fmt.Errorf("cannot find volume plugin for alpha provisioning") } // Return a dummy StorageClass instance with no parameters - storageClass := &extensions.StorageClass{ + storageClass := &storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, diff --git a/pkg/controller/volume/persistentvolume/controller_base.go b/pkg/controller/volume/persistentvolume/controller_base.go index bc3e22f137e..93fe8326f57 100644 --- a/pkg/controller/volume/persistentvolume/controller_base.go +++ b/pkg/controller/volume/persistentvolume/controller_base.go @@ -24,7 +24,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/cache" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" unversioned_core "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned" @@ -111,10 +111,10 @@ func NewPersistentVolumeController( if classSource == nil { classSource = &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { - return kubeClient.Extensions().StorageClasses().List(options) + return kubeClient.Storage().StorageClasses().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { - return kubeClient.Extensions().StorageClasses().Watch(options) + return kubeClient.Storage().StorageClasses().Watch(options) }, } } @@ -147,7 +147,7 @@ func NewPersistentVolumeController( controller.classes = cache.NewStore(framework.DeletionHandlingMetaNamespaceKeyFunc) controller.classReflector = cache.NewReflector( classSource, - &extensions.StorageClass{}, + &storage.StorageClass{}, controller.classes, syncPeriod, ) diff --git a/pkg/controller/volume/persistentvolume/delete_test.go b/pkg/controller/volume/persistentvolume/delete_test.go index 3972588a173..0accc96522f 100644 --- a/pkg/controller/volume/persistentvolume/delete_test.go +++ b/pkg/controller/volume/persistentvolume/delete_test.go @@ -21,7 +21,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" ) // Test single call to syncVolume, expecting recycling to happen. @@ -134,7 +134,7 @@ func TestDeleteSync(t *testing.T) { wrapTestWithReclaimCalls(operationDelete, []error{nil}, testSyncVolume), }, } - runSyncTests(t, tests, []*extensions.StorageClass{}) + runSyncTests(t, tests, []*storage.StorageClass{}) } // Test multiple calls to syncClaim/syncVolume and periodic sync of all @@ -166,5 +166,5 @@ func TestDeleteMultiSync(t *testing.T) { }, } - runMultisyncTests(t, tests, []*extensions.StorageClass{}, "") + runMultisyncTests(t, tests, []*storage.StorageClass{}, "") } diff --git a/pkg/controller/volume/persistentvolume/framework_test.go b/pkg/controller/volume/persistentvolume/framework_test.go index d505eb752f9..22dc8559054 100644 --- a/pkg/controller/volume/persistentvolume/framework_test.go +++ b/pkg/controller/volume/persistentvolume/framework_test.go @@ -33,7 +33,7 @@ import ( "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/cache" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" @@ -905,7 +905,7 @@ func evaluateTestResults(ctrl *PersistentVolumeController, reactor *volumeReacto // 2. Call the tested function (syncClaim/syncVolume) via // controllerTest.testCall *once*. // 3. Compare resulting volumes and claims with expected volumes and claims. -func runSyncTests(t *testing.T, tests []controllerTest, storageClasses []*extensions.StorageClass) { +func runSyncTests(t *testing.T, tests []controllerTest, storageClasses []*storage.StorageClass) { for _, test := range tests { glog.V(4).Infof("starting test %q", test.name) @@ -961,7 +961,7 @@ func runSyncTests(t *testing.T, tests []controllerTest, storageClasses []*extens // 5. When 3. does not do any changes, finish the tests and compare final set // of volumes/claims with expected claims/volumes and report differences. // Some limit of calls in enforced to prevent endless loops. -func runMultisyncTests(t *testing.T, tests []controllerTest, storageClasses []*extensions.StorageClass, defaultStorageClass string) { +func runMultisyncTests(t *testing.T, tests []controllerTest, storageClasses []*storage.StorageClass, defaultStorageClass string) { for _, test := range tests { glog.V(4).Infof("starting multisync test %q", test.name) diff --git a/pkg/controller/volume/persistentvolume/provision_test.go b/pkg/controller/volume/persistentvolume/provision_test.go index c029d8aa145..002d1bacf72 100644 --- a/pkg/controller/volume/persistentvolume/provision_test.go +++ b/pkg/controller/volume/persistentvolume/provision_test.go @@ -22,7 +22,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" ) var class1Parameters = map[string]string{ @@ -31,7 +31,7 @@ var class1Parameters = map[string]string{ var class2Parameters = map[string]string{ "param2": "value2", } -var storageClasses = []*extensions.StorageClass{ +var storageClasses = []*storage.StorageClass{ { TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", @@ -345,7 +345,7 @@ func TestAlphaProvisionSync(t *testing.T) { noevents, noerrors, wrapTestWithProvisionCalls([]provisionCall{provisionAlphaSuccess}, testSyncClaim), }, } - runSyncTests(t, tests, []*extensions.StorageClass{}) + runSyncTests(t, tests, []*storage.StorageClass{}) } // Test multiple calls to syncClaim/syncVolume and periodic sync of all diff --git a/pkg/controller/volume/persistentvolume/recycle_test.go b/pkg/controller/volume/persistentvolume/recycle_test.go index f49a9d43ea0..4fe26061f8b 100644 --- a/pkg/controller/volume/persistentvolume/recycle_test.go +++ b/pkg/controller/volume/persistentvolume/recycle_test.go @@ -21,7 +21,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" ) // Test single call to syncVolume, expecting recycling to happen. @@ -161,7 +161,7 @@ func TestRecycleSync(t *testing.T) { []string{"Warning VolumeUnknownReclaimPolicy"}, noerrors, testSyncVolume, }, } - runSyncTests(t, tests, []*extensions.StorageClass{}) + runSyncTests(t, tests, []*storage.StorageClass{}) } // Test multiple calls to syncClaim/syncVolume and periodic sync of all @@ -193,5 +193,5 @@ func TestRecycleMultiSync(t *testing.T) { }, } - runMultisyncTests(t, tests, []*extensions.StorageClass{}, "") + runMultisyncTests(t, tests, []*storage.StorageClass{}, "") } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 9af9777dd45..4e7401f6fff 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -53,6 +53,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/typed/discovery" "k8s.io/kubernetes/pkg/client/typed/dynamic" @@ -1151,6 +1152,11 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { return errors.New("unable to validate: no rbac client") } return getSchemaAndValidate(c.c.RbacClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c) + case storage.GroupName: + if c.c.StorageClient == nil { + return errors.New("unable to validate: no storage client") + } + return getSchemaAndValidate(c.c.StorageClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c) } if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) { // Don't attempt to validate third party objects diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 32ba6c502ab..e56c7511091 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -42,6 +42,7 @@ import ( "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" @@ -2068,7 +2069,7 @@ func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, opt return nil } -func printStorageClass(sc *extensions.StorageClass, w io.Writer, options PrintOptions) error { +func printStorageClass(sc *storage.StorageClass, w io.Writer, options PrintOptions) error { name := sc.Name provtype := sc.Provisioner @@ -2085,7 +2086,7 @@ func printStorageClass(sc *extensions.StorageClass, w io.Writer, options PrintOp return nil } -func printStorageClassList(scList *extensions.StorageClassList, w io.Writer, options PrintOptions) error { +func printStorageClassList(scList *storage.StorageClassList, w io.Writer, options PrintOptions) error { for _, sc := range scList.Items { if err := printStorageClass(&sc, w, options); err != nil { return err diff --git a/pkg/master/import_known_versions.go b/pkg/master/import_known_versions.go index 926a95fdfc8..e64a5fafab7 100644 --- a/pkg/master/import_known_versions.go +++ b/pkg/master/import_known_versions.go @@ -33,6 +33,7 @@ import ( _ "k8s.io/kubernetes/pkg/apis/imagepolicy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" + _ "k8s.io/kubernetes/pkg/apis/storage/install" ) func init() { diff --git a/pkg/master/master.go b/pkg/master/master.go index 8e3d9d82551..5d0293b2b5b 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -49,6 +49,8 @@ import ( policyapiv1alpha1 "k8s.io/kubernetes/pkg/apis/policy/v1alpha1" "k8s.io/kubernetes/pkg/apis/rbac" rbacapi "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1" + "k8s.io/kubernetes/pkg/apis/storage" + storageapiv1beta1 "k8s.io/kubernetes/pkg/apis/storage/v1beta1" "k8s.io/kubernetes/pkg/apiserver" apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics" "k8s.io/kubernetes/pkg/genericapiserver" @@ -205,6 +207,7 @@ func New(c *Config) (*Master, error) { } c.RESTStorageProviders[policy.GroupName] = PolicyRESTStorageProvider{} c.RESTStorageProviders[rbac.GroupName] = RBACRESTStorageProvider{AuthorizerRBACSuperUser: c.AuthorizerRBACSuperUser} + c.RESTStorageProviders[storage.GroupName] = StorageRESTStorageProvider{} c.RESTStorageProviders[authenticationv1beta1.GroupName] = AuthenticationRESTStorageProvider{Authenticator: c.Authenticator} c.RESTStorageProviders[authorization.GroupName] = AuthorizationRESTStorageProvider{Authorizer: c.Authorizer} m.InstallAPIs(c) @@ -840,6 +843,7 @@ func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig { appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion, rbacapi.SchemeGroupVersion, + storageapiv1beta1.SchemeGroupVersion, certificatesapiv1alpha1.SchemeGroupVersion, authorizationapiv1beta1.SchemeGroupVersion, ) @@ -854,7 +858,6 @@ func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig { extensionsapiv1beta1.SchemeGroupVersion.WithResource("networkpolicies"), extensionsapiv1beta1.SchemeGroupVersion.WithResource("replicasets"), extensionsapiv1beta1.SchemeGroupVersion.WithResource("thirdpartyresources"), - extensionsapiv1beta1.SchemeGroupVersion.WithResource("storageclasses"), ) return ret diff --git a/pkg/master/storage_extensions.go b/pkg/master/storage_extensions.go index 9e651312a65..aa2e0ca80b6 100644 --- a/pkg/master/storage_extensions.go +++ b/pkg/master/storage_extensions.go @@ -35,7 +35,6 @@ import ( networkpolicyetcd "k8s.io/kubernetes/pkg/registry/networkpolicy/etcd" pspetcd "k8s.io/kubernetes/pkg/registry/podsecuritypolicy/etcd" replicasetetcd "k8s.io/kubernetes/pkg/registry/replicaset/etcd" - storageclassetcd "k8s.io/kubernetes/pkg/registry/storageclass/etcd" thirdpartyresourceetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresource/etcd" "k8s.io/kubernetes/pkg/util/wait" ) @@ -124,10 +123,6 @@ func (p ExtensionsRESTStorageProvider) v1beta1Storage(apiResourceConfigSource ge networkExtensionsStorage := networkpolicyetcd.NewREST(restOptionsGetter(extensions.Resource("networkpolicies"))) storage["networkpolicies"] = networkExtensionsStorage } - if apiResourceConfigSource.ResourceEnabled(version.WithResource("storageclasses")) { - storageClassStorage := storageclassetcd.NewREST(restOptionsGetter(extensions.Resource("storageclasses"))) - storage["storageclasses"] = storageClassStorage - } return storage } diff --git a/pkg/master/storage_storage.go b/pkg/master/storage_storage.go new file mode 100644 index 00000000000..dcddb44dabe --- /dev/null +++ b/pkg/master/storage_storage.go @@ -0,0 +1,54 @@ +/* +Copyright 2016 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 master + +import ( + "k8s.io/kubernetes/pkg/api/rest" + storageapi "k8s.io/kubernetes/pkg/apis/storage" + storageapiv1beta1 "k8s.io/kubernetes/pkg/apis/storage/v1beta1" + "k8s.io/kubernetes/pkg/genericapiserver" + storageclassetcd "k8s.io/kubernetes/pkg/registry/storageclass/etcd" +) + +type StorageRESTStorageProvider struct { +} + +var _ RESTStorageProvider = &StorageRESTStorageProvider{} + +func (p StorageRESTStorageProvider) NewRESTStorage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(storageapi.GroupName) + + if apiResourceConfigSource.AnyResourcesForVersionEnabled(storageapiv1beta1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[storageapiv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter) + apiGroupInfo.GroupMeta.GroupVersion = storageapiv1beta1.SchemeGroupVersion + } + + return apiGroupInfo, true +} + +func (p StorageRESTStorageProvider) v1beta1Storage(apiResourceConfigSource genericapiserver.APIResourceConfigSource, restOptionsGetter RESTOptionsGetter) map[string]rest.Storage { + version := storageapiv1beta1.SchemeGroupVersion + + storage := map[string]rest.Storage{} + + if apiResourceConfigSource.ResourceEnabled(version.WithResource("storageclasses")) { + storageClassStorage := storageclassetcd.NewREST(restOptionsGetter(storageapi.Resource("storageclasses"))) + storage["storageclasses"] = storageClassStorage + } + + return storage +} diff --git a/pkg/registry/storageclass/etcd/etcd.go b/pkg/registry/storageclass/etcd/etcd.go index b8467d566cf..7b26058d972 100644 --- a/pkg/registry/storageclass/etcd/etcd.go +++ b/pkg/registry/storageclass/etcd/etcd.go @@ -18,7 +18,7 @@ package etcd import ( "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" + storageapi "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/registry/generic/registry" @@ -35,11 +35,11 @@ type REST struct { func NewREST(opts generic.RESTOptions) *REST { prefix := "/" + opts.ResourcePrefix - newListFunc := func() runtime.Object { return &extensions.StorageClassList{} } + newListFunc := func() runtime.Object { return &storageapi.StorageClassList{} } storageInterface, _ := opts.Decorator( opts.StorageConfig, cachesize.GetWatchCacheSizeByResource(cachesize.StorageClasses), - &extensions.StorageClass{}, + &storageapi.StorageClass{}, prefix, storageclass.Strategy, newListFunc, @@ -47,7 +47,7 @@ func NewREST(opts generic.RESTOptions) *REST { ) store := ®istry.Store{ - NewFunc: func() runtime.Object { return &extensions.StorageClass{} }, + NewFunc: func() runtime.Object { return &storageapi.StorageClass{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return prefix @@ -56,10 +56,10 @@ func NewREST(opts generic.RESTOptions) *REST { return registry.NoNamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { - return obj.(*extensions.StorageClass).Name, nil + return obj.(*storageapi.StorageClass).Name, nil }, PredicateFunc: storageclass.MatchStorageClasses, - QualifiedResource: api.Resource("storageclasses"), + QualifiedResource: storageapi.Resource("storageclasses"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: storageclass.Strategy, diff --git a/pkg/registry/storageclass/etcd/etcd_test.go b/pkg/registry/storageclass/etcd/etcd_test.go index cc31983b872..63cb6af4c06 100644 --- a/pkg/registry/storageclass/etcd/etcd_test.go +++ b/pkg/registry/storageclass/etcd/etcd_test.go @@ -20,7 +20,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" + storageapi "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/generic" @@ -30,14 +30,14 @@ import ( ) func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) { - etcdStorage, server := registrytest.NewEtcdStorage(t, extensions.GroupName) + etcdStorage, server := registrytest.NewEtcdStorage(t, storageapi.GroupName) restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1} storageClassStorage := NewREST(restOptions) return storageClassStorage, server } -func validNewStorageClass(name string) *extensions.StorageClass { - return &extensions.StorageClass{ +func validNewStorageClass(name string) *storageapi.StorageClass { + return &storageapi.StorageClass{ ObjectMeta: api.ObjectMeta{ Name: name, }, @@ -48,7 +48,7 @@ func validNewStorageClass(name string) *extensions.StorageClass { } } -func validChangedStorageClass() *extensions.StorageClass { +func validChangedStorageClass() *storageapi.StorageClass { return validNewStorageClass("foo") } @@ -62,7 +62,7 @@ func TestCreate(t *testing.T) { // valid storageClass, // invalid - &extensions.StorageClass{ + &storageapi.StorageClass{ ObjectMeta: api.ObjectMeta{Name: "*BadName!"}, }, ) @@ -77,13 +77,13 @@ func TestUpdate(t *testing.T) { validNewStorageClass("foo"), // updateFunc func(obj runtime.Object) runtime.Object { - object := obj.(*extensions.StorageClass) + object := obj.(*storageapi.StorageClass) object.Parameters = map[string]string{"foo": "bar"} return object }, //invalid update func(obj runtime.Object) runtime.Object { - object := obj.(*extensions.StorageClass) + object := obj.(*storageapi.StorageClass) object.Parameters = map[string]string{"faz": "bar"} return object }, diff --git a/pkg/registry/storageclass/strategy.go b/pkg/registry/storageclass/strategy.go index 4dc18f88cb1..a0de3848298 100644 --- a/pkg/registry/storageclass/strategy.go +++ b/pkg/registry/storageclass/strategy.go @@ -20,8 +20,8 @@ import ( "fmt" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/apis/extensions/validation" + "k8s.io/kubernetes/pkg/apis/storage" + "k8s.io/kubernetes/pkg/apis/storage/validation" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/generic" @@ -45,11 +45,11 @@ func (storageClassStrategy) NamespaceScoped() bool { // ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation. func (storageClassStrategy) PrepareForCreate(ctx api.Context, obj runtime.Object) { - _ = obj.(*extensions.StorageClass) + _ = obj.(*storage.StorageClass) } func (storageClassStrategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList { - storageClass := obj.(*extensions.StorageClass) + storageClass := obj.(*storage.StorageClass) return validation.ValidateStorageClass(storageClass) } @@ -63,13 +63,13 @@ func (storageClassStrategy) AllowCreateOnUpdate() bool { // PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a PV func (storageClassStrategy) PrepareForUpdate(ctx api.Context, obj, old runtime.Object) { - _ = obj.(*extensions.StorageClass) - _ = old.(*extensions.StorageClass) + _ = obj.(*storage.StorageClass) + _ = old.(*storage.StorageClass) } func (storageClassStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList { - errorList := validation.ValidateStorageClass(obj.(*extensions.StorageClass)) - return append(errorList, validation.ValidateStorageClassUpdate(obj.(*extensions.StorageClass), old.(*extensions.StorageClass))...) + errorList := validation.ValidateStorageClass(obj.(*storage.StorageClass)) + return append(errorList, validation.ValidateStorageClassUpdate(obj.(*storage.StorageClass), old.(*storage.StorageClass))...) } func (storageClassStrategy) AllowUnconditionalUpdate() bool { @@ -82,7 +82,7 @@ func MatchStorageClasses(label labels.Selector, field fields.Selector) *generic. Label: label, Field: field, GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { - cls, ok := obj.(*extensions.StorageClass) + cls, ok := obj.(*storage.StorageClass) if !ok { return nil, nil, fmt.Errorf("given object is not of type StorageClass") } @@ -93,6 +93,6 @@ func MatchStorageClasses(label labels.Selector, field fields.Selector) *generic. } // StorageClassToSelectableFields returns a label set that represents the object -func StorageClassToSelectableFields(storageClass *extensions.StorageClass) fields.Set { +func StorageClassToSelectableFields(storageClass *storage.StorageClass) fields.Set { return generic.ObjectMetaFieldsSet(&storageClass.ObjectMeta, false) } diff --git a/pkg/registry/storageclass/strategy_test.go b/pkg/registry/storageclass/strategy_test.go index cc5339f6efe..2ea5b8fdd17 100644 --- a/pkg/registry/storageclass/strategy_test.go +++ b/pkg/registry/storageclass/strategy_test.go @@ -20,7 +20,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" ) func TestStorageClassStrategy(t *testing.T) { @@ -32,7 +32,7 @@ func TestStorageClassStrategy(t *testing.T) { t.Errorf("StorageClass should not allow create on update") } - storageClass := &extensions.StorageClass{ + storageClass := &storage.StorageClass{ ObjectMeta: api.ObjectMeta{ Name: "valid-class", }, @@ -49,7 +49,7 @@ func TestStorageClassStrategy(t *testing.T) { t.Errorf("unexpected error validating %v", errs) } - newStorageClass := &extensions.StorageClass{ + newStorageClass := &storage.StorageClass{ ObjectMeta: api.ObjectMeta{ Name: "valid-class-2", ResourceVersion: "4", diff --git a/plugin/pkg/admission/storageclass/default/admission.go b/plugin/pkg/admission/storageclass/default/admission.go index c6728db7df1..d4a47d06a3b 100644 --- a/plugin/pkg/admission/storageclass/default/admission.go +++ b/plugin/pkg/admission/storageclass/default/admission.go @@ -25,7 +25,7 @@ import ( admission "k8s.io/kubernetes/pkg/admission" api "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/cache" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/runtime" @@ -62,13 +62,13 @@ func newPlugin(kclient clientset.Interface) *claimDefaulterPlugin { reflector := cache.NewReflector( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { - return kclient.Extensions().StorageClasses().List(options) + return kclient.Storage().StorageClasses().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { - return kclient.Extensions().StorageClasses().Watch(options) + return kclient.Storage().StorageClasses().Watch(options) }, }, - &extensions.StorageClass{}, + &storage.StorageClass{}, store, 0, ) @@ -147,10 +147,10 @@ func (c *claimDefaulterPlugin) Admit(a admission.Attributes) error { } // getDefaultClass returns the default StorageClass from the store, or nil. -func getDefaultClass(store cache.Store) (*extensions.StorageClass, error) { - defaultClasses := []*extensions.StorageClass{} +func getDefaultClass(store cache.Store) (*storage.StorageClass, error) { + defaultClasses := []*storage.StorageClass{} for _, c := range store.List() { - class, ok := c.(*extensions.StorageClass) + class, ok := c.(*storage.StorageClass) if !ok { return nil, errors.NewInternalError(fmt.Errorf("error converting stored object to StorageClass: %v", c)) } diff --git a/plugin/pkg/admission/storageclass/default/admission_test.go b/plugin/pkg/admission/storageclass/default/admission_test.go index ecfe16cd0c7..427c7abcf67 100644 --- a/plugin/pkg/admission/storageclass/default/admission_test.go +++ b/plugin/pkg/admission/storageclass/default/admission_test.go @@ -24,12 +24,12 @@ import ( "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/conversion" ) func TestAdmission(t *testing.T) { - defaultClass1 := &extensions.StorageClass{ + defaultClass1 := &storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, @@ -41,7 +41,7 @@ func TestAdmission(t *testing.T) { }, Provisioner: "default1", } - defaultClass2 := &extensions.StorageClass{ + defaultClass2 := &storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, @@ -54,7 +54,7 @@ func TestAdmission(t *testing.T) { Provisioner: "default2", } // Class that has explicit default = false - classWithFalseDefault := &extensions.StorageClass{ + classWithFalseDefault := &storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, @@ -67,7 +67,7 @@ func TestAdmission(t *testing.T) { Provisioner: "nondefault1", } // Class with missing default annotation (=non-default) - classWithNoDefault := &extensions.StorageClass{ + classWithNoDefault := &storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, @@ -77,7 +77,7 @@ func TestAdmission(t *testing.T) { Provisioner: "nondefault1", } // Class with empty default annotation (=non-default) - classWithEmptyDefault := &extensions.StorageClass{ + classWithEmptyDefault := &storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, @@ -126,56 +126,56 @@ func TestAdmission(t *testing.T) { tests := []struct { name string - classes []*extensions.StorageClass + classes []*storage.StorageClass claim *api.PersistentVolumeClaim expectError bool expectedClassName string }{ { "no default, no modification of PVCs", - []*extensions.StorageClass{classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + []*storage.StorageClass{classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithNoClass, false, "", }, { "one default, modify PVC with class=nil", - []*extensions.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + []*storage.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithNoClass, false, "default1", }, { "one default, no modification of PVC with class=''", - []*extensions.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + []*storage.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithEmptyClass, false, "", }, { "one default, no modification of PVC with class='foo'", - []*extensions.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + []*storage.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithClass, false, "foo", }, { "two defaults, error with PVC with class=nil", - []*extensions.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + []*storage.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithNoClass, true, "", }, { "two defaults, no modification of PVC with class=''", - []*extensions.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + []*storage.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithEmptyClass, false, "", }, { "two defaults, no modification of PVC with class='foo'", - []*extensions.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + []*storage.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, claimWithClass, false, "foo", diff --git a/test/e2e/volume_provisioning.go b/test/e2e/volume_provisioning.go index 2febbe6c59a..3eb209521dd 100644 --- a/test/e2e/volume_provisioning.go +++ b/test/e2e/volume_provisioning.go @@ -22,7 +22,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/test/e2e/framework" @@ -119,8 +119,8 @@ var _ = framework.KubeDescribe("Dynamic provisioning", func() { By("creating a StorageClass") class := newStorageClass() - _, err := c.Extensions().StorageClasses().Create(class) - defer c.Extensions().StorageClasses().Delete(class.Name) + _, err := c.Storage().StorageClasses().Create(class) + defer c.Storage().StorageClasses().Delete(class.Name) Expect(err).NotTo(HaveOccurred()) By("creating a claim with a dynamic provisioning annotation") @@ -231,7 +231,7 @@ func runInPodWithVolume(c *client.Client, ns, claimName, command string) { framework.ExpectNoError(framework.WaitForPodSuccessInNamespaceSlow(c, pod.Name, pod.Spec.Containers[0].Name, pod.Namespace)) } -func newStorageClass() *extensions.StorageClass { +func newStorageClass() *storage.StorageClass { var pluginName string switch { @@ -243,7 +243,7 @@ func newStorageClass() *extensions.StorageClass { pluginName = "kubernetes.io/cinder" } - return &extensions.StorageClass{ + return &storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, diff --git a/test/integration/framework/master_utils.go b/test/integration/framework/master_utils.go index 5f4b4a53269..295a4972de2 100644 --- a/test/integration/framework/master_utils.go +++ b/test/integration/framework/master_utils.go @@ -37,6 +37,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/storage" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/record" "k8s.io/kubernetes/pkg/client/restclient" @@ -209,6 +210,10 @@ func NewMasterConfig() *master.Config { unversioned.GroupResource{Group: certificates.GroupName, Resource: genericapiserver.AllResources}, "", NewSingleContentTypeSerializer(api.Scheme, testapi.Certificates.Codec(), runtime.ContentTypeJSON)) + storageFactory.SetSerializer( + unversioned.GroupResource{Group: storage.GroupName, Resource: genericapiserver.AllResources}, + "", + NewSingleContentTypeSerializer(api.Scheme, testapi.Storage.Codec(), runtime.ContentTypeJSON)) return &master.Config{ Config: &genericapiserver.Config{ diff --git a/test/integration/persistentvolumes/persistent_volumes_test.go b/test/integration/persistentvolumes/persistent_volumes_test.go index 760b3e03ee0..4706d78df3c 100644 --- a/test/integration/persistentvolumes/persistent_volumes_test.go +++ b/test/integration/persistentvolumes/persistent_volumes_test.go @@ -31,7 +31,7 @@ import ( "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/restclient" fake_cloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake" @@ -862,9 +862,9 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) { // NOTE: This test cannot run in parallel, because it is creating and deleting // non-namespaced objects (PersistenceVolumes and StorageClasses). defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) - defer testClient.Extensions().StorageClasses().DeleteCollection(nil, api.ListOptions{}) + defer testClient.Storage().StorageClasses().DeleteCollection(nil, api.ListOptions{}) - storageClass := extensions.StorageClass{ + storageClass := storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, @@ -873,7 +873,7 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) { }, Provisioner: provisionerPluginName, } - testClient.Extensions().StorageClasses().Create(&storageClass) + testClient.Storage().StorageClasses().Create(&storageClass) stopCh := make(chan struct{}) binder.Run(stopCh) diff --git a/test/integration/storageclasses/storage_classes_test.go b/test/integration/storageclasses/storage_classes_test.go index fd7f783c44a..0bf9bf1f21c 100644 --- a/test/integration/storageclasses/storage_classes_test.go +++ b/test/integration/storageclasses/storage_classes_test.go @@ -27,7 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/restclient" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/test/integration/framework" @@ -51,7 +51,7 @@ func TestStorageClasses(t *testing.T) { // DoTestStorageClasses tests storage classes for one api version. func DoTestStorageClasses(t *testing.T, client *client.Client, ns *api.Namespace) { // Make a storage class object. - s := extensions.StorageClass{ + s := storage.StorageClass{ TypeMeta: unversioned.TypeMeta{ Kind: "StorageClass", }, @@ -61,7 +61,7 @@ func DoTestStorageClasses(t *testing.T, client *client.Client, ns *api.Namespace Provisioner: provisionerPluginName, } - if _, err := client.Extensions().StorageClasses().Create(&s); err != nil { + if _, err := client.Storage().StorageClasses().Create(&s); err != nil { t.Errorf("unable to create test storage class: %v", err) } defer deleteStorageClassOrErrorf(t, client, s.Namespace, s.Name) @@ -89,7 +89,7 @@ func DoTestStorageClasses(t *testing.T, client *client.Client, ns *api.Namespace } func deleteStorageClassOrErrorf(t *testing.T, c *client.Client, ns, name string) { - if err := c.Extensions().StorageClasses().Delete(name); err != nil { + if err := c.Storage().StorageClasses().Delete(name); err != nil { t.Errorf("unable to delete storage class %v: %v", name, err) } }