diff --git a/pkg/registry/apps/daemonset/storage/storage.go b/pkg/registry/apps/daemonset/storage/storage.go index 84df2450283..c40ac6fc36d 100644 --- a/pkg/registry/apps/daemonset/storage/storage.go +++ b/pkg/registry/apps/daemonset/storage/storage.go @@ -78,6 +78,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "daemonset" +} + // StatusREST implements the REST endpoint for changing the status of a daemonset type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/apps/daemonset/storage/storage_test.go b/pkg/registry/apps/daemonset/storage/storage_test.go index eaf15ffcdae..fcd06c180dd 100644 --- a/pkg/registry/apps/daemonset/storage/storage_test.go +++ b/pkg/registry/apps/daemonset/storage/storage_test.go @@ -197,4 +197,12 @@ func TestShortNames(t *testing.T) { registrytest.AssertShortNames(t, storage, expected) } +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "daemonset" + registrytest.AssertSingularName(t, storage, expected) +} + // TODO TestUpdateStatus diff --git a/pkg/registry/apps/deployment/storage/storage.go b/pkg/registry/apps/deployment/storage/storage.go index 8b05dd0b663..3da212d0974 100644 --- a/pkg/registry/apps/deployment/storage/storage.go +++ b/pkg/registry/apps/deployment/storage/storage.go @@ -130,6 +130,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "deployment" +} + // StatusREST implements the REST endpoint for changing the status of a deployment type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/apps/deployment/storage/storage_test.go b/pkg/registry/apps/deployment/storage/storage_test.go index d7b371040d2..05b14b0b45e 100644 --- a/pkg/registry/apps/deployment/storage/storage_test.go +++ b/pkg/registry/apps/deployment/storage/storage_test.go @@ -455,6 +455,14 @@ func TestCategories(t *testing.T) { registrytest.AssertCategories(t, storage.Deployment, expected) } +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Deployment.Store.DestroyFunc() + expected := "deployment" + registrytest.AssertSingularName(t, storage.Deployment, expected) +} + func TestScalePatchErrors(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) diff --git a/pkg/registry/apps/replicaset/storage/storage.go b/pkg/registry/apps/replicaset/storage/storage.go index 0c467d37327..97592c1d770 100644 --- a/pkg/registry/apps/replicaset/storage/storage.go +++ b/pkg/registry/apps/replicaset/storage/storage.go @@ -126,6 +126,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "replicaset" +} + // StatusREST implements the REST endpoint for changing the status of a ReplicaSet type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/apps/replicaset/storage/storage_test.go b/pkg/registry/apps/replicaset/storage/storage_test.go index 392c1f95b21..d6223bbe849 100644 --- a/pkg/registry/apps/replicaset/storage/storage_test.go +++ b/pkg/registry/apps/replicaset/storage/storage_test.go @@ -399,6 +399,14 @@ func TestCategories(t *testing.T) { registrytest.AssertCategories(t, storage.ReplicaSet, expected) } +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.ReplicaSet.Store.DestroyFunc() + expected := "replicaset" + registrytest.AssertSingularName(t, storage.ReplicaSet, expected) +} + func TestScalePatchErrors(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) diff --git a/pkg/registry/apps/statefulset/storage/storage.go b/pkg/registry/apps/statefulset/storage/storage.go index 47558534cdd..cf834362f9f 100644 --- a/pkg/registry/apps/statefulset/storage/storage.go +++ b/pkg/registry/apps/statefulset/storage/storage.go @@ -114,6 +114,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "statefulset" +} + // 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 cdce909bd77..649ffffed15 100644 --- a/pkg/registry/apps/statefulset/storage/storage_test.go +++ b/pkg/registry/apps/statefulset/storage/storage_test.go @@ -199,6 +199,14 @@ func TestCategories(t *testing.T) { registrytest.AssertCategories(t, storage.StatefulSet, expected) } +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.StatefulSet.Store.DestroyFunc() + expected := "statefulset" + registrytest.AssertSingularName(t, storage.StatefulSet, expected) +} + func TestShortNames(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) diff --git a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go index 03171ed1ff0..44a7911eea3 100644 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go +++ b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go @@ -78,6 +78,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "horizontalpodautoscaler" +} + // 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 589932db37f..bf269222cd9 100644 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage_test.go +++ b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage_test.go @@ -176,6 +176,14 @@ func TestCategories(t *testing.T) { registrytest.AssertCategories(t, storage, expected) } +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "horizontalpodautoscaler" + registrytest.AssertSingularName(t, storage, expected) +} + func TestUpdateStatus(t *testing.T) { storage, statusStorage, server := newStorage(t) defer server.Terminate(t) diff --git a/pkg/registry/batch/cronjob/storage/storage.go b/pkg/registry/batch/cronjob/storage/storage.go index 637dc8f3754..51d453bfe74 100644 --- a/pkg/registry/batch/cronjob/storage/storage.go +++ b/pkg/registry/batch/cronjob/storage/storage.go @@ -76,6 +76,13 @@ func (r *REST) ShortNames() []string { return []string{"cj"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "cronjob" +} + // 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.go b/pkg/registry/batch/job/storage/storage.go index f6cfa679536..93c6eb8b606 100644 --- a/pkg/registry/batch/job/storage/storage.go +++ b/pkg/registry/batch/job/storage/storage.go @@ -96,6 +96,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "job" +} + func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { //nolint:staticcheck // SA1019 backwards compatibility //nolint: staticcheck diff --git a/pkg/registry/batch/job/storage/storage_test.go b/pkg/registry/batch/job/storage/storage_test.go index 765b3179509..5b9b991f61c 100644 --- a/pkg/registry/batch/job/storage/storage_test.go +++ b/pkg/registry/batch/job/storage/storage_test.go @@ -375,3 +375,11 @@ func TestCategories(t *testing.T) { expected := []string{"all"} registrytest.AssertCategories(t, storage.Job, expected) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Job.Store.DestroyFunc() + expected := "job" + registrytest.AssertSingularName(t, storage.Job, expected) +} diff --git a/pkg/registry/certificates/certificates/storage/storage.go b/pkg/registry/certificates/certificates/storage/storage.go index 8de7828efde..5c72cc2be68 100644 --- a/pkg/registry/certificates/certificates/storage/storage.go +++ b/pkg/registry/certificates/certificates/storage/storage.go @@ -79,6 +79,13 @@ func (r *REST) ShortNames() []string { return []string{"csr"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "certificatesigningrequest" +} + // StatusREST implements the REST endpoint for changing the status of a CSR. type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/coordination/lease/storage/storage.go b/pkg/registry/coordination/lease/storage/storage.go index 8989a293a6d..a0f67bc2ff4 100644 --- a/pkg/registry/coordination/lease/storage/storage.go +++ b/pkg/registry/coordination/lease/storage/storage.go @@ -20,6 +20,7 @@ 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" coordinationapi "k8s.io/kubernetes/pkg/apis/coordination" "k8s.io/kubernetes/pkg/printers" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" @@ -52,3 +53,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "lease" +} diff --git a/pkg/registry/core/configmap/storage/storage.go b/pkg/registry/core/configmap/storage/storage.go index 8180c56e830..47648ded842 100644 --- a/pkg/registry/core/configmap/storage/storage.go +++ b/pkg/registry/core/configmap/storage/storage.go @@ -66,3 +66,10 @@ var _ rest.ShortNamesProvider = &REST{} func (r *REST) ShortNames() []string { return []string{"cm"} } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "configmap" +} diff --git a/pkg/registry/core/configmap/storage/storage_test.go b/pkg/registry/core/configmap/storage/storage_test.go index 04ccc8d8999..78b927d4586 100644 --- a/pkg/registry/core/configmap/storage/storage_test.go +++ b/pkg/registry/core/configmap/storage/storage_test.go @@ -172,3 +172,11 @@ func TestShortNames(t *testing.T) { expected := []string{"cm"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "configmap" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/endpoint/storage/storage.go b/pkg/registry/core/endpoint/storage/storage.go index ff082994cea..e0f36f29e73 100644 --- a/pkg/registry/core/endpoint/storage/storage.go +++ b/pkg/registry/core/endpoint/storage/storage.go @@ -60,3 +60,10 @@ var _ rest.ShortNamesProvider = &REST{} func (r *REST) ShortNames() []string { return []string{"ep"} } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "endpoint" +} diff --git a/pkg/registry/core/event/storage/storage.go b/pkg/registry/core/event/storage/storage.go index ee4498934d1..7be63d0ce60 100644 --- a/pkg/registry/core/event/storage/storage.go +++ b/pkg/registry/core/event/storage/storage.go @@ -64,3 +64,10 @@ var _ rest.ShortNamesProvider = &REST{} func (r *REST) ShortNames() []string { return []string{"ev"} } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "event" +} diff --git a/pkg/registry/core/event/storage/storage_test.go b/pkg/registry/core/event/storage/storage_test.go index 134de72b8bf..854c7776050 100644 --- a/pkg/registry/core/event/storage/storage_test.go +++ b/pkg/registry/core/event/storage/storage_test.go @@ -123,3 +123,11 @@ func TestShortNames(t *testing.T) { expected := []string{"ev"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "event" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/limitrange/storage/storage.go b/pkg/registry/core/limitrange/storage/storage.go index b3fd401f701..b286f7f9230 100644 --- a/pkg/registry/core/limitrange/storage/storage.go +++ b/pkg/registry/core/limitrange/storage/storage.go @@ -58,3 +58,10 @@ var _ rest.ShortNamesProvider = &REST{} func (r *REST) ShortNames() []string { return []string{"limits"} } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "limitrange" +} diff --git a/pkg/registry/core/limitrange/storage/storage_test.go b/pkg/registry/core/limitrange/storage/storage_test.go index 3bbcad7f5e3..38b16a3b7d0 100644 --- a/pkg/registry/core/limitrange/storage/storage_test.go +++ b/pkg/registry/core/limitrange/storage/storage_test.go @@ -171,3 +171,11 @@ func TestShortNames(t *testing.T) { expected := []string{"limits"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "limitrange" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/namespace/storage/storage.go b/pkg/registry/core/namespace/storage/storage.go index 37b130092ca..3201ca45498 100644 --- a/pkg/registry/core/namespace/storage/storage.go +++ b/pkg/registry/core/namespace/storage/storage.go @@ -291,6 +291,13 @@ func (r *REST) ShortNames() []string { return []string{"ns"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "namespace" +} + var _ rest.StorageVersionProvider = &REST{} func (r *REST) StorageVersion() runtime.GroupVersioner { diff --git a/pkg/registry/core/namespace/storage/storage_test.go b/pkg/registry/core/namespace/storage/storage_test.go index a1bfd9e9af8..e94c9b3fb33 100644 --- a/pkg/registry/core/namespace/storage/storage_test.go +++ b/pkg/registry/core/namespace/storage/storage_test.go @@ -628,3 +628,11 @@ func TestShortNames(t *testing.T) { expected := []string{"ns"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.store.DestroyFunc() + expected := "namespace" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/node/storage/storage.go b/pkg/registry/core/node/storage/storage.go index 56e806770f4..e8ecf8b9fe4 100644 --- a/pkg/registry/core/node/storage/storage.go +++ b/pkg/registry/core/node/storage/storage.go @@ -167,7 +167,17 @@ func (r *REST) ResourceLocation(ctx context.Context, id string) (*url.URL, http. return node.ResourceLocation(r, r.connection, r.proxyTransport, ctx, id) } +// 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{"no"} } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "node" +} diff --git a/pkg/registry/core/persistentvolume/storage/storage.go b/pkg/registry/core/persistentvolume/storage/storage.go index df65eabc879..06bd7beeb4c 100644 --- a/pkg/registry/core/persistentvolume/storage/storage.go +++ b/pkg/registry/core/persistentvolume/storage/storage.go @@ -73,6 +73,13 @@ func (r *REST) ShortNames() []string { return []string{"pv"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "persistentvolume" +} + // StatusREST implements the REST endpoint for changing the status of a persistentvolume. type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/core/persistentvolume/storage/storage_test.go b/pkg/registry/core/persistentvolume/storage/storage_test.go index c03fa3248a9..5c8a7b5bdce 100644 --- a/pkg/registry/core/persistentvolume/storage/storage_test.go +++ b/pkg/registry/core/persistentvolume/storage/storage_test.go @@ -208,3 +208,11 @@ func TestShortNames(t *testing.T) { expected := []string{"pv"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "persistentvolume" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/persistentvolumeclaim/storage/storage.go b/pkg/registry/core/persistentvolumeclaim/storage/storage.go index d472bbd8ef3..26e6d80c867 100644 --- a/pkg/registry/core/persistentvolumeclaim/storage/storage.go +++ b/pkg/registry/core/persistentvolumeclaim/storage/storage.go @@ -77,6 +77,13 @@ func (r *REST) ShortNames() []string { return []string{"pvc"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "persistentvolumeclaim" +} + // defaultOnRead sets interlinked fields that were not previously set on read. // We can't do this in the normal defaulting path because that same logic // applies on Get, Create, and Update, but we need to distinguish between them. diff --git a/pkg/registry/core/persistentvolumeclaim/storage/storage_test.go b/pkg/registry/core/persistentvolumeclaim/storage/storage_test.go index 54c0ca625f5..d94fed0fcfe 100644 --- a/pkg/registry/core/persistentvolumeclaim/storage/storage_test.go +++ b/pkg/registry/core/persistentvolumeclaim/storage/storage_test.go @@ -212,6 +212,14 @@ func TestShortNames(t *testing.T) { registrytest.AssertShortNames(t, storage, expected) } +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "persistentvolumeclaim" + registrytest.AssertSingularName(t, storage, expected) +} + func TestDefaultOnReadPvc(t *testing.T) { storage, _, server := newStorage(t) defer server.Terminate(t) diff --git a/pkg/registry/core/pod/storage/storage.go b/pkg/registry/core/pod/storage/storage.go index 3960918de62..034884960d9 100644 --- a/pkg/registry/core/pod/storage/storage.go +++ b/pkg/registry/core/pod/storage/storage.go @@ -142,6 +142,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "pod" +} + // 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 1e15ca8a834..dc7160078c4 100644 --- a/pkg/registry/core/pod/storage/storage_test.go +++ b/pkg/registry/core/pod/storage/storage_test.go @@ -1274,3 +1274,11 @@ func TestCategories(t *testing.T) { expected := []string{"all"} registrytest.AssertCategories(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, _, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "pod" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/podtemplate/storage/storage.go b/pkg/registry/core/podtemplate/storage/storage.go index d7dca23868a..66d267d6777 100644 --- a/pkg/registry/core/podtemplate/storage/storage.go +++ b/pkg/registry/core/podtemplate/storage/storage.go @@ -20,6 +20,7 @@ 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" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/printers" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" @@ -53,3 +54,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { } return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "podtemplate" +} diff --git a/pkg/registry/core/podtemplate/storage/storage_test.go b/pkg/registry/core/podtemplate/storage/storage_test.go index 785fc6fcb62..59c3413f902 100644 --- a/pkg/registry/core/podtemplate/storage/storage_test.go +++ b/pkg/registry/core/podtemplate/storage/storage_test.go @@ -153,3 +153,11 @@ func TestWatch(t *testing.T) { }, ) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "podtemplate" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/replicationcontroller/storage/storage.go b/pkg/registry/core/replicationcontroller/storage/storage.go index 39a59f4a73e..d5fca9c0f96 100644 --- a/pkg/registry/core/replicationcontroller/storage/storage.go +++ b/pkg/registry/core/replicationcontroller/storage/storage.go @@ -122,6 +122,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "replicationcontroller" +} + // 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 009fed33b1a..60e133cb26a 100644 --- a/pkg/registry/core/replicationcontroller/storage/storage_test.go +++ b/pkg/registry/core/replicationcontroller/storage/storage_test.go @@ -352,6 +352,14 @@ func TestCategories(t *testing.T) { registrytest.AssertCategories(t, storage.Controller, expected) } +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Controller.Store.DestroyFunc() + expected := "replicationcontroller" + registrytest.AssertSingularName(t, storage.Controller, expected) +} + func TestScalePatchErrors(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) diff --git a/pkg/registry/core/resourcequota/storage/storage.go b/pkg/registry/core/resourcequota/storage/storage.go index d438ec792e2..76b71dd2096 100644 --- a/pkg/registry/core/resourcequota/storage/storage.go +++ b/pkg/registry/core/resourcequota/storage/storage.go @@ -72,6 +72,13 @@ func (r *REST) ShortNames() []string { return []string{"quota"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "resourcequota" +} + // StatusREST implements the REST endpoint for changing the status of a resourcequota. type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/core/resourcequota/storage/storage_test.go b/pkg/registry/core/resourcequota/storage/storage_test.go index 9b750553f83..8459c32bf7f 100644 --- a/pkg/registry/core/resourcequota/storage/storage_test.go +++ b/pkg/registry/core/resourcequota/storage/storage_test.go @@ -217,3 +217,11 @@ func TestShortNames(t *testing.T) { expected := []string{"quota"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "resourcequota" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/secret/storage/storage.go b/pkg/registry/core/secret/storage/storage.go index 81e2892df57..b02cb83546c 100644 --- a/pkg/registry/core/secret/storage/storage.go +++ b/pkg/registry/core/secret/storage/storage.go @@ -20,6 +20,7 @@ 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/apiserver/pkg/storage" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/printers" @@ -57,3 +58,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { } return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "secret" +} diff --git a/pkg/registry/core/secret/storage/storage_test.go b/pkg/registry/core/secret/storage/storage_test.go index a3850a2dafe..68bc7dcb5fb 100644 --- a/pkg/registry/core/secret/storage/storage_test.go +++ b/pkg/registry/core/secret/storage/storage_test.go @@ -143,3 +143,11 @@ func TestWatch(t *testing.T) { }, ) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "secret" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/core/service/storage/storage.go b/pkg/registry/core/service/storage/storage.go index 4ee5428e819..172135ed88e 100644 --- a/pkg/registry/core/service/storage/storage.go +++ b/pkg/registry/core/service/storage/storage.go @@ -153,6 +153,13 @@ func (r *REST) Categories() []string { return []string{"all"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "service" +} + // Destroy cleans up everything on shutdown. func (r *REST) Destroy() { r.Store.Destroy() diff --git a/pkg/registry/core/service/storage/storage_test.go b/pkg/registry/core/service/storage/storage_test.go index 1345ed519fa..1cb514132df 100644 --- a/pkg/registry/core/service/storage/storage_test.go +++ b/pkg/registry/core/service/storage/storage_test.go @@ -279,6 +279,14 @@ func TestGenericCategories(t *testing.T) { registrytest.AssertCategories(t, storage, expected) } +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t, []api.IPFamily{api.IPv4Protocol}) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "service" + registrytest.AssertSingularName(t, storage, expected) +} + // // Tests of internal functions // diff --git a/pkg/registry/core/serviceaccount/storage/storage.go b/pkg/registry/core/serviceaccount/storage/storage.go index 60f83b74133..d44573bf607 100644 --- a/pkg/registry/core/serviceaccount/storage/storage.go +++ b/pkg/registry/core/serviceaccount/storage/storage.go @@ -84,3 +84,10 @@ var _ rest.ShortNamesProvider = &REST{} func (r *REST) ShortNames() []string { return []string{"sa"} } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "serviceaccount" +} diff --git a/pkg/registry/core/serviceaccount/storage/storage_test.go b/pkg/registry/core/serviceaccount/storage/storage_test.go index 220f5053047..dfaaa30b315 100644 --- a/pkg/registry/core/serviceaccount/storage/storage_test.go +++ b/pkg/registry/core/serviceaccount/storage/storage_test.go @@ -146,3 +146,11 @@ func TestShortNames(t *testing.T) { expected := []string{"sa"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "serviceaccount" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/discovery/endpointslice/storage/storage.go b/pkg/registry/discovery/endpointslice/storage/storage.go index 756553ad580..d8b4d1b9113 100644 --- a/pkg/registry/discovery/endpointslice/storage/storage.go +++ b/pkg/registry/discovery/endpointslice/storage/storage.go @@ -20,6 +20,7 @@ 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/apis/discovery" "k8s.io/kubernetes/pkg/printers" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" @@ -51,3 +52,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { } return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "endpointslice" +} diff --git a/pkg/registry/networking/ingress/storage/storage.go b/pkg/registry/networking/ingress/storage/storage.go index 71ab37c455e..dd82551cb1d 100644 --- a/pkg/registry/networking/ingress/storage/storage.go +++ b/pkg/registry/networking/ingress/storage/storage.go @@ -70,6 +70,13 @@ func (r *REST) ShortNames() []string { return []string{"ing"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "ingress" +} + // StatusREST implements the REST endpoint for changing the status of an ingress type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/networking/ingress/storage/storage_test.go b/pkg/registry/networking/ingress/storage/storage_test.go index 28c42ea1c48..199ea5002e9 100644 --- a/pkg/registry/networking/ingress/storage/storage_test.go +++ b/pkg/registry/networking/ingress/storage/storage_test.go @@ -250,4 +250,12 @@ func TestShortNames(t *testing.T) { registrytest.AssertShortNames(t, storage, expected) } +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "ingress" + registrytest.AssertSingularName(t, storage, expected) +} + // TODO TestUpdateStatus diff --git a/pkg/registry/networking/ingressclass/storage/storage.go b/pkg/registry/networking/ingressclass/storage/storage.go index 187c7317cba..889b7398011 100644 --- a/pkg/registry/networking/ingressclass/storage/storage.go +++ b/pkg/registry/networking/ingressclass/storage/storage.go @@ -20,6 +20,7 @@ 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/apis/networking" "k8s.io/kubernetes/pkg/printers" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" @@ -52,3 +53,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "ingressclass" +} diff --git a/pkg/registry/networking/networkpolicy/storage/storage.go b/pkg/registry/networking/networkpolicy/storage/storage.go index 0101077b662..8f2ed82fc16 100644 --- a/pkg/registry/networking/networkpolicy/storage/storage.go +++ b/pkg/registry/networking/networkpolicy/storage/storage.go @@ -72,6 +72,13 @@ func (r *REST) ShortNames() []string { return []string{"netpol"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "networkpolicy" +} + // StatusREST implements the REST endpoint for changing the status of an ingress type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/networking/networkpolicy/storage/storage_test.go b/pkg/registry/networking/networkpolicy/storage/storage_test.go index 15ab8780910..57685615099 100644 --- a/pkg/registry/networking/networkpolicy/storage/storage_test.go +++ b/pkg/registry/networking/networkpolicy/storage/storage_test.go @@ -198,6 +198,14 @@ func TestShortNames(t *testing.T) { registrytest.AssertShortNames(t, storage, expected) } +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "networkpolicy" + registrytest.AssertSingularName(t, storage, expected) +} + func TestStatusUpdate(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NetworkPolicyStatus, true)() storage, statusStorage, server := newStorage(t) diff --git a/pkg/registry/policy/poddisruptionbudget/storage/storage.go b/pkg/registry/policy/poddisruptionbudget/storage/storage.go index 4af2ea2429f..55fb2e360f6 100644 --- a/pkg/registry/policy/poddisruptionbudget/storage/storage.go +++ b/pkg/registry/policy/poddisruptionbudget/storage/storage.go @@ -67,6 +67,13 @@ func (r *REST) ShortNames() []string { return []string{"pdb"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "poddisruptionbudget" +} + // StatusREST implements the REST endpoint for changing the status of an podDisruptionBudget. type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/rbac/clusterrole/storage/storage.go b/pkg/registry/rbac/clusterrole/storage/storage.go index 59347f767a4..aa04dd376b4 100644 --- a/pkg/registry/rbac/clusterrole/storage/storage.go +++ b/pkg/registry/rbac/clusterrole/storage/storage.go @@ -51,3 +51,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "clusterrole" +} diff --git a/pkg/registry/rbac/clusterrolebinding/storage/storage.go b/pkg/registry/rbac/clusterrolebinding/storage/storage.go index 762730788b6..7d662fd726a 100644 --- a/pkg/registry/rbac/clusterrolebinding/storage/storage.go +++ b/pkg/registry/rbac/clusterrolebinding/storage/storage.go @@ -20,6 +20,7 @@ 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/apis/rbac" "k8s.io/kubernetes/pkg/printers" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" @@ -52,3 +53,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "clusterrolebinding" +} diff --git a/pkg/registry/rbac/role/storage/storage.go b/pkg/registry/rbac/role/storage/storage.go index 6e6700ede98..1eb312c12bf 100644 --- a/pkg/registry/rbac/role/storage/storage.go +++ b/pkg/registry/rbac/role/storage/storage.go @@ -51,3 +51,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "role" +} diff --git a/pkg/registry/rbac/rolebinding/storage/storage.go b/pkg/registry/rbac/rolebinding/storage/storage.go index 0cbcfa0f644..8a6ee171a3d 100644 --- a/pkg/registry/rbac/rolebinding/storage/storage.go +++ b/pkg/registry/rbac/rolebinding/storage/storage.go @@ -20,6 +20,7 @@ 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/apis/rbac" "k8s.io/kubernetes/pkg/printers" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" @@ -52,3 +53,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { return &REST{store}, nil } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "rolebinding" +} diff --git a/pkg/registry/registrytest/singularNameProvider.go b/pkg/registry/registrytest/singularNameProvider.go new file mode 100644 index 00000000000..5056b0dd477 --- /dev/null +++ b/pkg/registry/registrytest/singularNameProvider.go @@ -0,0 +1,30 @@ +/* +Copyright 2014 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 ( + "testing" + + "k8s.io/apiserver/pkg/registry/rest" +) + +func AssertSingularName(t *testing.T, storage rest.SingularNameProvider, expected string) { + actual := storage.SingularName() + if actual != expected { + t.Errorf("singular name not equal. expected = %v actual = %v", expected, actual) + } +} diff --git a/pkg/registry/scheduling/priorityclass/storage/storage.go b/pkg/registry/scheduling/priorityclass/storage/storage.go index b84a573a12e..b2e78035b19 100644 --- a/pkg/registry/scheduling/priorityclass/storage/storage.go +++ b/pkg/registry/scheduling/priorityclass/storage/storage.go @@ -67,6 +67,13 @@ func (r *REST) ShortNames() []string { return []string{"pc"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "priorityclass" +} + // Delete ensures that system priority classes are not deleted. func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { for _, spc := range schedulingapiv1.SystemPriorityClasses() { diff --git a/pkg/registry/scheduling/priorityclass/storage/storage_test.go b/pkg/registry/scheduling/priorityclass/storage/storage_test.go index de6eba80999..a0a0ae65de0 100644 --- a/pkg/registry/scheduling/priorityclass/storage/storage_test.go +++ b/pkg/registry/scheduling/priorityclass/storage/storage_test.go @@ -179,3 +179,11 @@ func TestShortNames(t *testing.T) { expected := []string{"pc"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "priorityclass" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/storage/storageclass/storage/storage.go b/pkg/registry/storage/storageclass/storage/storage.go index fb4394e6641..fbac3a484ef 100644 --- a/pkg/registry/storage/storageclass/storage/storage.go +++ b/pkg/registry/storage/storageclass/storage/storage.go @@ -62,3 +62,10 @@ var _ rest.ShortNamesProvider = &REST{} func (r *REST) ShortNames() []string { return []string{"sc"} } + +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "storageclass" +} diff --git a/pkg/registry/storage/storageclass/storage/storage_test.go b/pkg/registry/storage/storageclass/storage/storage_test.go index fb1a20dba23..8196e5225dd 100644 --- a/pkg/registry/storage/storageclass/storage/storage_test.go +++ b/pkg/registry/storage/storageclass/storage/storage_test.go @@ -160,3 +160,11 @@ func TestShortNames(t *testing.T) { expected := []string{"sc"} registrytest.AssertShortNames(t, storage, expected) } + +func TestSingularName(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "storageclass" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/pkg/registry/storage/volumeattachment/storage/storage.go b/pkg/registry/storage/volumeattachment/storage/storage.go index 91c1c2a1142..e82338fbd7f 100644 --- a/pkg/registry/storage/volumeattachment/storage/storage.go +++ b/pkg/registry/storage/volumeattachment/storage/storage.go @@ -73,6 +73,13 @@ func NewStorage(optsGetter generic.RESTOptionsGetter) (*VolumeAttachmentStorage, }, nil } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "volumeattachment" +} + // StatusREST implements the REST endpoint for changing the status of a VolumeAttachment type StatusREST struct { store *genericregistry.Store diff --git a/pkg/registry/storage/volumeattachment/storage/storage_test.go b/pkg/registry/storage/volumeattachment/storage/storage_test.go index 6ff5c9f749a..22c13985fed 100644 --- a/pkg/registry/storage/volumeattachment/storage/storage_test.go +++ b/pkg/registry/storage/volumeattachment/storage/storage_test.go @@ -200,3 +200,11 @@ func TestEtcdStatusUpdate(t *testing.T) { t.Errorf("objects differ: %v", diff.ObjectDiff(attachmentOut.Status, attachmentIn.Status)) } } + +func TestSingularName(t *testing.T) { + storage, _, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := "volumeattachment" + registrytest.AssertSingularName(t, storage, expected) +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go index 2523076edf9..55a7cbbf8cf 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go @@ -79,6 +79,13 @@ func (r *REST) Categories() []string { return []string{"api-extensions"} } +var _ rest.SingularNameProvider = &REST{} + +// SingularName implements the SingularNameProvider interfaces. This returns singular name of core resource. +func (r *REST) SingularName() string { + return "customresourcedefinition" +} + // Delete adds the CRD finalizer to the list func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { obj, err := r.Get(ctx, name, &metav1.GetOptions{}) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go b/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go index b0af449f09b..50c59277539 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go @@ -1080,6 +1080,9 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag if categoriesProvider, ok := storage.(rest.CategoriesProvider); ok { apiResource.Categories = categoriesProvider.Categories() } + if singularNameProvider, ok := storage.(rest.SingularNameProvider); ok { + apiResource.SingularName = singularNameProvider.SingularName() + } if gvkProvider, ok := storage.(rest.GroupVersionKindProvider); ok { gvk := gvkProvider.GroupVersionKind(a.group.GroupVersion) apiResource.Group = gvk.Group 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 6330ea8f531..c55ae9675af 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go @@ -89,6 +89,12 @@ type CategoriesProvider interface { Categories() []string } +// SingularNameProvider returns singular name of resources. This is used by kubectl discovery to have singular +// name representation of resources. In case of shortcut conflicts(with CRD shortcuts) singular name should always map to this resource. +type SingularNameProvider interface { + SingularName() string +} + // GroupVersionKindProvider is used to specify a particular GroupVersionKind to discovery. This is used for polymorphic endpoints // which generally point to foreign versions. Scale refers to Scale.v1beta1.extensions for instance. // This trumps KindProvider since it is capable of providing the information required. diff --git a/test/cmd/discovery.sh b/test/cmd/discovery.sh index 4308d9d216b..56aa7859fb1 100755 --- a/test/cmd/discovery.sh +++ b/test/cmd/discovery.sh @@ -55,7 +55,7 @@ run_assert_short_name_tests() { output_message=$(kubectl get --raw=/api/v1) ## 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"\],"storageVersionHash":' + kube::test::if_has_string "${output_message}" '{"name":"configmaps","singularName":"configmap","namespaced":true,"kind":"ConfigMap","verbs":\["create","delete","deletecollection","get","list","patch","update","watch"\],"shortNames":\["cm"\],"storageVersionHash":' # check that there is no pod with the name test-crd-example output_message=$(kubectl get pod)