diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index 3293e99d0c..3124da38aa 100644 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -3354,6 +3354,17 @@ runTests() { ## test if a short name is exported during discovery kube::test::if_has_string "${output_message}" '{"name":"configmaps","singularName":"","namespaced":true,"kind":"ConfigMap","verbs":\["create","delete","deletecollection","get","list","patch","update","watch"\],"shortNames":\["cm"\]}' + ######################### + # Assert categories # + ######################### + + ## test if a category is exported during discovery + if kube::test::if_supports_resource "${pods}" ; then + kube::log::status "Testing propagation of categories for resources" + output_message=$(kubectl get --raw=/api/v1 | grep -Po '"name":"pods".*?}') + kube::test::if_has_string "${output_message}" '"categories":\["all"\]' + fi + ########################### # POD creation / deletion # ########################### diff --git a/pkg/registry/apps/statefulset/storage/storage.go b/pkg/registry/apps/statefulset/storage/storage.go index 8e60c9e088..fc6d417c7f 100644 --- a/pkg/registry/apps/statefulset/storage/storage.go +++ b/pkg/registry/apps/statefulset/storage/storage.go @@ -58,6 +58,14 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { return &REST{store}, &StatusREST{store: &statusStore} } +// Implement CategoriesProvider +var _ rest.CategoriesProvider = &REST{} + +// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. +func (r *REST) Categories() []string { + return []string{"all"} +} + // StatusREST implements the REST endpoint for changing the status of an statefulSet type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/apps/statefulset/storage/storage_test.go b/pkg/registry/apps/statefulset/storage/storage_test.go index e7d5ac830f..949a208dfa 100644 --- a/pkg/registry/apps/statefulset/storage/storage_test.go +++ b/pkg/registry/apps/statefulset/storage/storage_test.go @@ -187,4 +187,12 @@ func TestWatch(t *testing.T) { ) } +func TestCategories(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := []string{"all"} + registrytest.AssertCategories(t, storage, expected) +} + // TODO: Test generation number. diff --git a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go index 7a56e2d8c7..33280d8223 100644 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go +++ b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go @@ -65,7 +65,15 @@ func (r *REST) ShortNames() []string { return []string{"hpa"} } -// StatusREST implements the REST endpoint for changing the status of a daemonset +// Implement CategoriesProvider +var _ rest.CategoriesProvider = &REST{} + +// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. +func (r *REST) Categories() []string { + return []string{"all"} +} + +/// StatusREST implements the REST endpoint for changing the status of a daemonset type StatusREST struct { store *genericregistry.Store } diff --git a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage_test.go b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage_test.go index a3af118378..7a19981642 100644 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage_test.go +++ b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage_test.go @@ -157,4 +157,12 @@ func TestShortNames(t *testing.T) { registrytest.AssertShortNames(t, storage, expected) } +func TestCategories(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := []string{"all"} + registrytest.AssertCategories(t, storage, expected) +} + // TODO TestUpdateStatus diff --git a/pkg/registry/batch/job/storage/storage.go b/pkg/registry/batch/job/storage/storage.go index ca172c9c8c..912c86e482 100644 --- a/pkg/registry/batch/job/storage/storage.go +++ b/pkg/registry/batch/job/storage/storage.go @@ -74,6 +74,14 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { return &REST{store}, &StatusREST{store: &statusStore} } +// Implement CategoriesProvider +var _ rest.CategoriesProvider = &REST{} + +// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. +func (r *REST) Categories() []string { + return []string{"all"} +} + // StatusREST implements the REST endpoint for changing the status of a resourcequota. type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/batch/job/storage/storage_test.go b/pkg/registry/batch/job/storage/storage_test.go index 2e73e8b59b..963977eb25 100644 --- a/pkg/registry/batch/job/storage/storage_test.go +++ b/pkg/registry/batch/job/storage/storage_test.go @@ -182,3 +182,11 @@ func newBool(val bool) *bool { *p = val return p } + +func TestCategories(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Job.Store.DestroyFunc() + expected := []string{"all"} + registrytest.AssertCategories(t, storage.Job, expected) +} diff --git a/pkg/registry/core/pod/storage/storage.go b/pkg/registry/core/pod/storage/storage.go index 0e1a7d61be..ca58827d83 100644 --- a/pkg/registry/core/pod/storage/storage.go +++ b/pkg/registry/core/pod/storage/storage.go @@ -117,6 +117,14 @@ func (r *REST) ShortNames() []string { return []string{"po"} } +// Implement CategoriesProvider +var _ rest.CategoriesProvider = &REST{} + +// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. +func (r *REST) Categories() []string { + return []string{"all"} +} + // BindingREST implements the REST endpoint for binding pods to nodes when etcd is in use. type BindingREST struct { store *genericregistry.Store diff --git a/pkg/registry/core/pod/storage/storage_test.go b/pkg/registry/core/pod/storage/storage_test.go index d096a1ce4b..72e9ab88aa 100644 --- a/pkg/registry/core/pod/storage/storage_test.go +++ b/pkg/registry/core/pod/storage/storage_test.go @@ -888,3 +888,11 @@ func TestShortNames(t *testing.T) { expected := []string{"po"} registrytest.AssertShortNames(t, storage, expected) } + +func TestCategories(t *testing.T) { + storage, _, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := []string{"all"} + registrytest.AssertCategories(t, storage, expected) +} diff --git a/pkg/registry/core/replicationcontroller/storage/storage.go b/pkg/registry/core/replicationcontroller/storage/storage.go index 1d8417e091..54b84f4246 100644 --- a/pkg/registry/core/replicationcontroller/storage/storage.go +++ b/pkg/registry/core/replicationcontroller/storage/storage.go @@ -91,6 +91,14 @@ func (r *REST) ShortNames() []string { return []string{"rc"} } +// Implement CategoriesProvider +var _ rest.CategoriesProvider = &REST{} + +// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. +func (r *REST) Categories() []string { + return []string{"all"} +} + // StatusREST implements the REST endpoint for changing the status of a replication controller type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/core/replicationcontroller/storage/storage_test.go b/pkg/registry/core/replicationcontroller/storage/storage_test.go index 258676fdc4..2fb6797f18 100644 --- a/pkg/registry/core/replicationcontroller/storage/storage_test.go +++ b/pkg/registry/core/replicationcontroller/storage/storage_test.go @@ -335,3 +335,11 @@ func TestShortNames(t *testing.T) { expected := []string{"rc"} registrytest.AssertShortNames(t, storage.Controller, expected) } + +func TestCategories(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Controller.Store.DestroyFunc() + expected := []string{"all"} + registrytest.AssertCategories(t, storage.Controller, expected) +} diff --git a/pkg/registry/core/service/storage/storage.go b/pkg/registry/core/service/storage/storage.go index 9b26465129..4551ba3971 100644 --- a/pkg/registry/core/service/storage/storage.go +++ b/pkg/registry/core/service/storage/storage.go @@ -65,6 +65,14 @@ func (r *REST) ShortNames() []string { return []string{"svc"} } +// Implement CategoriesProvider +var _ rest.CategoriesProvider = &REST{} + +// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. +func (r *REST) Categories() []string { + return []string{"all"} +} + // StatusREST implements the REST endpoint for changing the status of a service. type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/core/service/storage/storage_test.go b/pkg/registry/core/service/storage/storage_test.go index 903eeda98e..9e2fab55e8 100644 --- a/pkg/registry/core/service/storage/storage_test.go +++ b/pkg/registry/core/service/storage/storage_test.go @@ -175,3 +175,11 @@ func TestShortNames(t *testing.T) { expected := []string{"svc"} registrytest.AssertShortNames(t, storage, expected) } + +func TestCategories(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := []string{"all"} + registrytest.AssertCategories(t, storage, expected) +} diff --git a/pkg/registry/extensions/deployment/storage/storage.go b/pkg/registry/extensions/deployment/storage/storage.go index 701ccf1494..55fa90b6cb 100644 --- a/pkg/registry/extensions/deployment/storage/storage.go +++ b/pkg/registry/extensions/deployment/storage/storage.go @@ -92,6 +92,14 @@ func (r *REST) ShortNames() []string { return []string{"deploy"} } +// Implement CategoriesProvider +var _ rest.CategoriesProvider = &REST{} + +// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. +func (r *REST) Categories() []string { + return []string{"all"} +} + // StatusREST implements the REST endpoint for changing the status of a deployment type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/extensions/deployment/storage/storage_test.go b/pkg/registry/extensions/deployment/storage/storage_test.go index b3c14f69f5..07c84d58c2 100644 --- a/pkg/registry/extensions/deployment/storage/storage_test.go +++ b/pkg/registry/extensions/deployment/storage/storage_test.go @@ -396,3 +396,11 @@ func TestShortNames(t *testing.T) { expected := []string{"deploy"} registrytest.AssertShortNames(t, storage.Deployment, expected) } + +func TestCategories(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Deployment.Store.DestroyFunc() + expected := []string{"all"} + registrytest.AssertCategories(t, storage.Deployment, expected) +} diff --git a/pkg/registry/extensions/replicaset/storage/storage.go b/pkg/registry/extensions/replicaset/storage/storage.go index a936fffca2..78e0ecc656 100644 --- a/pkg/registry/extensions/replicaset/storage/storage.go +++ b/pkg/registry/extensions/replicaset/storage/storage.go @@ -90,6 +90,14 @@ func (r *REST) ShortNames() []string { return []string{"rs"} } +// Implement CategoriesProvider +var _ rest.CategoriesProvider = &REST{} + +// Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. +func (r *REST) Categories() []string { + return []string{"all"} +} + // StatusREST implements the REST endpoint for changing the status of a ReplicaSet type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/extensions/replicaset/storage/storage_test.go b/pkg/registry/extensions/replicaset/storage/storage_test.go index fc89666d5a..ff2d8a50c6 100644 --- a/pkg/registry/extensions/replicaset/storage/storage_test.go +++ b/pkg/registry/extensions/replicaset/storage/storage_test.go @@ -376,3 +376,11 @@ func TestShortNames(t *testing.T) { expected := []string{"rs"} registrytest.AssertShortNames(t, storage.ReplicaSet, expected) } + +func TestCategories(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.ReplicaSet.Store.DestroyFunc() + expected := []string{"all"} + registrytest.AssertCategories(t, storage.ReplicaSet, expected) +} diff --git a/pkg/registry/registrytest/BUILD b/pkg/registry/registrytest/BUILD index 9716908440..b7c88e5770 100644 --- a/pkg/registry/registrytest/BUILD +++ b/pkg/registry/registrytest/BUILD @@ -13,6 +13,7 @@ go_library( "doc.go", "endpoint.go", "etcd.go", + "groupNamesProvider.go", "node.go", "service.go", "shortNamesProvider.go", diff --git a/pkg/registry/registrytest/categoriesProvider.go b/pkg/registry/registrytest/categoriesProvider.go new file mode 100644 index 0000000000..7bec6359b7 --- /dev/null +++ b/pkg/registry/registrytest/categoriesProvider.go @@ -0,0 +1,32 @@ +/* +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 registrytest + +import ( + "reflect" + "testing" + + "k8s.io/apiserver/pkg/registry/rest" +) + +func AssertCategories(t *testing.T, storage rest.CategoriesProvider, expected []string) { + actual := storage.Categories() + ok := reflect.DeepEqual(actual, expected) + if !ok { + t.Errorf("categories are not equal. expected = %v actual = %v", expected, actual) + } +} diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go b/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go index eef3b64c62..5a39a6ae1c 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go @@ -379,6 +379,12 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag tableProvider = rest.DefaultTableConvertor } + var categories []string + categoriesProvider, ok := storage.(rest.CategoriesProvider) + if ok { + categories = categoriesProvider.Categories() + } + var apiResource metav1.APIResource // Get the list of actions for the given scope. switch scope.Name() { @@ -825,6 +831,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag } sort.Strings(apiResource.Verbs) apiResource.ShortNames = shortNames + apiResource.Categories = categories return &apiResource, nil } diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go index 4421ffcac3..07871c5bb3 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go @@ -71,6 +71,12 @@ type ShortNamesProvider interface { ShortNames() []string } +// CategoriesProvider allows a resource to specify which groups of resources (categories) it's part of. Categories can +// be used by API clients to refer to a batch of resources by using a single name (e.g. "all" could translate to "pod,rc,svc,..."). +type CategoriesProvider interface { + Categories() []string +} + // Lister is an object that can retrieve resources that match the provided field and label criteria. type Lister interface { // NewList returns an empty object that can be used with the List call.