From 1394d8d85c8e033c958e7c08675d7b3495ffe351 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sat, 4 May 2019 16:55:49 -0400 Subject: [PATCH] API server should offer metav1 Table/Partial transforms Now that internal types are equivalent, allow the apiserver to serve metav1 and metav1beta1 depending on the client. Test that in the apiserver integration test and ensure we get the appropriate responses. Register the metav1 type in the appropriate external locations. Kubernetes-commit: 33a3e325f754d179b25558dee116fca1c67d353a --- pkg/endpoints/apiserver_test.go | 165 +++++++++++++++++++++++------ pkg/endpoints/handlers/response.go | 107 +++++++++++++------ pkg/endpoints/handlers/rest.go | 2 +- 3 files changed, 209 insertions(+), 65 deletions(-) diff --git a/pkg/endpoints/apiserver_test.go b/pkg/endpoints/apiserver_test.go index 553948c08..3d0f1106e 100644 --- a/pkg/endpoints/apiserver_test.go +++ b/pkg/endpoints/apiserver_test.go @@ -1749,14 +1749,14 @@ func TestGetPretty(t *testing.T) { pretty bool }{ {accept: runtime.ContentTypeJSON}, - {accept: runtime.ContentTypeJSON + ";pretty=0"}, + {accept: "application/json;pretty=0"}, {accept: runtime.ContentTypeJSON, userAgent: "kubectl"}, {accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"0"}}}, {pretty: true, accept: runtime.ContentTypeJSON, userAgent: "curl"}, {pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Mozilla/5.0"}, {pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Wget"}, - {pretty: true, accept: runtime.ContentTypeJSON + ";pretty=1"}, + {pretty: true, accept: "application/json;pretty=1"}, {pretty: true, accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"1"}}}, {pretty: true, accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"true"}}}, } @@ -1818,14 +1818,28 @@ func TestGetTable(t *testing.T) { if err != nil { t.Fatal(err) } - partial := meta.AsPartialObjectMetadata(m) - partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) - encodedBody, err := runtime.Encode(metainternalversion.Codecs.LegacyCodec(metav1beta1.SchemeGroupVersion), partial) - if err != nil { - t.Fatal(err) + var encodedV1Beta1Body []byte + { + partial := meta.AsPartialObjectMetadata(m) + partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) + encodedBody, err := runtime.Encode(metainternalversion.Codecs.LegacyCodec(metav1beta1.SchemeGroupVersion), partial) + if err != nil { + t.Fatal(err) + } + // the codec includes a trailing newline that is not present during decode + encodedV1Beta1Body = bytes.TrimSpace(encodedBody) + } + var encodedV1Body []byte + { + partial := meta.AsPartialObjectMetadata(m) + partial.GetObjectKind().SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) + encodedBody, err := runtime.Encode(metainternalversion.Codecs.LegacyCodec(metav1.SchemeGroupVersion), partial) + if err != nil { + t.Fatal(err) + } + // the codec includes a trailing newline that is not present during decode + encodedV1Body = bytes.TrimSpace(encodedBody) } - // the codec includes a trailing newline that is not present during decode - encodedBody = bytes.TrimSpace(encodedBody) metaDoc := metav1.ObjectMeta{}.SwaggerDoc() @@ -1838,16 +1852,36 @@ func TestGetTable(t *testing.T) { item bool }{ { - accept: runtime.ContentTypeJSON + ";as=Table;v=v1;g=meta.k8s.io", + accept: "application/json;as=Table;v=v1alpha1;g=meta.k8s.io", statusCode: http.StatusNotAcceptable, }, { accept: runtime.ContentTypeProtobuf + ";as=Table;v=v1beta1;g=meta.k8s.io", statusCode: http.StatusNotAcceptable, }, + { + accept: runtime.ContentTypeProtobuf + ";as=Table;v=v1;g=meta.k8s.io", + statusCode: http.StatusNotAcceptable, + }, + { item: true, - accept: runtime.ContentTypeJSON + ";as=Table;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=Table;v=v1;g=meta.k8s.io", + expected: &metav1.Table{ + TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1"}, + ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"}, + ColumnDefinitions: []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]}, + {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, + }, + Rows: []metav1.TableRow{ + {Cells: []interface{}{"foo1", now.Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedV1Body}}, + }, + }, + }, + { + item: true, + accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io", expected: &metav1beta1.Table{ TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"}, ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"}, @@ -1856,7 +1890,7 @@ func TestGetTable(t *testing.T) { {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, }, Rows: []metav1beta1.TableRow{ - {Cells: []interface{}{"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: encodedV1Beta1Body}}, }, }, }, @@ -1864,7 +1898,7 @@ func TestGetTable(t *testing.T) { item: true, accept: strings.Join([]string{ runtime.ContentTypeProtobuf + ";as=Table;v=v1beta1;g=meta.k8s.io", - runtime.ContentTypeJSON + ";as=Table;v=v1beta1;g=meta.k8s.io", + "application/json;as=Table;v=v1beta1;g=meta.k8s.io", }, ","), expected: &metav1beta1.Table{ TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"}, @@ -1874,13 +1908,13 @@ func TestGetTable(t *testing.T) { {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, }, Rows: []metav1beta1.TableRow{ - {Cells: []interface{}{"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: encodedV1Beta1Body}}, }, }, }, { item: true, - accept: runtime.ContentTypeJSON + ";as=Table;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io", params: url.Values{"includeObject": []string{"Metadata"}}, expected: &metav1beta1.Table{ TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"}, @@ -1890,12 +1924,12 @@ func TestGetTable(t *testing.T) { {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, }, Rows: []metav1beta1.TableRow{ - {Cells: []interface{}{"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: encodedV1Beta1Body}}, }, }, }, { - accept: runtime.ContentTypeJSON + ";as=Table;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io", params: url.Values{"includeObject": []string{"Metadata"}}, expected: &metav1beta1.Table{ TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"}, @@ -1905,7 +1939,7 @@ func TestGetTable(t *testing.T) { {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, }, Rows: []metav1beta1.TableRow{ - {Cells: []interface{}{"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: encodedV1Beta1Body}}, }, }, }, @@ -1996,6 +2030,13 @@ func TestWatchTable(t *testing.T) { // the codec includes a trailing newline that is not present during decode encodedBody = bytes.TrimSpace(encodedBody) + encodedBodyV1, err := runtime.Encode(metainternalversion.Codecs.LegacyCodec(metav1.SchemeGroupVersion), partial) + if err != nil { + t.Fatal(err) + } + // the codec includes a trailing newline that is not present during decode + encodedBodyV1 = bytes.TrimSpace(encodedBodyV1) + metaDoc := metav1.ObjectMeta{}.SwaggerDoc() s := metainternalversion.Codecs.SupportedMediaTypes()[0].Serializer @@ -2011,11 +2052,11 @@ func TestWatchTable(t *testing.T) { item bool }{ { - accept: runtime.ContentTypeJSON + ";as=Table;v=v1;g=meta.k8s.io", + accept: "application/json;as=Table;v=v1alpha1;g=meta.k8s.io", statusCode: http.StatusNotAcceptable, }, { - accept: runtime.ContentTypeJSON + ";as=Table;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io", send: func(w *watch.FakeWatcher) { w.Add(&obj) }, @@ -2039,7 +2080,7 @@ func TestWatchTable(t *testing.T) { }, }, { - accept: runtime.ContentTypeJSON + ";as=Table;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io", send: func(w *watch.FakeWatcher) { w.Add(&obj) w.Modify(&obj) @@ -2075,6 +2116,43 @@ func TestWatchTable(t *testing.T) { }, }, }, + { + accept: "application/json;as=Table;v=v1;g=meta.k8s.io", + send: func(w *watch.FakeWatcher) { + w.Add(&obj) + w.Modify(&obj) + }, + expected: []*metav1.WatchEvent{ + { + Type: "ADDED", + Object: runtime.RawExtension{ + Raw: []byte(strings.TrimSpace(runtime.EncodeOrDie(s, &metav1.Table{ + TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1"}, + ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"}, + ColumnDefinitions: []metav1beta1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]}, + {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, + }, + Rows: []metav1.TableRow{ + {Cells: []interface{}{"foo1", time.Unix(1, 0).UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBodyV1}}, + }, + }))), + }, + }, + { + Type: "MODIFIED", + Object: runtime.RawExtension{ + Raw: []byte(strings.TrimSpace(runtime.EncodeOrDie(s, &metav1.Table{ + TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1"}, + ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"}, + Rows: []metav1.TableRow{ + {Cells: []interface{}{"foo1", time.Unix(1, 0).UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBodyV1}}, + }, + }))), + }, + }, + }, + }, } for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { @@ -2122,6 +2200,7 @@ func TestWatchTable(t *testing.T) { if err != nil { t.Fatal(err) } + defer resp.Body.Close() if test.statusCode != 0 { if resp.StatusCode != test.statusCode { t.Fatalf("%d: unexpected response: %#v", i, resp) @@ -2228,46 +2307,72 @@ func TestGetPartialObjectMetadata(t *testing.T) { statusCode int }{ { - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadata;v=v1;g=meta.k8s.io", + accept: "application/json;as=PartialObjectMetadata;v=v1alpha1;g=meta.k8s.io", statusCode: http.StatusNotAcceptable, }, { - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadata;v=v1;g=meta.k8s.io, application/json", + accept: "application/json;as=PartialObjectMetadata;v=v1alpha1;g=meta.k8s.io, application/json", expectKind: schema.GroupVersionKind{Kind: "Simple", Group: testGroupVersion.Group, Version: testGroupVersion.Version}, }, { - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json", + accept: "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json", expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"}, }, { list: true, - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io", statusCode: http.StatusNotAcceptable, }, + + // verify preferred version overrides supported version + { + accept: "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io, application/json", + expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"}, + }, + { + accept: "application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io, application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json", + expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1"}, + }, + { + accept: "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io", + expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"}, + }, + { + accept: "application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io, application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io", + expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1"}, + }, + { list: true, - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadata;v=v1;g=meta.k8s.io, application/json", + accept: "application/json;as=PartialObjectMetadata;v=v1alpha1;g=meta.k8s.io, application/json", expectKind: schema.GroupVersionKind{Kind: "SimpleList", Group: testGroupVersion.Group, Version: testGroupVersion.Version}, }, { list: true, - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io, application/json", + accept: "application/json;as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io, application/json", expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadataList", Group: "meta.k8s.io", Version: "v1beta1"}, }, { - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io", statusCode: http.StatusNotAcceptable, }, { - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io", expected: &metav1beta1.PartialObjectMetadata{ ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("abcdef0123")}, }, expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"}, }, + { + accept: "application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io", + expected: &metav1.PartialObjectMetadata{ + ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("abcdef0123")}, + }, + expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1"}, + }, { list: true, - accept: runtime.ContentTypeJSON + ";as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io", + accept: "application/json;as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io", expected: &metav1beta1.PartialObjectMetadataList{ ListMeta: metav1.ListMeta{ ResourceVersion: "10", diff --git a/pkg/endpoints/handlers/response.go b/pkg/endpoints/handlers/response.go index 214dbc1e7..0fe8a71c7 100644 --- a/pkg/endpoints/handlers/response.go +++ b/pkg/endpoints/handlers/response.go @@ -49,18 +49,18 @@ func transformObject(ctx context.Context, obj runtime.Object, opts interface{}, case target == nil: return obj, nil - case target.Kind == "PartialObjectMetadata" && target.GroupVersion() == metav1beta1.SchemeGroupVersion: - return asV1Beta1PartialObjectMetadata(obj) + case target.Kind == "PartialObjectMetadata": + return asPartialObjectMetadata(obj, target.GroupVersion()) - case target.Kind == "PartialObjectMetadataList" && target.GroupVersion() == metav1beta1.SchemeGroupVersion: - return asV1Beta1PartialObjectMetadataList(obj) + case target.Kind == "PartialObjectMetadataList": + return asPartialObjectMetadataList(obj, target.GroupVersion()) - case target.Kind == "Table" && target.GroupVersion() == metav1beta1.SchemeGroupVersion: + case target.Kind == "Table": options, ok := opts.(*metav1beta1.TableOptions) if !ok { return nil, fmt.Errorf("unexpected TableOptions, got %T", opts) } - return asV1Beta1Table(ctx, obj, options, scope) + return asTable(ctx, obj, options, scope, target.GroupVersion()) default: accepted, _ := negotiation.MediaTypesForSerializer(metainternalversion.Codecs) @@ -74,7 +74,7 @@ func transformObject(ctx context.Context, obj runtime.Object, opts interface{}, func optionsForTransform(mediaType negotiation.MediaTypeOptions, req *http.Request) (interface{}, error) { switch target := mediaType.Convert; { case target == nil: - case target.Kind == "Table" && target.GroupVersion() == metav1beta1.SchemeGroupVersion: + case target.Kind == "Table" && (target.GroupVersion() == metav1beta1.SchemeGroupVersion || target.GroupVersion() == metav1.SchemeGroupVersion): opts := &metav1beta1.TableOptions{} if err := metav1beta1.ParameterCodec.DecodeParameters(req.URL.Query(), metav1beta1.SchemeGroupVersion, opts); err != nil { return nil, err @@ -95,9 +95,8 @@ func optionsForTransform(mediaType negotiation.MediaTypeOptions, req *http.Reque func targetEncodingForTransform(scope *RequestScope, mediaType negotiation.MediaTypeOptions, req *http.Request) (schema.GroupVersionKind, runtime.NegotiatedSerializer, bool) { switch target := mediaType.Convert; { case target == nil: - case target.Kind == "PartialObjectMetadata" && target.GroupVersion() == metav1beta1.SchemeGroupVersion, - target.Kind == "PartialObjectMetadataList" && target.GroupVersion() == metav1beta1.SchemeGroupVersion, - target.Kind == "Table" && target.GroupVersion() == metav1beta1.SchemeGroupVersion: + case (target.Kind == "PartialObjectMetadata" || target.Kind == "PartialObjectMetadataList" || target.Kind == "Table") && + (target.GroupVersion() == metav1beta1.SchemeGroupVersion || target.GroupVersion() == metav1.SchemeGroupVersion): return *target, metainternalversion.Codecs, true } return scope.Kind, scope.Serializer, false @@ -142,31 +141,39 @@ func (e errNotAcceptable) Status() metav1.Status { } } -func asV1Beta1Table(ctx context.Context, result runtime.Object, opts *metav1beta1.TableOptions, scope *RequestScope) (runtime.Object, error) { - table, err := scope.TableConvertor.ConvertToTable(ctx, result, opts) +func asTable(ctx context.Context, result runtime.Object, opts *metav1beta1.TableOptions, scope *RequestScope, groupVersion schema.GroupVersion) (runtime.Object, error) { + switch groupVersion { + case metav1beta1.SchemeGroupVersion, metav1.SchemeGroupVersion: + default: + return nil, newNotAcceptableError(fmt.Sprintf("no Table exists in group version %s", groupVersion)) + } + + obj, err := scope.TableConvertor.ConvertToTable(ctx, result, opts) if err != nil { return nil, err } + table := (*metav1.Table)(obj) + for i := range table.Rows { item := &table.Rows[i] switch opts.IncludeObject { - case metav1beta1.IncludeObject: + case metav1.IncludeObject: item.Object.Object, err = scope.Convertor.ConvertToVersion(item.Object.Object, scope.Kind.GroupVersion()) if err != nil { return nil, err } // TODO: rely on defaulting for the value here? - case metav1beta1.IncludeMetadata, "": + case metav1.IncludeMetadata, "": m, err := meta.Accessor(item.Object.Object) if err != nil { return nil, err } // TODO: turn this into an internal type and do conversion in order to get object kind automatically set? partial := meta.AsPartialObjectMetadata(m) - partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) + partial.GetObjectKind().SetGroupVersionKind(groupVersion.WithKind("PartialObjectMetadata")) item.Object.Object = partial - case metav1beta1.IncludeNone: + case metav1.IncludeNone: item.Object.Object = nil default: err = errors.NewBadRequest(fmt.Sprintf("unrecognized includeObject value: %q", opts.IncludeObject)) @@ -177,42 +184,74 @@ func asV1Beta1Table(ctx context.Context, result runtime.Object, opts *metav1beta return table, nil } -func asV1Beta1PartialObjectMetadata(result runtime.Object) (runtime.Object, error) { +func asPartialObjectMetadata(result runtime.Object, groupVersion schema.GroupVersion) (runtime.Object, error) { if meta.IsListType(result) { err := newNotAcceptableError(fmt.Sprintf("you requested PartialObjectMetadata, but the requested object is a list (%T)", result)) return nil, err } + switch groupVersion { + case metav1beta1.SchemeGroupVersion, metav1.SchemeGroupVersion: + default: + return nil, newNotAcceptableError(fmt.Sprintf("no PartialObjectMetadataList exists in group version %s", groupVersion)) + } m, err := meta.Accessor(result) if err != nil { return nil, err } partial := meta.AsPartialObjectMetadata(m) - partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) + partial.GetObjectKind().SetGroupVersionKind(groupVersion.WithKind("PartialObjectMetadata")) return partial, nil } -func asV1Beta1PartialObjectMetadataList(result runtime.Object) (runtime.Object, error) { - if !meta.IsListType(result) { +func asPartialObjectMetadataList(result runtime.Object, groupVersion schema.GroupVersion) (runtime.Object, error) { + li, ok := result.(metav1.ListInterface) + if !ok { return nil, newNotAcceptableError(fmt.Sprintf("you requested PartialObjectMetadataList, but the requested object is not a list (%T)", result)) } - list := &metav1beta1.PartialObjectMetadataList{} - if li, ok := result.(metav1.ListInterface); ok { + + gvk := groupVersion.WithKind("PartialObjectMetadata") + switch { + case groupVersion == metav1beta1.SchemeGroupVersion: + list := &metav1beta1.PartialObjectMetadataList{} + err := meta.EachListItem(result, func(obj runtime.Object) error { + m, err := meta.Accessor(obj) + if err != nil { + return err + } + partial := meta.AsPartialObjectMetadata(m) + partial.GetObjectKind().SetGroupVersionKind(gvk) + list.Items = append(list.Items, partial) + return nil + }) + if err != nil { + return nil, err + } list.SelfLink = li.GetSelfLink() list.ResourceVersion = li.GetResourceVersion() list.Continue = li.GetContinue() - } - err := meta.EachListItem(result, func(obj runtime.Object) error { - m, err := meta.Accessor(obj) + return list, nil + + case groupVersion == metav1.SchemeGroupVersion: + list := &metav1.PartialObjectMetadataList{} + err := meta.EachListItem(result, func(obj runtime.Object) error { + m, err := meta.Accessor(obj) + if err != nil { + return err + } + partial := meta.AsPartialObjectMetadata(m) + partial.GetObjectKind().SetGroupVersionKind(gvk) + list.Items = append(list.Items, partial) + return nil + }) if err != nil { - return err + return nil, err } - partial := meta.AsPartialObjectMetadata(m) - partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) - list.Items = append(list.Items, partial) - return nil - }) - if err != nil { - return nil, err + list.SelfLink = li.GetSelfLink() + list.ResourceVersion = li.GetResourceVersion() + list.Continue = li.GetContinue() + return list, nil + + default: + return nil, newNotAcceptableError(fmt.Sprintf("no PartialObjectMetadataList exists in group version %s", groupVersion)) } - return list, nil } diff --git a/pkg/endpoints/handlers/rest.go b/pkg/endpoints/handlers/rest.go index 6adbf8495..4db0c0676 100644 --- a/pkg/endpoints/handlers/rest.go +++ b/pkg/endpoints/handlers/rest.go @@ -78,7 +78,7 @@ func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Reque func (scope *RequestScope) AllowsConversion(gvk schema.GroupVersionKind, mimeType, mimeSubType string) bool { // TODO: this is temporary, replace with an abstraction calculated at endpoint installation time - if gvk.GroupVersion() == metav1beta1.SchemeGroupVersion { + if gvk.GroupVersion() == metav1beta1.SchemeGroupVersion || gvk.GroupVersion() == metav1.SchemeGroupVersion { switch gvk.Kind { case "Table": return scope.TableConvertor != nil &&