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
This commit is contained in:
Clayton Coleman 2017-06-02 16:14:27 -04:00 committed by Kubernetes Publisher
parent 42b5738617
commit 90d1b25a67
4 changed files with 37 additions and 54 deletions

View File

@ -437,6 +437,10 @@ func (storage *SimpleRESTStorage) Export(ctx request.Context, name string, opts
return obj, storage.errors["export"] 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) { func (storage *SimpleRESTStorage) List(ctx request.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
storage.checkContext(ctx) storage.checkContext(ctx)
result := &genericapitesting.SimpleList{ result := &genericapitesting.SimpleList{
@ -1699,12 +1703,11 @@ func TestGetTable(t *testing.T) {
expected: &metav1alpha1.Table{ expected: &metav1alpha1.Table{
TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1alpha1"}, TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1alpha1"},
ColumnDefinitions: []metav1alpha1.TableColumnDefinition{ ColumnDefinitions: []metav1alpha1.TableColumnDefinition{
{Name: "Namespace", Type: "string", Description: metaDoc["namespace"]},
{Name: "Name", Type: "string", Description: metaDoc["name"]}, {Name: "Name", Type: "string", Description: metaDoc["name"]},
{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
}, },
Rows: []metav1alpha1.TableRow{ 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 continue
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
t.Fatal(err) t.Errorf("%d: unexpected response: %#v", resp)
} }
var itemOut metav1alpha1.Table var itemOut metav1alpha1.Table
if _, err = extractBody(resp, &itemOut); err != nil { if _, err = extractBody(resp, &itemOut); err != nil {

View File

@ -374,10 +374,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
shortNames = shortNamesProvider.ShortNames() shortNames = shortNamesProvider.ShortNames()
} }
tableProvider, ok := storage.(rest.TableConvertor) tableProvider, _ := storage.(rest.TableConvertor)
if !ok {
tableProvider = rest.DefaultTableConvertor
}
var apiResource metav1.APIResource var apiResource metav1.APIResource
// Get the list of actions for the given scope. // Get the list of actions for the given scope.

View File

@ -1351,5 +1351,5 @@ func (e *Store) ConvertToTable(ctx genericapirequest.Context, object runtime.Obj
if e.TableConvertor != nil { if e.TableConvertor != nil {
return e.TableConvertor.ConvertToTable(ctx, object, tableOptions) return e.TableConvertor.ConvertToTable(ctx, object, tableOptions)
} }
return rest.DefaultTableConvertor.ConvertToTable(ctx, object, tableOptions) return rest.NewDefaultTableConvertor(e.QualifiedResource).ConvertToTable(ctx, object, tableOptions)
} }

View File

@ -17,31 +17,38 @@ limitations under the License.
package rest package rest
import ( import (
"fmt"
"net/http"
"time" "time"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1" metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 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() 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 var table metav1alpha1.Table
fn := func(obj runtime.Object) error { fn := func(obj runtime.Object) error {
m, err := meta.Accessor(obj) m, err := meta.Accessor(obj)
if err != nil { if err != nil {
// TODO: skip objects we don't recognize return errNotAcceptable{resource: c.qualifiedResource}
return nil
} }
table.Rows = append(table.Rows, metav1alpha1.TableRow{ 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}, Object: runtime.RawExtension{Object: obj},
}) })
return nil return nil
@ -57,50 +64,26 @@ func (defaultTableConvertor) ConvertToTable(ctx genericapirequest.Context, objec
} }
} }
table.ColumnDefinitions = []metav1alpha1.TableColumnDefinition{ 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: "Name", Type: "string", Description: swaggerMetadataDescriptions["name"]},
{Name: "Created At", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"]}, {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 return &table, nil
} }
func trimColumn(column int, table *metav1alpha1.Table) bool { // errNotAcceptable indicates the resource doesn't support Table conversion
for _, item := range table.Rows { type errNotAcceptable struct {
switch t := item.Cells[column].(type) { resource schema.GroupResource
case string: }
if len(t) > 0 {
return false func (e errNotAcceptable) Error() string {
} return fmt.Sprintf("the resource %s does not support being converted to a Table", e.resource)
case interface{}: }
if t == nil {
return false func (e errNotAcceptable) Status() metav1.Status {
} return metav1.Status{
} Status: metav1.StatusFailure,
} Code: http.StatusNotAcceptable,
if column == 0 { Reason: metav1.StatusReason("NotAcceptable"),
table.ColumnDefinitions = table.ColumnDefinitions[1:] Message: e.Error(),
} 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
} }