move StorageClass to its own group

This commit is contained in:
deads2k 2016-09-01 11:29:26 -04:00
parent 008fc22d31
commit cd5b6cc491
57 changed files with 936 additions and 370 deletions

View File

@ -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\"")

View File

@ -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",
}

View File

@ -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"
},

View File

@ -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

View File

@ -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:-}

View File

@ -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"

View File

@ -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}

View File

@ -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]
}

View File

@ -76,8 +76,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&PodSecurityPolicyList{},
&NetworkPolicy{},
&NetworkPolicyList{},
&StorageClass{},
&StorageClassList{},
)
return nil
}

View File

@ -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"`
}

View File

@ -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)

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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)
}
}
}

19
pkg/apis/storage/doc.go Normal file
View File

@ -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"

View File

@ -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)
}
}
}
}

View File

@ -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
}

60
pkg/apis/storage/types.go Normal file
View File

@ -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"`
}

View File

@ -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"

View File

@ -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
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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() {

View File

@ -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 = &copyGroupVersion
//}
config.NegotiatedSerializer = api.Codecs
return nil
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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})
}

View File

@ -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
}

View File

@ -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{}, "")
}

View File

@ -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",
},

View File

@ -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,
)

View File

@ -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{}, "")
}

View File

@ -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)

View File

@ -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

View File

@ -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{}, "")
}

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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 := &registry.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,

View File

@ -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
},

View File

@ -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)
}

View File

@ -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",

View File

@ -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))
}

View File

@ -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",

View File

@ -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",
},

View File

@ -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{

View File

@ -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)

View File

@ -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)
}
}