From 90d1b25a67af72fa617f3bebb7bba56acd687c67 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 2 Jun 2017 16:14:27 -0400 Subject: [PATCH] Add an e2e test for server side get Print a better error from the response. Performs validation to ensure it does not regress in alpha state. Kubernetes-commit: ce972ca47591cc24a3a24362478dc61ec8e91278 --- pkg/endpoints/apiserver_test.go | 9 ++-- pkg/endpoints/installer.go | 5 +- pkg/registry/generic/registry/store.go | 2 +- pkg/registry/rest/table.go | 75 ++++++++++---------------- 4 files changed, 37 insertions(+), 54 deletions(-) diff --git a/pkg/endpoints/apiserver_test.go b/pkg/endpoints/apiserver_test.go index 4d6d58970..0c4730dba 100644 --- a/pkg/endpoints/apiserver_test.go +++ b/pkg/endpoints/apiserver_test.go @@ -437,6 +437,10 @@ func (storage *SimpleRESTStorage) Export(ctx request.Context, name string, opts return obj, storage.errors["export"] } +func (storage *SimpleRESTStorage) ConvertToTable(ctx request.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error) { + return rest.NewDefaultTableConvertor(schema.GroupResource{Resource: "simple"}).ConvertToTable(ctx, obj, tableOptions) +} + func (storage *SimpleRESTStorage) List(ctx request.Context, options *metainternalversion.ListOptions) (runtime.Object, error) { storage.checkContext(ctx) result := &genericapitesting.SimpleList{ @@ -1699,12 +1703,11 @@ func TestGetTable(t *testing.T) { expected: &metav1alpha1.Table{ TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1alpha1"}, ColumnDefinitions: []metav1alpha1.TableColumnDefinition{ - {Name: "Namespace", Type: "string", Description: metaDoc["namespace"]}, {Name: "Name", Type: "string", Description: metaDoc["name"]}, {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, }, Rows: []metav1alpha1.TableRow{ - {Cells: []interface{}{"ns1", "foo1", now.Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBody}}, + {Cells: []interface{}{"foo1", now.Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBody}}, }, }, }, @@ -1729,7 +1732,7 @@ func TestGetTable(t *testing.T) { continue } if resp.StatusCode != http.StatusOK { - t.Fatal(err) + t.Errorf("%d: unexpected response: %#v", resp) } var itemOut metav1alpha1.Table if _, err = extractBody(resp, &itemOut); err != nil { diff --git a/pkg/endpoints/installer.go b/pkg/endpoints/installer.go index eef3b64c6..0fc2308c6 100644 --- a/pkg/endpoints/installer.go +++ b/pkg/endpoints/installer.go @@ -374,10 +374,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag shortNames = shortNamesProvider.ShortNames() } - tableProvider, ok := storage.(rest.TableConvertor) - if !ok { - tableProvider = rest.DefaultTableConvertor - } + tableProvider, _ := storage.(rest.TableConvertor) var apiResource metav1.APIResource // Get the list of actions for the given scope. diff --git a/pkg/registry/generic/registry/store.go b/pkg/registry/generic/registry/store.go index 77b55229e..f7133a310 100644 --- a/pkg/registry/generic/registry/store.go +++ b/pkg/registry/generic/registry/store.go @@ -1351,5 +1351,5 @@ func (e *Store) ConvertToTable(ctx genericapirequest.Context, object runtime.Obj if e.TableConvertor != nil { return e.TableConvertor.ConvertToTable(ctx, object, tableOptions) } - return rest.DefaultTableConvertor.ConvertToTable(ctx, object, tableOptions) + return rest.NewDefaultTableConvertor(e.QualifiedResource).ConvertToTable(ctx, object, tableOptions) } diff --git a/pkg/registry/rest/table.go b/pkg/registry/rest/table.go index cc1e83d2b..21e393c45 100644 --- a/pkg/registry/rest/table.go +++ b/pkg/registry/rest/table.go @@ -17,31 +17,38 @@ limitations under the License. package rest import ( + "fmt" + "net/http" "time" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" ) -var DefaultTableConvertor TableConvertor = defaultTableConvertor{} +type defaultTableConvertor struct { + qualifiedResource schema.GroupResource +} -type defaultTableConvertor struct{} +// NewDefaultTableConvertor creates a default convertor for the provided resource. +func NewDefaultTableConvertor(resource schema.GroupResource) TableConvertor { + return defaultTableConvertor{qualifiedResource: resource} +} var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc() -func (defaultTableConvertor) ConvertToTable(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error) { +func (c defaultTableConvertor) ConvertToTable(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error) { var table metav1alpha1.Table fn := func(obj runtime.Object) error { m, err := meta.Accessor(obj) if err != nil { - // TODO: skip objects we don't recognize - return nil + return errNotAcceptable{resource: c.qualifiedResource} } table.Rows = append(table.Rows, metav1alpha1.TableRow{ - Cells: []interface{}{m.GetClusterName(), m.GetNamespace(), m.GetName(), m.GetCreationTimestamp().Time.UTC().Format(time.RFC3339)}, + Cells: []interface{}{m.GetName(), m.GetCreationTimestamp().Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Object: obj}, }) return nil @@ -57,50 +64,26 @@ func (defaultTableConvertor) ConvertToTable(ctx genericapirequest.Context, objec } } table.ColumnDefinitions = []metav1alpha1.TableColumnDefinition{ - {Name: "Cluster Name", Type: "string", Description: swaggerMetadataDescriptions["clusterName"]}, - {Name: "Namespace", Type: "string", Description: swaggerMetadataDescriptions["namespace"]}, {Name: "Name", Type: "string", Description: swaggerMetadataDescriptions["name"]}, {Name: "Created At", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"]}, } - // trim the left two columns if completely empty - if trimColumn(0, &table) { - trimColumn(0, &table) - } else { - trimColumn(1, &table) - } return &table, nil } -func trimColumn(column int, table *metav1alpha1.Table) bool { - for _, item := range table.Rows { - switch t := item.Cells[column].(type) { - case string: - if len(t) > 0 { - return false - } - case interface{}: - if t == nil { - return false - } - } - } - if column == 0 { - table.ColumnDefinitions = table.ColumnDefinitions[1:] - } else { - for j := column; j < len(table.ColumnDefinitions); j++ { - table.ColumnDefinitions[j] = table.ColumnDefinitions[j+1] - } - } - for i := range table.Rows { - cells := table.Rows[i].Cells - if column == 0 { - table.Rows[i].Cells = cells[1:] - continue - } - for j := column; j < len(cells); j++ { - cells[j] = cells[j+1] - } - table.Rows[i].Cells = cells[:len(cells)-1] - } - return true +// errNotAcceptable indicates the resource doesn't support Table conversion +type errNotAcceptable struct { + resource schema.GroupResource +} + +func (e errNotAcceptable) Error() string { + return fmt.Sprintf("the resource %s does not support being converted to a Table", e.resource) +} + +func (e errNotAcceptable) Status() metav1.Status { + return metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotAcceptable, + Reason: metav1.StatusReason("NotAcceptable"), + Message: e.Error(), + } }