diff --git a/pkg/endpoints/handlers/response.go b/pkg/endpoints/handlers/response.go index 70aff7198..1aec3ba2d 100644 --- a/pkg/endpoints/handlers/response.go +++ b/pkg/endpoints/handlers/response.go @@ -34,7 +34,7 @@ import ( // transformResponseObject takes an object loaded from storage and performs any necessary transformations. // Will write the complete response object. func transformResponseObject(ctx request.Context, scope RequestScope, req *http.Request, w http.ResponseWriter, statusCode int, result runtime.Object) { - // TODO: use returned serializer + // TODO: fetch the media type much earlier in request processing and pass it into this method. mediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope) if err != nil { status := responsewriters.ErrorToAPIStatus(err) @@ -169,7 +169,7 @@ func transformResponseObject(ctx request.Context, scope RequestScope, req *http. } } - responsewriters.WriteObject(statusCode, scope.Kind.GroupVersion(), scope.Serializer, result, w, req) + responsewriters.WriteObject(ctx, statusCode, scope.Kind.GroupVersion(), scope.Serializer, result, w, req) } // errNotAcceptable indicates Accept negotiation has failed diff --git a/pkg/registry/generic/registry/BUILD b/pkg/registry/generic/registry/BUILD index 32697b608..15a507c66 100644 --- a/pkg/registry/generic/registry/BUILD +++ b/pkg/registry/generic/registry/BUILD @@ -62,6 +62,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/api/validation/path:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", diff --git a/pkg/registry/generic/registry/store.go b/pkg/registry/generic/registry/store.go index 5ce0c6376..77cf39849 100644 --- a/pkg/registry/generic/registry/store.go +++ b/pkg/registry/generic/registry/store.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/api/validation/path" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -154,6 +155,9 @@ type Store struct { // ExportStrategy implements resource-specific behavior during export, // optional. Exported objects are not decorated. ExportStrategy rest.RESTExportStrategy + // TableConvertor is an optional interface for transforming items or lists + // of items into tabular output. If unset, the default will be used. + TableConvertor rest.TableConvertor // Storage is the interface for the underlying storage for the resource. Storage storage.Interface @@ -168,6 +172,7 @@ type Store struct { // Note: the rest.StandardStorage interface aggregates the common REST verbs var _ rest.StandardStorage = &Store{} var _ rest.Exporter = &Store{} +var _ rest.TableConvertor = &Store{} const OptimisticLockErrorMsg = "the object has been modified; please apply your changes to the latest version and try again" @@ -1233,3 +1238,10 @@ func (e *Store) CompleteWithOptions(options *generic.StoreOptions) error { return nil } + +func (e *Store) ConvertToTable(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error) { + if e.TableConvertor != nil { + return e.TableConvertor.ConvertToTable(ctx, object, tableOptions) + } + return rest.DefaultTableConvertor.ConvertToTable(ctx, object, tableOptions) +} diff --git a/pkg/registry/rest/BUILD b/pkg/registry/rest/BUILD index 3911586cc..7dcca50ca 100644 --- a/pkg/registry/rest/BUILD +++ b/pkg/registry/rest/BUILD @@ -31,6 +31,7 @@ go_library( "export.go", "meta.go", "rest.go", + "table.go", "update.go", ], tags = ["automanaged"], @@ -42,6 +43,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", diff --git a/pkg/registry/rest/rest.go b/pkg/registry/rest/rest.go index 262e05697..015754f86 100644 --- a/pkg/registry/rest/rest.go +++ b/pkg/registry/rest/rest.go @@ -118,7 +118,7 @@ type GetterWithOptions interface { } type TableConvertor interface { - ConvertToTableList(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.TableList, error) + ConvertToTable(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error) } // Deleter is an object that can delete a named RESTful resource. diff --git a/pkg/registry/rest/table.go b/pkg/registry/rest/table.go index 4b48711d3..cc1e83d2b 100644 --- a/pkg/registry/rest/table.go +++ b/pkg/registry/rest/table.go @@ -32,15 +32,15 @@ type defaultTableConvertor struct{} var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc() -func (defaultTableConvertor) ConvertToTableList(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.TableList, error) { - var table metav1alpha1.TableList +func (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 } - table.Items = append(table.Items, metav1alpha1.TableListItem{ + table.Rows = append(table.Rows, metav1alpha1.TableRow{ Cells: []interface{}{m.GetClusterName(), m.GetNamespace(), m.GetName(), m.GetCreationTimestamp().Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Object: obj}, }) @@ -56,7 +56,7 @@ func (defaultTableConvertor) ConvertToTableList(ctx genericapirequest.Context, o return nil, err } } - table.Headers = []metav1alpha1.TableListHeader{ + 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"]}, @@ -71,8 +71,8 @@ func (defaultTableConvertor) ConvertToTableList(ctx genericapirequest.Context, o return &table, nil } -func trimColumn(column int, table *metav1alpha1.TableList) bool { - for _, item := range table.Items { +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 { @@ -85,22 +85,22 @@ func trimColumn(column int, table *metav1alpha1.TableList) bool { } } if column == 0 { - table.Headers = table.Headers[1:] + table.ColumnDefinitions = table.ColumnDefinitions[1:] } else { - for j := column; j < len(table.Headers); j++ { - table.Headers[j] = table.Headers[j+1] + for j := column; j < len(table.ColumnDefinitions); j++ { + table.ColumnDefinitions[j] = table.ColumnDefinitions[j+1] } } - for i := range table.Items { - cells := table.Items[i].Cells + for i := range table.Rows { + cells := table.Rows[i].Cells if column == 0 { - table.Items[i].Cells = cells[1:] + table.Rows[i].Cells = cells[1:] continue } for j := column; j < len(cells); j++ { cells[j] = cells[j+1] } - table.Items[i].Cells = cells[:len(cells)-1] + table.Rows[i].Cells = cells[:len(cells)-1] } return true }