Commit e7341f4d authored by Nikhita Raghunath's avatar Nikhita Raghunath

Add Categories to CRD spec

We can group custom resources into categories i.e. use them with kubectl get all.
parent da564ef4
......@@ -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
......
......@@ -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
......
......@@ -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
}
......
......@@ -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 {
......
......@@ -459,7 +459,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
statusSpec,
scaleSpec,
),
r.restOptionsGetter,
r.restOptionsGetter, crd.Status.AcceptedNames.Categories,
)
selfLinkPrefix := ""
......
......@@ -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
......
......@@ -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
......
......@@ -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)
......
......@@ -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) {
......
......@@ -72,6 +72,7 @@ func NewNoxuCustomResourceDefinition(scope apiextensionsv1beta1.ResourceScope) *
Kind: "WishIHadChosenNoxu",
ShortNames: []string{"foo", "bar", "abc", "def"},
ListKind: "NoxuItemList",
Categories: []string{"all"},
},
Scope: scope,
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment