diff --git a/cmd/libs/go2idl/client-gen/main.go b/cmd/libs/go2idl/client-gen/main.go index 77ac1cf7872..f845e9b62c0 100644 --- a/cmd/libs/go2idl/client-gen/main.go +++ b/cmd/libs/go2idl/client-gen/main.go @@ -47,6 +47,7 @@ var ( "storage/", "apps/", "policy/", + "scheduling/", "settings/", "networking/", }, "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...\".") diff --git a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go index cbe4423c3e6..dcdfcfe2998 100644 --- a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go +++ b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go @@ -87,6 +87,7 @@ func New() *Generator { `k8s.io/api/rbac/v1beta1`, `k8s.io/api/certificates/v1beta1`, `k8s.io/api/imagepolicy/v1alpha1`, + `k8s.io/api/scheduling/v1alpha1`, `k8s.io/api/settings/v1alpha1`, `k8s.io/api/storage/v1beta1`, `k8s.io/api/storage/v1`, diff --git a/hack/.golint_failures b/hack/.golint_failures index 1374c7a5be0..5432f53bb32 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -149,6 +149,8 @@ pkg/apis/policy/validation pkg/apis/rbac pkg/apis/rbac/v1beta1 pkg/apis/rbac/validation +pkg/apis/scheduling +pkg/apis/scheduling/v1alpha1 pkg/apis/settings pkg/apis/settings/v1alpha1 pkg/apis/storage @@ -415,6 +417,8 @@ pkg/registry/rbac/rolebinding pkg/registry/rbac/rolebinding/policybased pkg/registry/rbac/validation pkg/registry/registrytest +pkg/registry/scheduling/priorityclass/storage +pkg/registry/scheduling/rest pkg/registry/settings/podpreset pkg/registry/settings/podpreset/storage pkg/registry/settings/rest @@ -546,6 +550,7 @@ staging/src/k8s.io/api/networking/v1 staging/src/k8s.io/api/policy/v1beta1 staging/src/k8s.io/api/rbac/v1alpha1 staging/src/k8s.io/api/rbac/v1beta1 +staging/src/k8s.io/api/scheduling/v1alpha1 staging/src/k8s.io/api/settings/v1alpha1 staging/src/k8s.io/api/storage/v1 staging/src/k8s.io/api/storage/v1beta1 @@ -739,6 +744,8 @@ staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1 staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/fake staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1 staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/fake +staging/src/k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1 +staging/src/k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1/fake staging/src/k8s.io/client-go/kubernetes/typed/settings/v1alpha1 staging/src/k8s.io/client-go/kubernetes/typed/settings/v1alpha1/fake staging/src/k8s.io/client-go/kubernetes/typed/storage/v1 diff --git a/hack/lib/init.sh b/hack/lib/init.sh index 02aba9eeddd..f787efd687b 100755 --- a/hack/lib/init.sh +++ b/hack/lib/init.sh @@ -70,9 +70,10 @@ networking.k8s.io/v1 \ policy/v1beta1 \ rbac.authorization.k8s.io/v1beta1 \ rbac.authorization.k8s.io/v1alpha1 \ +scheduling.k8s.io/v1alpha1 \ settings.k8s.io/v1alpha1 \ storage.k8s.io/v1beta1 \ -storage.k8s.io/v1\ +storage.k8s.io/v1 \ }" # not all group versions are exposed by the server. This list contains those diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index ebb42a4cb18..340a58a98db 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -48,6 +48,7 @@ import ( "k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/scheduling" "k8s.io/kubernetes/pkg/apis/settings" "k8s.io/kubernetes/pkg/apis/storage" @@ -67,6 +68,7 @@ import ( _ "k8s.io/kubernetes/pkg/apis/networking/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" + _ "k8s.io/kubernetes/pkg/apis/scheduling/install" _ "k8s.io/kubernetes/pkg/apis/settings/install" _ "k8s.io/kubernetes/pkg/apis/storage/install" ) @@ -83,6 +85,7 @@ var ( Federation TestGroup Rbac TestGroup Certificates TestGroup + Scheduling TestGroup Settings TestGroup Storage TestGroup ImagePolicy TestGroup @@ -241,6 +244,15 @@ func init() { externalTypes: api.Scheme.KnownTypes(externalGroupVersion), } } + if _, ok := Groups[scheduling.GroupName]; !ok { + externalGroupVersion := schema.GroupVersion{Group: scheduling.GroupName, Version: api.Registry.GroupOrDie(scheduling.GroupName).GroupVersion.Version} + Groups[scheduling.GroupName] = TestGroup{ + externalGroupVersion: externalGroupVersion, + internalGroupVersion: scheduling.SchemeGroupVersion, + internalTypes: api.Scheme.KnownTypes(scheduling.SchemeGroupVersion), + externalTypes: api.Scheme.KnownTypes(externalGroupVersion), + } + } if _, ok := Groups[settings.GroupName]; !ok { externalGroupVersion := schema.GroupVersion{Group: settings.GroupName, Version: api.Registry.GroupOrDie(settings.GroupName).GroupVersion.Version} Groups[settings.GroupName] = TestGroup{ @@ -323,6 +335,7 @@ func init() { Extensions = Groups[extensions.GroupName] Federation = Groups[federation.GroupName] Rbac = Groups[rbac.GroupName] + Scheduling = Groups[scheduling.GroupName] Settings = Groups[settings.GroupName] Storage = Groups[storage.GroupName] ImagePolicy = Groups[imagepolicy.GroupName] diff --git a/pkg/apis/scheduling/doc.go b/pkg/apis/scheduling/doc.go new file mode 100644 index 00000000000..782bf3fbd00 --- /dev/null +++ b/pkg/apis/scheduling/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2017 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=scheduling.k8s.io +package scheduling // import "k8s.io/kubernetes/pkg/apis/scheduling" diff --git a/pkg/apis/scheduling/install/install.go b/pkg/apis/scheduling/install/install.go new file mode 100644 index 00000000000..c5484e0abfe --- /dev/null +++ b/pkg/apis/scheduling/install/install.go @@ -0,0 +1,51 @@ +/* +Copyright 2017 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 ( + "k8s.io/apimachinery/pkg/apimachinery/announced" + "k8s.io/apimachinery/pkg/apimachinery/registered" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/scheduling" + "k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1" +) + +func init() { + Install(api.GroupFactoryRegistry, api.Registry, api.Scheme) +} + +// Install registers the API group and adds types to a scheme +func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) { + if err := announced.NewGroupMetaFactory( + &announced.GroupMetaFactoryArgs{ + GroupName: scheduling.GroupName, + VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version}, + ImportPrefix: "k8s.io/api/scheduling", + RootScopedKinds: sets.NewString("PriorityClass"), + AddInternalObjectsToScheme: scheduling.AddToScheme, + }, + announced.VersionToSchemeFunc{ + v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme, + }, + ).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil { + panic(err) + } +} diff --git a/pkg/apis/scheduling/register.go b/pkg/apis/scheduling/register.go new file mode 100644 index 00000000000..c3a611049a6 --- /dev/null +++ b/pkg/apis/scheduling/register.go @@ -0,0 +1,51 @@ +/* +Copyright 2017 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 scheduling + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "scheduling.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Kind takes an unqualified kind and returns a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &PriorityClass{}, + &PriorityClassList{}, + ) + return nil +} diff --git a/pkg/apis/scheduling/types.go b/pkg/apis/scheduling/types.go new file mode 100644 index 00000000000..62a2b96c18e --- /dev/null +++ b/pkg/apis/scheduling/types.go @@ -0,0 +1,60 @@ +/* +Copyright 2017 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 scheduling + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// +genclient=true +// +nonNamespaced=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PriorityClass defines the mapping from a priority class name to the priority +// integer value. The value can be any valid integer. +type PriorityClass struct { + metav1.TypeMeta + // Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. + // +optional + metav1.ObjectMeta + + // The value of this priority class. This is the actual priority that pods + // receive when they have the name of this class in their pod spec. + Value int32 + + // GlobalDefault specifies whether this PriorityClass should be considered as + // the default priority for pods that do not have any priority class. + // +optional + GlobalDefault bool + + // Description is an arbitrary string that usually provides guidelines on + // where this priority class should be used. + // +optional + Description string +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PriorityClassList is a collection of priority classes. +type PriorityClassList struct { + metav1.TypeMeta + // Standard list metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds + // +optional + metav1.ListMeta + + // Items is the list of PriorityClasses. + Items []PriorityClass +} diff --git a/pkg/apis/scheduling/v1alpha1/doc.go b/pkg/apis/scheduling/v1alpha1/doc.go new file mode 100644 index 00000000000..115597cc3b8 --- /dev/null +++ b/pkg/apis/scheduling/v1alpha1/doc.go @@ -0,0 +1,22 @@ +/* +Copyright 2017 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:conversion-gen=k8s.io/kubernetes/pkg/apis/scheduling +// +k8s:conversion-gen-external-types=../../../../vendor/k8s.io/api/scheduling/v1alpha1 +// +groupName=scheduling.k8s.io +// +k8s:defaulter-gen=TypeMeta +// +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/scheduling/v1alpha1 +package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1" diff --git a/pkg/apis/scheduling/v1alpha1/register.go b/pkg/apis/scheduling/v1alpha1/register.go new file mode 100644 index 00000000000..eeb90863ba6 --- /dev/null +++ b/pkg/apis/scheduling/v1alpha1/register.go @@ -0,0 +1,45 @@ +/* +Copyright 2017 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 v1alpha1 + +import ( + schedulingv1alpha1 "k8s.io/api/scheduling/v1alpha1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "scheduling.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + localSchemeBuilder = &schedulingv1alpha1.SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(RegisterDefaults) +} diff --git a/pkg/apis/scheduling/validation/validation.go b/pkg/apis/scheduling/validation/validation.go new file mode 100644 index 00000000000..0c32fe2513d --- /dev/null +++ b/pkg/apis/scheduling/validation/validation.go @@ -0,0 +1,47 @@ +/* +Copyright 2017 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 ( + "k8s.io/apimachinery/pkg/util/validation/field" + apivalidation "k8s.io/kubernetes/pkg/api/validation" + "k8s.io/kubernetes/pkg/apis/scheduling" +) + +// ValidatePriorityClassName can be used to check whether the given priority +// class name is valid. +var ValidatePriorityClassName = apivalidation.NameIsDNSSubdomain + +// ValidatePriorityClass tests whether required fields in the PriorityClass are +// set correctly. +func ValidatePriorityClass(pc *scheduling.PriorityClass) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&pc.ObjectMeta, false, ValidatePriorityClassName, field.NewPath("metadata"))...) + // The "Value" field can be any valid integer. So, no need to validate. + return allErrs +} + +// ValidatePriorityClassUpdate tests if required fields in the PriorityClass are +// set and are valid. PriorityClass does not allow updating Name, and Value. +func ValidatePriorityClassUpdate(pc, oldPc *scheduling.PriorityClass) field.ErrorList { + allErrs := apivalidation.ValidateObjectMetaUpdate(&pc.ObjectMeta, &oldPc.ObjectMeta, field.NewPath("metadata")) + // Name is immutable and is checked by the ObjectMeta validator. + if pc.Value != oldPc.Value { + allErrs = append(allErrs, field.Forbidden(field.NewPath("Value"), "may not be changed in an update.")) + } + return allErrs +} diff --git a/pkg/apis/scheduling/validation/validation_test.go b/pkg/apis/scheduling/validation/validation_test.go new file mode 100644 index 00000000000..8b0cec40b6c --- /dev/null +++ b/pkg/apis/scheduling/validation/validation_test.go @@ -0,0 +1,143 @@ +/* +Copyright 2017 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 ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/kubernetes/pkg/apis/scheduling" +) + +func TestValidatePriorityClass(t *testing.T) { + successCases := map[string]scheduling.PriorityClass{ + "no description": { + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: ""}, + Value: 100, + }, + "with description": { + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: ""}, + Value: 200, + GlobalDefault: false, + Description: "Used for the highest priority pods.", + }, + } + + for k, v := range successCases { + if errs := ValidatePriorityClass(&v); len(errs) != 0 { + t.Errorf("Expected success for %s, got %v", k, errs) + } + } + + errorCases := map[string]scheduling.PriorityClass{ + "with namespace": { + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "foo"}, + Value: 100, + }, + "invalid name": { + ObjectMeta: metav1.ObjectMeta{Name: "tier&1", Namespace: ""}, + Value: 100, + }, + } + + for k, v := range errorCases { + if errs := ValidatePriorityClass(&v); len(errs) == 0 { + t.Errorf("Expected error for %s, but it succeeded", k) + } + } +} + +func TestValidatePriorityClassUpdate(t *testing.T) { + old := scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "", ResourceVersion: "1"}, + Value: 100, + } + successCases := map[string]scheduling.PriorityClass{ + "no change": { + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "", ResourceVersion: "2"}, + Value: 100, + Description: "Used for the highest priority pods.", + }, + "change description": { + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "", ResourceVersion: "2"}, + Value: 100, + Description: "A different description.", + }, + "remove description": { + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "", ResourceVersion: "2"}, + Value: 100, + }, + "change globalDefault": { + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "", ResourceVersion: "2"}, + Value: 100, + GlobalDefault: true, + }, + } + + for k, v := range successCases { + if errs := ValidatePriorityClassUpdate(&v, &old); len(errs) != 0 { + t.Errorf("Expected success for %s, got %v", k, errs) + } + } + + errorCases := map[string]struct { + P scheduling.PriorityClass + T field.ErrorType + }{ + "add namespace": { + P: scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "foo", ResourceVersion: "2"}, + Value: 100, + }, + T: field.ErrorTypeInvalid, + }, + "change name": { + P: scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{Name: "tier2", Namespace: "", ResourceVersion: "2"}, + Value: 100, + }, + T: field.ErrorTypeInvalid, + }, + "remove value": { + P: scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "", ResourceVersion: "2"}, + }, + T: field.ErrorTypeForbidden, + }, + "change value": { + P: scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: "", ResourceVersion: "2"}, + Value: 101, + }, + T: field.ErrorTypeForbidden, + }, + } + + for k, v := range errorCases { + errs := ValidatePriorityClassUpdate(&v.P, &old) + if len(errs) == 0 { + t.Errorf("Expected error for %s, but it succeeded", k) + continue + } + for i := range errs { + if errs[i].Type != v.T { + t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i]) + } + } + } +} diff --git a/pkg/client/clientset_generated/clientset/import_known_versions.go b/pkg/client/clientset_generated/clientset/import_known_versions.go index 061c2af6d59..b02bb9da1fe 100644 --- a/pkg/client/clientset_generated/clientset/import_known_versions.go +++ b/pkg/client/clientset_generated/clientset/import_known_versions.go @@ -31,6 +31,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/scheduling/install" _ "k8s.io/kubernetes/pkg/apis/settings/install" _ "k8s.io/kubernetes/pkg/apis/storage/install" ) diff --git a/pkg/master/import_known_versions.go b/pkg/master/import_known_versions.go index 5176dba5638..93388ad9b0f 100644 --- a/pkg/master/import_known_versions.go +++ b/pkg/master/import_known_versions.go @@ -36,6 +36,7 @@ import ( _ "k8s.io/kubernetes/pkg/apis/networking/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" + _ "k8s.io/kubernetes/pkg/apis/scheduling/install" _ "k8s.io/kubernetes/pkg/apis/settings/install" _ "k8s.io/kubernetes/pkg/apis/storage/install" ) diff --git a/pkg/master/master.go b/pkg/master/master.go index 012339a95fe..b0cdb979f55 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -38,6 +38,7 @@ import ( policyapiv1beta1 "k8s.io/api/policy/v1beta1" rbacv1alpha1 "k8s.io/api/rbac/v1alpha1" rbacv1beta1 "k8s.io/api/rbac/v1beta1" + schedulingapiv1alpha1 "k8s.io/api/scheduling/v1alpha1" settingv1alpha1 "k8s.io/api/settings/v1alpha1" storageapiv1 "k8s.io/api/storage/v1" storageapiv1beta1 "k8s.io/api/storage/v1beta1" @@ -73,6 +74,7 @@ import ( networkingrest "k8s.io/kubernetes/pkg/registry/networking/rest" policyrest "k8s.io/kubernetes/pkg/registry/policy/rest" rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest" + schedulingrest "k8s.io/kubernetes/pkg/registry/scheduling/rest" settingsrest "k8s.io/kubernetes/pkg/registry/settings/rest" storagerest "k8s.io/kubernetes/pkg/registry/storage/rest" ) @@ -260,6 +262,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) networkingrest.RESTStorageProvider{}, policyrest.RESTStorageProvider{}, rbacrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorizer}, + schedulingrest.RESTStorageProvider{}, settingsrest.RESTStorageProvider{}, storagerest.RESTStorageProvider{}, // keep apps after extensions so legacy clients resolve the extensions versions of shared resource names. @@ -394,6 +397,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { // TODO: disable rbac/v1alpha1 and settings/v1alpha1 by default in 1.8 rbacv1alpha1.SchemeGroupVersion, settingv1alpha1.SchemeGroupVersion, + schedulingapiv1alpha1.SchemeGroupVersion, storageapiv1.SchemeGroupVersion, storageapiv1beta1.SchemeGroupVersion, certificatesapiv1beta1.SchemeGroupVersion, diff --git a/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index e2e2d0f9c49..00d70f01cb8 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -65,6 +65,7 @@ import ( "k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/scheduling" "k8s.io/kubernetes/pkg/apis/storage" storageutil "k8s.io/kubernetes/pkg/apis/storage/util" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" @@ -133,6 +134,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer api.Kind("Namespace"): &NamespaceDescriber{c}, api.Kind("Endpoints"): &EndpointsDescriber{c}, api.Kind("ConfigMap"): &ConfigMapDescriber{c}, + api.Kind("PriorityClass"): &PriorityClassDescriber{c}, extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c}, extensions.Kind("NetworkPolicy"): &ExtensionsNetworkPolicyDescriber{c}, @@ -152,6 +154,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer rbac.Kind("RoleBinding"): &RoleBindingDescriber{c}, rbac.Kind("ClusterRoleBinding"): &ClusterRoleBindingDescriber{c}, networking.Kind("NetworkPolicy"): &NetworkPolicyDescriber{c}, + scheduling.Kind("PriorityClass"): &PriorityClassDescriber{c}, } return m @@ -3097,6 +3100,42 @@ func describePodDisruptionBudget(pdb *policy.PodDisruptionBudget, events *api.Ev }) } +// PriorityClassDescriber generates information about a PriorityClass. +type PriorityClassDescriber struct { + clientset.Interface +} + +func (s *PriorityClassDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { + pc, err := s.Scheduling().PriorityClasses().Get(name, metav1.GetOptions{}) + if err != nil { + return "", err + } + + var events *api.EventList + if describerSettings.ShowEvents { + events, _ = s.Core().Events(namespace).Search(api.Scheme, pc) + } + + return describePriorityClass(pc, events) +} + +func describePriorityClass(pc *scheduling.PriorityClass, events *api.EventList) (string, error) { + return tabbedString(func(out io.Writer) error { + w := NewPrefixWriter(out) + w.Write(LEVEL_0, "Name:\t%s\n", pc.Name) + w.Write(LEVEL_0, "Value:\t%s\n", pc.Value) + w.Write(LEVEL_0, "GlobalDefault:\t%s\n", pc.GlobalDefault) + w.Write(LEVEL_0, "Description:\t%s\n", pc.Description) + + w.Write(LEVEL_0, "Annotations:\t%s\n", labels.FormatLabels(pc.Annotations)) + if events != nil { + DescribeEvents(events, w) + } + + return nil + }) +} + // newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types. func newErrNoDescriber(types ...reflect.Type) error { names := make([]string, 0, len(types)) diff --git a/pkg/registry/cachesize/cachesize.go b/pkg/registry/cachesize/cachesize.go index b853cb5e6a8..cea62fe5f4d 100644 --- a/pkg/registry/cachesize/cachesize.go +++ b/pkg/registry/cachesize/cachesize.go @@ -51,6 +51,7 @@ const ( Pods Resource = "pods" PodSecurityPolicies Resource = "podsecuritypolicies" PodTemplates Resource = "podtemplates" + PriorityClasses Resource = "priorityclasses" Replicasets Resource = "replicasets" ResourceQuotas Resource = "resourcequotas" CronJobs Resource = "cronjobs" diff --git a/pkg/registry/scheduling/priorityclass/doc.go b/pkg/registry/scheduling/priorityclass/doc.go new file mode 100644 index 00000000000..b4359aed6c2 --- /dev/null +++ b/pkg/registry/scheduling/priorityclass/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2017 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 priorityclass // import "k8s.io/kubernetes/pkg/registry/scheduling/priorityclass" diff --git a/pkg/registry/scheduling/priorityclass/registry.go b/pkg/registry/scheduling/priorityclass/registry.go new file mode 100644 index 00000000000..53e2ade1974 --- /dev/null +++ b/pkg/registry/scheduling/priorityclass/registry.go @@ -0,0 +1,84 @@ +/* +Copyright 2017 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 priorityclass + +import ( + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/scheduling" +) + +// Registry is an interface for things that know how to store PriorityClass. +type Registry interface { + ListPriorityClasses(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*scheduling.PriorityClassList, error) + CreatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass) error + UpdatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass) error + GetPriorityClass(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*scheduling.PriorityClass, error) + DeletePriorityClass(ctx genericapirequest.Context, name string) error + WatchPriorityClasses(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) +} + +// storage puts strong typing around storage calls +type storage struct { + rest.StandardStorage +} + +// NewRegistry returns a new Registry interface for the given Storage. Any mismatched +// types will panic. +func NewRegistry(s rest.StandardStorage) Registry { + return &storage{s} +} + +func (s *storage) ListPriorityClasses(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*scheduling.PriorityClassList, error) { + obj, err := s.List(ctx, options) + if err != nil { + return nil, err + } + + return obj.(*scheduling.PriorityClassList), nil +} + +func (s *storage) CreatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass) error { + _, err := s.Create(ctx, pc, false) + return err +} + +func (s *storage) UpdatePriorityClass(ctx genericapirequest.Context, pc *scheduling.PriorityClass) error { + _, _, err := s.Update(ctx, pc.Name, rest.DefaultUpdatedObjectInfo(pc, api.Scheme)) + return err +} + +func (s *storage) WatchPriorityClasses(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { + return s.Watch(ctx, options) +} + +func (s *storage) GetPriorityClass(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*scheduling.PriorityClass, error) { + obj, err := s.Get(ctx, name, options) + if err != nil { + return nil, err + } + return obj.(*scheduling.PriorityClass), nil +} + +func (s *storage) DeletePriorityClass(ctx genericapirequest.Context, name string) error { + _, _, err := s.Delete(ctx, name, nil) + return err +} diff --git a/pkg/registry/scheduling/priorityclass/storage/storage.go b/pkg/registry/scheduling/priorityclass/storage/storage.go new file mode 100644 index 00000000000..e24a319f680 --- /dev/null +++ b/pkg/registry/scheduling/priorityclass/storage/storage.go @@ -0,0 +1,63 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package storage + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/generic" + genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" + "k8s.io/apiserver/pkg/registry/rest" + "k8s.io/kubernetes/pkg/api" + schedulingapi "k8s.io/kubernetes/pkg/apis/scheduling" + "k8s.io/kubernetes/pkg/registry/cachesize" + "k8s.io/kubernetes/pkg/registry/scheduling/priorityclass" +) + +// rest implements a RESTStorage for priority classes against etcd +type REST struct { + *genericregistry.Store +} + +// NewREST returns a RESTStorage object that will work against priority classes. +func NewREST(optsGetter generic.RESTOptionsGetter) *REST { + store := &genericregistry.Store{ + Copier: api.Scheme, + NewFunc: func() runtime.Object { return &schedulingapi.PriorityClass{} }, + NewListFunc: func() runtime.Object { return &schedulingapi.PriorityClassList{} }, + PredicateFunc: priorityclass.Matcher, + QualifiedResource: schedulingapi.Resource("priorityclasses"), + WatchCacheSize: cachesize.GetWatchCacheSizeByResource("priorityclasses"), + + CreateStrategy: priorityclass.Strategy, + UpdateStrategy: priorityclass.Strategy, + DeleteStrategy: priorityclass.Strategy, + } + options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: priorityclass.GetAttrs} + if err := store.CompleteWithOptions(options); err != nil { + panic(err) // TODO: Propagate error up + } + + return &REST{store} +} + +// Implement ShortNamesProvider +var _ rest.ShortNamesProvider = &REST{} + +// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource. +func (r *REST) ShortNames() []string { + return []string{"pc"} +} diff --git a/pkg/registry/scheduling/priorityclass/storage/storage_test.go b/pkg/registry/scheduling/priorityclass/storage/storage_test.go new file mode 100644 index 00000000000..75a99f690a5 --- /dev/null +++ b/pkg/registry/scheduling/priorityclass/storage/storage_test.go @@ -0,0 +1,153 @@ +/* +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 ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/generic" + etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing" + "k8s.io/kubernetes/pkg/apis/scheduling" + "k8s.io/kubernetes/pkg/registry/registrytest" +) + +func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) { + etcdStorage, server := registrytest.NewEtcdStorage(t, scheduling.GroupName) + restOptions := generic.RESTOptions{ + StorageConfig: etcdStorage, + Decorator: generic.UndecoratedStorage, + DeleteCollectionWorkers: 1, + ResourcePrefix: "priorityclasses", + } + return NewREST(restOptions), server +} + +func validNewPriorityClass() *scheduling.PriorityClass { + return &scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Value: 100, + GlobalDefault: false, + Description: "This is created only for testing.", + } +} + +func TestCreate(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := registrytest.New(t, storage.Store).ClusterScope() + test.TestCreate( + validNewPriorityClass(), + // invalid cases + &scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "*badName", + }, + Value: 100, + GlobalDefault: true, + Description: "This is created only for testing.", + }, + ) +} + +func TestUpdate(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := registrytest.New(t, storage.Store).ClusterScope() + test.TestUpdate( + // valid + validNewPriorityClass(), + // There is no valid update function + func(obj runtime.Object) runtime.Object { + pc := obj.(*scheduling.PriorityClass) + pc.Value = 100 + pc.GlobalDefault = false + return pc + }, + // invalid updates + // Change Value + func(obj runtime.Object) runtime.Object { + pc := obj.(*scheduling.PriorityClass) + pc.Value = 200 + pc.GlobalDefault = false + return pc + }, + ) +} + +func TestDelete(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := registrytest.New(t, storage.Store).ClusterScope() + test.TestDelete(validNewPriorityClass()) +} + +func TestGet(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := registrytest.New(t, storage.Store).ClusterScope() + test.TestGet(validNewPriorityClass()) +} + +func TestList(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := registrytest.New(t, storage.Store).ClusterScope() + test.TestList(validNewPriorityClass()) +} + +func TestWatch(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := registrytest.New(t, storage.Store).ClusterScope() + test.TestWatch( + validNewPriorityClass(), + // matching labels + []labels.Set{}, + // not matching labels + []labels.Set{ + {"foo": "bar"}, + }, + // matching fields + []fields.Set{ + {"metadata.name": "foo"}, + }, + // not matching fields + []fields.Set{ + {"metadata.name": "bar"}, + }, + ) +} + +func TestShortNames(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := []string{"pc"} + registrytest.AssertShortNames(t, storage, expected) +} diff --git a/pkg/registry/scheduling/priorityclass/strategy.go b/pkg/registry/scheduling/priorityclass/strategy.go new file mode 100644 index 00000000000..64be7365f81 --- /dev/null +++ b/pkg/registry/scheduling/priorityclass/strategy.go @@ -0,0 +1,109 @@ +/* +Copyright 2017 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 priorityclass + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/generic" + apistorage "k8s.io/apiserver/pkg/storage" + "k8s.io/apiserver/pkg/storage/names" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/scheduling" + "k8s.io/kubernetes/pkg/apis/scheduling/validation" +) + +// priorityClassStrategy implements verification logic for PriorityClass. +type priorityClassStrategy struct { + runtime.ObjectTyper + names.NameGenerator +} + +// Strategy is the default logic that applies when creating and updating PriorityClass objects. +var Strategy = priorityClassStrategy{api.Scheme, names.SimpleNameGenerator} + +// NamespaceScoped returns true because all PriorityClasses are global. +func (priorityClassStrategy) NamespaceScoped() bool { + return false +} + +// PrepareForCreate clears the status of a PriorityClass before creation. +func (priorityClassStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) { + pc := obj.(*scheduling.PriorityClass) + pc.Generation = 1 +} + +// PrepareForUpdate clears fields that are not allowed to be set by end users on update. +func (priorityClassStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) { + _ = obj.(*scheduling.PriorityClass) + _ = old.(*scheduling.PriorityClass) +} + +// Validate validates a new PriorityClass. +func (priorityClassStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList { + pc := obj.(*scheduling.PriorityClass) + return validation.ValidatePriorityClass(pc) +} + +// Canonicalize normalizes the object after validation. +func (priorityClassStrategy) Canonicalize(obj runtime.Object) {} + +// AllowCreateOnUpdate is false for PriorityClass; this means POST is needed to create one. +func (priorityClassStrategy) AllowCreateOnUpdate() bool { + return false +} + +// ValidateUpdate is the default update validation for an end user. +func (priorityClassStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { + validationErrorList := validation.ValidatePriorityClass(obj.(*scheduling.PriorityClass)) + updateErrorList := validation.ValidatePriorityClassUpdate(obj.(*scheduling.PriorityClass), old.(*scheduling.PriorityClass)) + return append(validationErrorList, updateErrorList...) +} + +// AllowUnconditionalUpdate is the default update policy for PriorityClass objects. +func (priorityClassStrategy) AllowUnconditionalUpdate() bool { + return true +} + +// SelectableFields returns a field set that represents the object. +func SelectableFields(pc *scheduling.PriorityClass) fields.Set { + return generic.ObjectMetaFieldsSet(&pc.ObjectMeta, false) +} + +// GetAttrs returns labels and fields of a given object for filtering purposes. +func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { + pc, ok := obj.(*scheduling.PriorityClass) + if !ok { + return nil, nil, false, fmt.Errorf("given object is not a PriorityClass") + } + return labels.Set(pc.ObjectMeta.Labels), SelectableFields(pc), pc.Initializers != nil, nil +} + +// Matcher is the filter used by the generic etcd backend to watch events +// from etcd to clients of the apiserver only interested in specific labels/fields. +func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { + return apistorage.SelectionPredicate{ + Label: label, + Field: field, + GetAttrs: GetAttrs, + } +} diff --git a/pkg/registry/scheduling/priorityclass/strategy_test.go b/pkg/registry/scheduling/priorityclass/strategy_test.go new file mode 100644 index 00000000000..95ff53f0192 --- /dev/null +++ b/pkg/registry/scheduling/priorityclass/strategy_test.go @@ -0,0 +1,64 @@ +/* +Copyright 2017 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 priorityclass + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/kubernetes/pkg/apis/scheduling" +) + +func TestPriorityClassStrategy(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + if Strategy.NamespaceScoped() { + t.Errorf("PriorityClass must not be namespace scoped") + } + if Strategy.AllowCreateOnUpdate() { + t.Errorf("PriorityClass should not allow create on update") + } + + priorityClass := &scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-class", + }, + Value: 10, + } + + Strategy.PrepareForCreate(ctx, priorityClass) + + errs := Strategy.Validate(ctx, priorityClass) + if len(errs) != 0 { + t.Errorf("unexpected error validating %v", errs) + } + + newPriorityClass := &scheduling.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-class-2", + ResourceVersion: "4", + }, + Value: 20, + } + + Strategy.PrepareForUpdate(ctx, newPriorityClass, priorityClass) + + errs = Strategy.ValidateUpdate(ctx, newPriorityClass, priorityClass) + if len(errs) == 0 { + t.Errorf("Expected a validation error") + } +} diff --git a/pkg/registry/scheduling/rest/storage_scheduling.go b/pkg/registry/scheduling/rest/storage_scheduling.go new file mode 100644 index 00000000000..49418a645ba --- /dev/null +++ b/pkg/registry/scheduling/rest/storage_scheduling.go @@ -0,0 +1,56 @@ +/* +Copyright 2017 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 rest + +import ( + "k8s.io/apiserver/pkg/registry/generic" + "k8s.io/apiserver/pkg/registry/rest" + genericapiserver "k8s.io/apiserver/pkg/server" + serverstorage "k8s.io/apiserver/pkg/server/storage" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/scheduling" + schedulingapiv1alpha1 "k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1" + priorityclassstore "k8s.io/kubernetes/pkg/registry/scheduling/priorityclass/storage" +) + +type RESTStorageProvider struct{} + +func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(scheduling.GroupName, api.Registry, api.Scheme, api.ParameterCodec, api.Codecs) + + if apiResourceConfigSource.AnyResourcesForVersionEnabled(schedulingapiv1alpha1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[schedulingapiv1alpha1.SchemeGroupVersion.Version] = p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter) + apiGroupInfo.GroupMeta.GroupVersion = schedulingapiv1alpha1.SchemeGroupVersion + } + + return apiGroupInfo, true +} + +func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage { + version := schedulingapiv1alpha1.SchemeGroupVersion + + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("priorityclasses")) { + priorityClassStorage := priorityclassstore.NewREST(restOptionsGetter) + storage["priorityclasses"] = priorityClassStorage + } + return storage +} + +func (p RESTStorageProvider) GroupName() string { + return scheduling.GroupName +} diff --git a/staging/src/k8s.io/api/scheduling/v1alpha1/doc.go b/staging/src/k8s.io/api/scheduling/v1alpha1/doc.go new file mode 100644 index 00000000000..bf9015986c9 --- /dev/null +++ b/staging/src/k8s.io/api/scheduling/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2017 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:openapi-gen=true + +// +groupName=scheduling.k8s.io +package v1alpha1 // import "k8s.io/api/scheduling/v1alpha1" diff --git a/staging/src/k8s.io/api/scheduling/v1alpha1/register.go b/staging/src/k8s.io/api/scheduling/v1alpha1/register.go new file mode 100644 index 00000000000..91ce6e0cc3a --- /dev/null +++ b/staging/src/k8s.io/api/scheduling/v1alpha1/register.go @@ -0,0 +1,52 @@ +/* +Copyright 2017 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "scheduling.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. + // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &PriorityClass{}, + &PriorityClassList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/staging/src/k8s.io/api/scheduling/v1alpha1/types.go b/staging/src/k8s.io/api/scheduling/v1alpha1/types.go new file mode 100644 index 00000000000..2443bb36ea6 --- /dev/null +++ b/staging/src/k8s.io/api/scheduling/v1alpha1/types.go @@ -0,0 +1,63 @@ +/* +Copyright 2017 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient=true +// +nonNamespaced=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PriorityClass defines mapping from a priority class name to the priority +// integer value. The value can be any valid integer. +type PriorityClass struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // The value of this priority class. This is the actual priority that pods + // receive when they have the name of this class in their pod spec. + Value int32 `json:"value" protobuf:"bytes,2,opt,name=value"` + + // GlobalDefault specifies whether this PriorityClass should be considered as + // the default priority for pods that do not have any priority class. + // +optional + GlobalDefault bool `json:"globalDefault,omitempty" protobuf:"bytes,3,opt,name=globalDefault"` + + // Description is an arbitrary string that usually provides guidelines on + // where this priority class should be used. + // +optional + Description string `json:"description,omitempty" protobuf:"bytes,4,opt,name=description"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PriorityClassList is a collection of priority classes. +type PriorityClassList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Items is the list of StorageClasses + Items []PriorityClass `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/test/integration/etcd/etcd_storage_path_test.go b/test/integration/etcd/etcd_storage_path_test.go index b229b071699..d23eb55111e 100644 --- a/test/integration/etcd/etcd_storage_path_test.go +++ b/test/integration/etcd/etcd_storage_path_test.go @@ -303,6 +303,14 @@ var etcdStorageData = map[schema.GroupVersionResource]struct { stub: `{"metadata":{"name":"hook1","creationTimestamp":null},"externalAdmissionHooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"","name":""},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`, expectedEtcdPath: "/registry/externaladmissionhookconfigurations/hook1", }, + // -- + + // k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1 + gvr("scheduling.k8s.io", "v1alpha1", "priorityclasses"): { + stub: `{"metadata":{"name":"pc1"},"Value":1000}`, + expectedEtcdPath: "/registry/priorityclasses/pc1", + }, + // -- } // Be very careful when whitelisting an object as ephemeral.