diff --git a/pkg/registry/auditregistration/auditsink/storage/BUILD b/pkg/registry/auditregistration/auditsink/storage/BUILD index daa0d67de052..0c58422bef79 100644 --- a/pkg/registry/auditregistration/auditsink/storage/BUILD +++ b/pkg/registry/auditregistration/auditsink/storage/BUILD @@ -11,6 +11,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/auditregistration/auditsink/storage/storage.go b/pkg/registry/auditregistration/auditsink/storage/storage.go index 2ef0b5ee6b84..a2ba834c7f78 100644 --- a/pkg/registry/auditregistration/auditsink/storage/storage.go +++ b/pkg/registry/auditregistration/auditsink/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/auditregistration" auditstrategy "k8s.io/kubernetes/pkg/registry/auditregistration/auditsink" ) @@ -42,6 +43,9 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { CreateStrategy: auditstrategy.Strategy, UpdateStrategy: auditstrategy.Strategy, DeleteStrategy: auditstrategy.Strategy, + + // TODO: define table converter that exposes more than name/creation timestamp + TableConvertor: rest.NewDefaultTableConvertor(auditregistration.Resource("auditsinks")), } options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { diff --git a/pkg/registry/core/limitrange/storage/storage.go b/pkg/registry/core/limitrange/storage/storage.go index dac160ea96a4..95af1d4fd1d9 100644 --- a/pkg/registry/core/limitrange/storage/storage.go +++ b/pkg/registry/core/limitrange/storage/storage.go @@ -41,6 +41,9 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { UpdateStrategy: limitrange.Strategy, DeleteStrategy: limitrange.Strategy, ExportStrategy: limitrange.Strategy, + + // TODO: define table converter that exposes more than name/creation timestamp + TableConvertor: rest.NewDefaultTableConvertor(api.Resource("limitranges")), } options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { diff --git a/pkg/registry/rbac/clusterrole/storage/BUILD b/pkg/registry/rbac/clusterrole/storage/BUILD index 2bffd61ab870..19c19bedfc72 100644 --- a/pkg/registry/rbac/clusterrole/storage/BUILD +++ b/pkg/registry/rbac/clusterrole/storage/BUILD @@ -15,6 +15,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/rbac/clusterrole/storage/storage.go b/pkg/registry/rbac/clusterrole/storage/storage.go index 7a32c8d7b5e7..59347f767a44 100644 --- a/pkg/registry/rbac/clusterrole/storage/storage.go +++ b/pkg/registry/rbac/clusterrole/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/registry/rbac/clusterrole" ) @@ -39,6 +40,9 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { CreateStrategy: clusterrole.Strategy, UpdateStrategy: clusterrole.Strategy, DeleteStrategy: clusterrole.Strategy, + + // TODO: define table converter that exposes more than name/creation timestamp? + TableConvertor: rest.NewDefaultTableConvertor(rbac.Resource("clusterroles")), } options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { diff --git a/pkg/registry/rbac/role/storage/BUILD b/pkg/registry/rbac/role/storage/BUILD index 3b2548d39911..51844b9d43f1 100644 --- a/pkg/registry/rbac/role/storage/BUILD +++ b/pkg/registry/rbac/role/storage/BUILD @@ -15,6 +15,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/rbac/role/storage/storage.go b/pkg/registry/rbac/role/storage/storage.go index 1c94fb89f950..6e6700ede989 100644 --- a/pkg/registry/rbac/role/storage/storage.go +++ b/pkg/registry/rbac/role/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/registry/rbac/role" ) @@ -39,6 +40,9 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { CreateStrategy: role.Strategy, UpdateStrategy: role.Strategy, DeleteStrategy: role.Strategy, + + // TODO: define table converter that exposes more than name/creation timestamp? + TableConvertor: rest.NewDefaultTableConvertor(rbac.Resource("roles")), } options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { diff --git a/pkg/registry/settings/podpreset/storage/BUILD b/pkg/registry/settings/podpreset/storage/BUILD index eddc3e03bb05..c106c75cf4e8 100644 --- a/pkg/registry/settings/podpreset/storage/BUILD +++ b/pkg/registry/settings/podpreset/storage/BUILD @@ -16,6 +16,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/settings/podpreset/storage/storage.go b/pkg/registry/settings/podpreset/storage/storage.go index c2a7224db43b..9fdd46bda9fe 100644 --- a/pkg/registry/settings/podpreset/storage/storage.go +++ b/pkg/registry/settings/podpreset/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" settingsapi "k8s.io/kubernetes/pkg/apis/settings" "k8s.io/kubernetes/pkg/registry/settings/podpreset" ) @@ -39,6 +40,9 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { CreateStrategy: podpreset.Strategy, UpdateStrategy: podpreset.Strategy, DeleteStrategy: podpreset.Strategy, + + // TODO: define table converter that exposes more than name/creation timestamp + TableConvertor: rest.NewDefaultTableConvertor(settingsapi.Resource("podpresets")), } options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { 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 0368678e1205..2252421148a5 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 @@ -50,6 +50,9 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) *REST CreateStrategy: strategy, UpdateStrategy: strategy, DeleteStrategy: strategy, + + // TODO: define table converter that exposes more than name/creation timestamp + TableConvertor: rest.NewDefaultTableConvertor(apiextensions.Resource("customresourcedefinitions")), } options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs} if err := store.CompleteWithOptions(options); err != nil { diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go b/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go index 37579820a4a0..06e0b2118979 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go @@ -381,7 +381,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag resourceKind = kind } - tableProvider, _ := storage.(rest.TableConvertor) + tableProvider, isTableProvider := storage.(rest.TableConvertor) + if isLister && !isTableProvider { + // All listers must implement TableProvider + return nil, fmt.Errorf("%q must implement TableConvertor", resource) + } var apiResource metav1.APIResource if utilfeature.DefaultFeatureGate.Enabled(features.StorageVersionHash) && diff --git a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go index 3993bd2366ca..edd75bc2af6b 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go @@ -1217,6 +1217,10 @@ func (e *Store) CompleteWithOptions(options *generic.StoreOptions) error { return fmt.Errorf("store for %s must set both KeyRootFunc and KeyFunc or neither", e.DefaultQualifiedResource.String()) } + if e.TableConvertor == nil { + return fmt.Errorf("store for %s must set TableConvertor; rest.NewDefaultTableConvertor(e.DefaultQualifiedResource) can be used to output just name/creation time", e.DefaultQualifiedResource.String()) + } + var isNamespaced bool switch { case e.CreateStrategy != nil: @@ -1377,7 +1381,7 @@ func (e *Store) ConvertToTable(ctx context.Context, object runtime.Object, table if e.TableConvertor != nil { return e.TableConvertor.ConvertToTable(ctx, object, tableOptions) } - return rest.NewDefaultTableConvertor(e.qualifiedResourceFromContext(ctx)).ConvertToTable(ctx, object, tableOptions) + return rest.NewDefaultTableConvertor(e.DefaultQualifiedResource).ConvertToTable(ctx, object, tableOptions) } func (e *Store) StorageVersion() runtime.GroupVersioner { diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go index 31a46c743337..d90ae70762b7 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go @@ -26,15 +26,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" ) type defaultTableConvertor struct { - qualifiedResource schema.GroupResource + defaultQualifiedResource schema.GroupResource } -// NewDefaultTableConvertor creates a default convertor for the provided resource. -func NewDefaultTableConvertor(resource schema.GroupResource) TableConvertor { - return defaultTableConvertor{qualifiedResource: resource} +// NewDefaultTableConvertor creates a default convertor; the provided resource is used for error messages +// if no resource info can be determined from the context passed to ConvertToTable. +func NewDefaultTableConvertor(defaultQualifiedResource schema.GroupResource) TableConvertor { + return defaultTableConvertor{defaultQualifiedResource: defaultQualifiedResource} } var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc() @@ -44,7 +46,11 @@ func (c defaultTableConvertor) ConvertToTable(ctx context.Context, object runtim fn := func(obj runtime.Object) error { m, err := meta.Accessor(obj) if err != nil { - return errNotAcceptable{resource: c.qualifiedResource} + resource := c.defaultQualifiedResource + if info, ok := genericapirequest.RequestInfoFrom(ctx); ok { + resource = schema.GroupResource{Group: info.APIGroup, Resource: info.Resource} + } + return errNotAcceptable{resource: resource} } table.Rows = append(table.Rows, metav1.TableRow{ Cells: []interface{}{m.GetName(), m.GetCreationTimestamp().Time.UTC().Format(time.RFC3339)}, diff --git a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go index dd3562391a12..7fefca5ce496 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go @@ -48,6 +48,9 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) *REST CreateStrategy: strategy, UpdateStrategy: strategy, DeleteStrategy: strategy, + + // TODO: define table converter that exposes more than name/creation timestamp + TableConvertor: rest.NewDefaultTableConvertor(apiregistration.Resource("apiservices")), } options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: apiservice.GetAttrs} if err := store.CompleteWithOptions(options); err != nil { diff --git a/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/BUILD b/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/BUILD index 6a3dc34d8485..c674b2d8cb4d 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/BUILD +++ b/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/BUILD @@ -20,6 +20,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library", "//staging/src/k8s.io/sample-apiserver/pkg/apis/wardle:go_default_library", diff --git a/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/etcd.go b/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/etcd.go index 34473c8cf20a..4a28fadc5a4d 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/etcd.go +++ b/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/etcd.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/sample-apiserver/pkg/apis/wardle" "k8s.io/sample-apiserver/pkg/registry" ) @@ -37,6 +38,9 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*reg CreateStrategy: strategy, UpdateStrategy: strategy, DeleteStrategy: strategy, + + // TODO: define table converter that exposes more than name/creation timestamp + TableConvertor: rest.NewDefaultTableConvertor(wardle.Resource("fischers")), } options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs} if err := store.CompleteWithOptions(options); err != nil { diff --git a/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/BUILD b/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/BUILD index 34493fd591b3..f10e2be4b52e 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/BUILD +++ b/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/BUILD @@ -20,6 +20,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library", "//staging/src/k8s.io/sample-apiserver/pkg/apis/wardle:go_default_library", diff --git a/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/etcd.go b/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/etcd.go index 99cf4d2a5c1e..271e63cd96af 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/etcd.go +++ b/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/etcd.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/sample-apiserver/pkg/apis/wardle" "k8s.io/sample-apiserver/pkg/registry" ) @@ -37,6 +38,9 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*reg CreateStrategy: strategy, UpdateStrategy: strategy, DeleteStrategy: strategy, + + // TODO: define table converter that exposes more than name/creation timestamp + TableConvertor: rest.NewDefaultTableConvertor(wardle.Resource("flunders")), } options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs} if err := store.CompleteWithOptions(options); err != nil { diff --git a/test/e2e/kubectl/BUILD b/test/e2e/kubectl/BUILD index e2f589f0cdd9..7ee2f774279c 100644 --- a/test/e2e/kubectl/BUILD +++ b/test/e2e/kubectl/BUILD @@ -19,20 +19,15 @@ go_library( "//staging/src/k8s.io/api/rbac/v1:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library", - "//staging/src/k8s.io/client-go/discovery:go_default_library", - "//staging/src/k8s.io/client-go/dynamic:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/kubectl/pkg/polymorphichelpers:go_default_library", "//test/e2e/common:go_default_library", @@ -44,7 +39,6 @@ go_library( "//test/e2e/framework/service:go_default_library", "//test/e2e/framework/testfiles:go_default_library", "//test/e2e/scheduling:go_default_library", - "//test/integration/etcd:go_default_library", "//test/utils:go_default_library", "//test/utils/crd:go_default_library", "//test/utils/image:go_default_library", diff --git a/test/e2e/kubectl/kubectl.go b/test/e2e/kubectl/kubectl.go index 11c5a2056d1f..66c8b6e9b9b4 100644 --- a/test/e2e/kubectl/kubectl.go +++ b/test/e2e/kubectl/kubectl.go @@ -48,20 +48,15 @@ import ( rbacv1 "k8s.io/api/rbac/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" utilnet "k8s.io/apimachinery/pkg/util/net" - "k8s.io/apimachinery/pkg/util/rand" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/authentication/serviceaccount" genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" - "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubernetes/pkg/controller" @@ -74,7 +69,6 @@ import ( e2eservice "k8s.io/kubernetes/test/e2e/framework/service" "k8s.io/kubernetes/test/e2e/framework/testfiles" "k8s.io/kubernetes/test/e2e/scheduling" - "k8s.io/kubernetes/test/integration/etcd" testutils "k8s.io/kubernetes/test/utils" "k8s.io/kubernetes/test/utils/crd" imageutils "k8s.io/kubernetes/test/utils/image" @@ -349,107 +343,6 @@ var _ = SIGDescribe("Kubectl client", func() { }) }) - ginkgo.Describe("kubectl get output", func() { - ginkgo.It("should contain custom columns for each resource", func() { - randString := rand.String(10) - - ignoredResources := map[string]bool{ - // ignored for intentionally using standard fields. - // This assumption is based on a lack of TableColumnDefinition - // in pkg/printers/internalversion/printers.go - "ClusterRole": true, - "Role": true, - "LimitRange": true, - "PodPreset": true, - - // ignored for being disruptive in an e2e, and getting automatically deleted by a controller - "Node": true, - - // ignored temporarily while waiting for bug fix. - "CustomResourceDefinition": true, - - // ignored because no test data exists. - // Do not add anything to this list, instead add fixtures in - // the test/integration/etcd package. - "BackendConfig": true, - "ComponentStatus": true, - "NodeMetrics": true, - "PodMetrics": true, - "VolumeSnapshotClass": true, - "VolumeSnapshotContent": true, - "VolumeSnapshot": true, - } - - apiGroups, err := c.Discovery().ServerPreferredResources() - - if discovery.IsGroupDiscoveryFailedError(err) { - discoveryErr := err.(*discovery.ErrGroupDiscoveryFailed) - for gv := range discoveryErr.Groups { - if strings.Contains(gv.Group, ".") && !strings.HasSuffix(gv.Group, ".k8s.io") { - // tolerate discovery errors for non-k8s.io groups (like aggregated/crd groups) - continue - } - if gv.Group == "metrics.k8s.io" { - // tolerate discovery errors for known test k8s.io groups like aggregated/metrics groups - continue - } - // otherwise, fail - framework.ExpectNoError(err) - } - } else { - // fail immediately if this isn't a discovery error - framework.ExpectNoError(err) - - } - - testableResources := etcd.GetEtcdStorageDataForNamespace(f.Namespace.Name) - - for _, group := range apiGroups { - // This limits the scope of this test to exclude CRDs. This - // assumes that CRDs will not have a .k8s.io group and will have - // a . in their name. - if !strings.Contains(group.GroupVersion, ".k8s.io") && strings.Contains(group.GroupVersion, ".") { - continue - } - - for _, resource := range group.APIResources { - if !verbsContain(resource.Verbs, "get") || ignoredResources[resource.Kind] || strings.HasPrefix(resource.Name, "e2e-test") { - continue - } - - // compute gvr - gv, err := schema.ParseGroupVersion(group.GroupVersion) - framework.ExpectNoError(err) - gvr := gv.WithResource(resource.Name) - - // assert test data exists - testData := testableResources[gvr] - gomega.ExpectWithOffset(1, testData).ToNot(gomega.BeZero(), "No test data available for %s", gvr) - - // create test resource - mapping := &meta.RESTMapping{ - Resource: gvr, - GroupVersionKind: gv.WithKind(resource.Kind), - } - - if resource.Namespaced { - mapping.Scope = meta.RESTScopeNamespace - } else { - mapping.Scope = meta.RESTScopeRoot - } - - client, obj, err := etcd.JSONToUnstructured(testData.Stub, f.Namespace.Name, mapping, f.DynamicClient) - framework.ExpectNoError(err) - if resource.Kind != "APIService" && resource.Kind != "CustomResourceDefinition" { - obj.SetName(obj.GetName() + randString) - } - - createObjValidateOutputAndCleanup(f.Namespace.Name, client, obj, resource) - } - } - }) - }) - ginkgo.Describe("Simple pod", func() { var podYaml string ginkgo.BeforeEach(func() { @@ -2224,52 +2117,3 @@ waitLoop: // Reaching here means that one of more checks failed multiple times. Assuming its not a race condition, something is broken. framework.Failf("Timed out after %v seconds waiting for %s pods to reach valid state", framework.PodStartTimeout.Seconds(), testname) } - -// verbsContain returns true if the provided list of verbs contain the provided -// verb string. -func verbsContain(verbs metav1.Verbs, str string) bool { - for _, v := range verbs { - if v == str { - return true - } - } - return false -} - -// deleteObj deletes an Object with the provided client and name. -func deleteObj(client dynamic.ResourceInterface, name string) { - err := client.Delete(context.TODO(), name, metav1.DeleteOptions{}) - framework.ExpectNoError(err) -} - -// createObjValidateOutputAndCleanup creates an object using the provided client -// and then verifies that the kubectl get output provides custom columns. Once -// the test has completed, it deletes the object. -func createObjValidateOutputAndCleanup(namespace string, client dynamic.ResourceInterface, obj *unstructured.Unstructured, resource metav1.APIResource) { - _, err := client.Create(context.TODO(), obj, metav1.CreateOptions{}) - framework.ExpectNoError(err) - defer deleteObj(client, obj.GetName()) - - // get test resource - output := framework.RunKubectlOrDie(namespace, "get", resource.Name, "--all-namespaces") - if output == "" { - framework.Failf("No stdout from kubectl get for %s (likely need to define test resources)", resource.Name) - } - - splitOutput := strings.SplitN(output, "\n", 2) - fields := strings.Fields(splitOutput[0]) - - defaultColumns := [][]string{ - // namespaced, server-side - {"NAMESPACE", "NAME", "CREATED", "AT"}, - // namespaced, client-side - {"NAMESPACE", "NAME", "AGE"}, - // cluster-scoped, server-side - {"NAME", "CREATED", "AT"}, - // cluster-scoped, client-side - {"NAME", "AGE"}, - } - for _, defaults := range defaultColumns { - framework.ExpectNotEqual(fields, defaults, fmt.Sprintf("expected non-default fields for resource: %s", resource.Name)) - } -}