Introduce singularNameProvider for core types

This introduces `singularNameProvider`. This provider will be used
by core types to have their singular names are defined in discovery
endpoint. Thanks to that, core resources singular name always have
higher precedence than CRDs shortcuts or singular names.
This commit is contained in:
Arda Güçlü 2022-11-02 12:53:56 +03:00
parent ab376e09dd
commit 0990ba1cc9
65 changed files with 501 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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