Add Categories to CRD spec

We can group custom resources into categories i.e.
use them with kubectl get all.
This commit is contained in:
Nikhita Raghunath 2018-02-08 08:18:46 +05:30
parent da564ef4fb
commit e7341f4deb
10 changed files with 49 additions and 7 deletions

View File

@ -49,6 +49,9 @@ type CustomResourceDefinitionNames struct {
Kind string
// ListKind is the serialized kind of the list for this resource. Defaults to <kind>List.
ListKind string
// Categories is a list of grouped resources custom resources belong to (e.g. 'all')
// +optional
Categories []string
}
// ResourceScope is an enum defining the different scopes available to a custom resource

View File

@ -53,6 +53,9 @@ type CustomResourceDefinitionNames struct {
Kind string `json:"kind" protobuf:"bytes,4,opt,name=kind"`
// ListKind is the serialized kind of the list for this resource. Defaults to <kind>List.
ListKind string `json:"listKind,omitempty" protobuf:"bytes,5,opt,name=listKind"`
// Categories is a list of grouped resources custom resources belong to (e.g. 'all')
// +optional
Categories []string `json:"categories,omitempty" protobuf:"bytes,6,rep,name=categories"`
}
// ResourceScope is an enum defining the different scopes available to a custom resource

View File

@ -165,7 +165,6 @@ func ValidateCustomResourceDefinitionNames(names *apiextensions.CustomResourceDe
if errs := validationutil.IsDNS1035Label(shortName); len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("shortNames").Index(i), shortName, strings.Join(errs, ",")))
}
}
// kind and listKind may not be the same or parsing become ambiguous
@ -173,6 +172,12 @@ func ValidateCustomResourceDefinitionNames(names *apiextensions.CustomResourceDe
allErrs = append(allErrs, field.Invalid(fldPath.Child("listKind"), names.ListKind, "kind and listKind may not be the same"))
}
for i, category := range names.Categories {
if errs := validationutil.IsDNS1035Label(category); len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("categories").Index(i), category, strings.Join(errs, ",")))
}
}
return allErrs
}

View File

@ -117,6 +117,7 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
Kind: crd.Status.AcceptedNames.Kind,
Verbs: verbs,
ShortNames: crd.Status.AcceptedNames.ShortNames,
Categories: crd.Status.AcceptedNames.Categories,
})
if crd.Spec.Subresources != nil && crd.Spec.Subresources.Status != nil {

View File

@ -459,7 +459,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
statusSpec,
scaleSpec,
),
r.restOptionsGetter,
r.restOptionsGetter, crd.Status.AcceptedNames.Categories,
)
selfLinkPrefix := ""

View File

@ -182,6 +182,8 @@ func (c *NamingConditionController) calculateNamesAndConditions(in *apiextension
newNames.ListKind = requestedNames.ListKind
}
newNames.Categories = requestedNames.Categories
// if we haven't changed the condition, then our names must be good.
if namesAcceptedCondition.Status == apiextensions.ConditionUnknown {
namesAcceptedCondition.Status = apiextensions.ConditionTrue

View File

@ -39,8 +39,8 @@ type CustomResourceStorage struct {
Scale *ScaleREST
}
func NewStorage(resource schema.GroupResource, listKind schema.GroupVersionKind, strategy customResourceStrategy, optsGetter generic.RESTOptionsGetter) CustomResourceStorage {
customResourceREST, customResourceStatusREST := newREST(resource, listKind, strategy, optsGetter)
func NewStorage(resource schema.GroupResource, listKind schema.GroupVersionKind, strategy customResourceStrategy, optsGetter generic.RESTOptionsGetter, categories []string) CustomResourceStorage {
customResourceREST, customResourceStatusREST := newREST(resource, listKind, strategy, optsGetter, categories)
customResourceRegistry := NewRegistry(customResourceREST)
s := CustomResourceStorage{
@ -71,10 +71,11 @@ func NewStorage(resource schema.GroupResource, listKind schema.GroupVersionKind,
// REST implements a RESTStorage for API services against etcd
type REST struct {
*genericregistry.Store
categories []string
}
// newREST returns a RESTStorage object that will work against API services.
func newREST(resource schema.GroupResource, listKind schema.GroupVersionKind, strategy customResourceStrategy, optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
func newREST(resource schema.GroupResource, listKind schema.GroupVersionKind, strategy customResourceStrategy, optsGetter generic.RESTOptionsGetter, categories []string) (*REST, *StatusREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &unstructured.Unstructured{} },
NewListFunc: func() runtime.Object {
@ -97,7 +98,15 @@ func newREST(resource schema.GroupResource, listKind schema.GroupVersionKind, st
statusStore := *store
statusStore.UpdateStrategy = NewStatusStrategy(strategy)
return &REST{store}, &StatusREST{store: &statusStore}
return &REST{store, categories}, &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 r.categories
}
// StatusREST implements the REST endpoint for changing the status of a CustomResource

View File

@ -18,6 +18,7 @@ package customresource_test
import (
"io"
"reflect"
"strings"
"testing"
@ -82,7 +83,7 @@ func newStorage(t *testing.T) (customresource.CustomResourceStorage, *etcdtestin
status,
scale,
),
restOptions,
restOptions, []string{"all"},
)
return storage, server
@ -153,6 +154,19 @@ func TestDelete(t *testing.T) {
test.TestDelete(validNewCustomResource())
}
func TestCategories(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.CustomResource.Store.DestroyFunc()
expected := []string{"all"}
actual := storage.CustomResource.Categories()
ok := reflect.DeepEqual(actual, expected)
if !ok {
t.Errorf("categories are not equal. expected = %v actual = %v", expected, actual)
}
}
func TestStatusUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)

View File

@ -388,6 +388,10 @@ func TestDiscovery(t *testing.T) {
if !reflect.DeepEqual([]string(r.Verbs), expectedVerbs) {
t.Fatalf("Unexpected verbs for resource \"noxus\" in group version %v/%v via discovery: expected=%v got=%v", group, version, expectedVerbs, r.Verbs)
}
if !reflect.DeepEqual(r.Categories, []string{"all"}) {
t.Fatalf("Expected exactly the category \"all\" in group version %v/%v via discovery, got: %v", group, version, r.Categories)
}
}
func TestNoNamespaceReject(t *testing.T) {

View File

@ -72,6 +72,7 @@ func NewNoxuCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *
Kind: "WishIHadChosenNoxu",
ShortNames: []string{"foo", "bar", "abc", "def"},
ListKind: "NoxuItemList",
Categories: []string{"all"},
},
Scope: scope,
},