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
This commit is contained in:
Clayton Coleman 2019-05-04 16:55:49 -04:00 committed by Kubernetes Publisher
parent d498040504
commit 1394d8d85c
3 changed files with 209 additions and 65 deletions

View File

@ -1749,14 +1749,14 @@ func TestGetPretty(t *testing.T) {
pretty bool pretty bool
}{ }{
{accept: runtime.ContentTypeJSON}, {accept: runtime.ContentTypeJSON},
{accept: runtime.ContentTypeJSON + ";pretty=0"}, {accept: "application/json;pretty=0"},
{accept: runtime.ContentTypeJSON, userAgent: "kubectl"}, {accept: runtime.ContentTypeJSON, userAgent: "kubectl"},
{accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"0"}}}, {accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"0"}}},
{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "curl"}, {pretty: true, accept: runtime.ContentTypeJSON, userAgent: "curl"},
{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Mozilla/5.0"}, {pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Mozilla/5.0"},
{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Wget"}, {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": {"1"}}},
{pretty: true, accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"true"}}}, {pretty: true, accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"true"}}},
} }
@ -1818,14 +1818,28 @@ func TestGetTable(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
partial := meta.AsPartialObjectMetadata(m) var encodedV1Beta1Body []byte
partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) {
encodedBody, err := runtime.Encode(metainternalversion.Codecs.LegacyCodec(metav1beta1.SchemeGroupVersion), partial) partial := meta.AsPartialObjectMetadata(m)
if err != nil { partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
t.Fatal(err) 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() metaDoc := metav1.ObjectMeta{}.SwaggerDoc()
@ -1838,16 +1852,36 @@ func TestGetTable(t *testing.T) {
item bool 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, statusCode: http.StatusNotAcceptable,
}, },
{ {
accept: runtime.ContentTypeProtobuf + ";as=Table;v=v1beta1;g=meta.k8s.io", accept: runtime.ContentTypeProtobuf + ";as=Table;v=v1beta1;g=meta.k8s.io",
statusCode: http.StatusNotAcceptable, statusCode: http.StatusNotAcceptable,
}, },
{
accept: runtime.ContentTypeProtobuf + ";as=Table;v=v1;g=meta.k8s.io",
statusCode: http.StatusNotAcceptable,
},
{ {
item: true, 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{ expected: &metav1beta1.Table{
TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"}, TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"},
ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"}, ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
@ -1856,7 +1890,7 @@ func TestGetTable(t *testing.T) {
{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]}, {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
}, },
Rows: []metav1beta1.TableRow{ 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, item: true,
accept: strings.Join([]string{ accept: strings.Join([]string{
runtime.ContentTypeProtobuf + ";as=Table;v=v1beta1;g=meta.k8s.io", 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{ expected: &metav1beta1.Table{
TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"}, 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"]}, {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
}, },
Rows: []metav1beta1.TableRow{ 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, 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"}}, params: url.Values{"includeObject": []string{"Metadata"}},
expected: &metav1beta1.Table{ expected: &metav1beta1.Table{
TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"}, 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"]}, {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
}, },
Rows: []metav1beta1.TableRow{ 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"}}, params: url.Values{"includeObject": []string{"Metadata"}},
expected: &metav1beta1.Table{ expected: &metav1beta1.Table{
TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"}, 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"]}, {Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
}, },
Rows: []metav1beta1.TableRow{ 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 // the codec includes a trailing newline that is not present during decode
encodedBody = bytes.TrimSpace(encodedBody) 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() metaDoc := metav1.ObjectMeta{}.SwaggerDoc()
s := metainternalversion.Codecs.SupportedMediaTypes()[0].Serializer s := metainternalversion.Codecs.SupportedMediaTypes()[0].Serializer
@ -2011,11 +2052,11 @@ func TestWatchTable(t *testing.T) {
item bool 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, 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) { send: func(w *watch.FakeWatcher) {
w.Add(&obj) 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) { send: func(w *watch.FakeWatcher) {
w.Add(&obj) w.Add(&obj)
w.Modify(&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 { for i, test := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
@ -2122,6 +2200,7 @@ func TestWatchTable(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer resp.Body.Close()
if test.statusCode != 0 { if test.statusCode != 0 {
if resp.StatusCode != test.statusCode { if resp.StatusCode != test.statusCode {
t.Fatalf("%d: unexpected response: %#v", i, resp) t.Fatalf("%d: unexpected response: %#v", i, resp)
@ -2228,46 +2307,72 @@ func TestGetPartialObjectMetadata(t *testing.T) {
statusCode int 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, 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}, 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"}, expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"},
}, },
{ {
list: true, 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, 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, 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}, expectKind: schema.GroupVersionKind{Kind: "SimpleList", Group: testGroupVersion.Group, Version: testGroupVersion.Version},
}, },
{ {
list: true, 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"}, 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, 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{ expected: &metav1beta1.PartialObjectMetadata{
ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("abcdef0123")}, ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("abcdef0123")},
}, },
expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"}, 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, 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{ expected: &metav1beta1.PartialObjectMetadataList{
ListMeta: metav1.ListMeta{ ListMeta: metav1.ListMeta{
ResourceVersion: "10", ResourceVersion: "10",

View File

@ -49,18 +49,18 @@ func transformObject(ctx context.Context, obj runtime.Object, opts interface{},
case target == nil: case target == nil:
return obj, nil return obj, nil
case target.Kind == "PartialObjectMetadata" && target.GroupVersion() == metav1beta1.SchemeGroupVersion: case target.Kind == "PartialObjectMetadata":
return asV1Beta1PartialObjectMetadata(obj) return asPartialObjectMetadata(obj, target.GroupVersion())
case target.Kind == "PartialObjectMetadataList" && target.GroupVersion() == metav1beta1.SchemeGroupVersion: case target.Kind == "PartialObjectMetadataList":
return asV1Beta1PartialObjectMetadataList(obj) return asPartialObjectMetadataList(obj, target.GroupVersion())
case target.Kind == "Table" && target.GroupVersion() == metav1beta1.SchemeGroupVersion: case target.Kind == "Table":
options, ok := opts.(*metav1beta1.TableOptions) options, ok := opts.(*metav1beta1.TableOptions)
if !ok { if !ok {
return nil, fmt.Errorf("unexpected TableOptions, got %T", opts) return nil, fmt.Errorf("unexpected TableOptions, got %T", opts)
} }
return asV1Beta1Table(ctx, obj, options, scope) return asTable(ctx, obj, options, scope, target.GroupVersion())
default: default:
accepted, _ := negotiation.MediaTypesForSerializer(metainternalversion.Codecs) 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) { func optionsForTransform(mediaType negotiation.MediaTypeOptions, req *http.Request) (interface{}, error) {
switch target := mediaType.Convert; { switch target := mediaType.Convert; {
case target == nil: 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{} opts := &metav1beta1.TableOptions{}
if err := metav1beta1.ParameterCodec.DecodeParameters(req.URL.Query(), metav1beta1.SchemeGroupVersion, opts); err != nil { if err := metav1beta1.ParameterCodec.DecodeParameters(req.URL.Query(), metav1beta1.SchemeGroupVersion, opts); err != nil {
return nil, err 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) { func targetEncodingForTransform(scope *RequestScope, mediaType negotiation.MediaTypeOptions, req *http.Request) (schema.GroupVersionKind, runtime.NegotiatedSerializer, bool) {
switch target := mediaType.Convert; { switch target := mediaType.Convert; {
case target == nil: case target == nil:
case target.Kind == "PartialObjectMetadata" && target.GroupVersion() == metav1beta1.SchemeGroupVersion, case (target.Kind == "PartialObjectMetadata" || target.Kind == "PartialObjectMetadataList" || target.Kind == "Table") &&
target.Kind == "PartialObjectMetadataList" && target.GroupVersion() == metav1beta1.SchemeGroupVersion, (target.GroupVersion() == metav1beta1.SchemeGroupVersion || target.GroupVersion() == metav1.SchemeGroupVersion):
target.Kind == "Table" && target.GroupVersion() == metav1beta1.SchemeGroupVersion:
return *target, metainternalversion.Codecs, true return *target, metainternalversion.Codecs, true
} }
return scope.Kind, scope.Serializer, false 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) { func asTable(ctx context.Context, result runtime.Object, opts *metav1beta1.TableOptions, scope *RequestScope, groupVersion schema.GroupVersion) (runtime.Object, error) {
table, err := scope.TableConvertor.ConvertToTable(ctx, result, opts) 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 { if err != nil {
return nil, err return nil, err
} }
table := (*metav1.Table)(obj)
for i := range table.Rows { for i := range table.Rows {
item := &table.Rows[i] item := &table.Rows[i]
switch opts.IncludeObject { switch opts.IncludeObject {
case metav1beta1.IncludeObject: case metav1.IncludeObject:
item.Object.Object, err = scope.Convertor.ConvertToVersion(item.Object.Object, scope.Kind.GroupVersion()) item.Object.Object, err = scope.Convertor.ConvertToVersion(item.Object.Object, scope.Kind.GroupVersion())
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO: rely on defaulting for the value here? // TODO: rely on defaulting for the value here?
case metav1beta1.IncludeMetadata, "": case metav1.IncludeMetadata, "":
m, err := meta.Accessor(item.Object.Object) m, err := meta.Accessor(item.Object.Object)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO: turn this into an internal type and do conversion in order to get object kind automatically set? // TODO: turn this into an internal type and do conversion in order to get object kind automatically set?
partial := meta.AsPartialObjectMetadata(m) partial := meta.AsPartialObjectMetadata(m)
partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) partial.GetObjectKind().SetGroupVersionKind(groupVersion.WithKind("PartialObjectMetadata"))
item.Object.Object = partial item.Object.Object = partial
case metav1beta1.IncludeNone: case metav1.IncludeNone:
item.Object.Object = nil item.Object.Object = nil
default: default:
err = errors.NewBadRequest(fmt.Sprintf("unrecognized includeObject value: %q", opts.IncludeObject)) 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 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) { if meta.IsListType(result) {
err := newNotAcceptableError(fmt.Sprintf("you requested PartialObjectMetadata, but the requested object is a list (%T)", result)) err := newNotAcceptableError(fmt.Sprintf("you requested PartialObjectMetadata, but the requested object is a list (%T)", result))
return nil, err 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) m, err := meta.Accessor(result)
if err != nil { if err != nil {
return nil, err return nil, err
} }
partial := meta.AsPartialObjectMetadata(m) partial := meta.AsPartialObjectMetadata(m)
partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) partial.GetObjectKind().SetGroupVersionKind(groupVersion.WithKind("PartialObjectMetadata"))
return partial, nil return partial, nil
} }
func asV1Beta1PartialObjectMetadataList(result runtime.Object) (runtime.Object, error) { func asPartialObjectMetadataList(result runtime.Object, groupVersion schema.GroupVersion) (runtime.Object, error) {
if !meta.IsListType(result) { 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)) 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.SelfLink = li.GetSelfLink()
list.ResourceVersion = li.GetResourceVersion() list.ResourceVersion = li.GetResourceVersion()
list.Continue = li.GetContinue() list.Continue = li.GetContinue()
} return list, nil
err := meta.EachListItem(result, func(obj runtime.Object) error {
m, err := meta.Accessor(obj) 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 { if err != nil {
return err return nil, err
} }
partial := meta.AsPartialObjectMetadata(m) list.SelfLink = li.GetSelfLink()
partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata")) list.ResourceVersion = li.GetResourceVersion()
list.Items = append(list.Items, partial) list.Continue = li.GetContinue()
return nil return list, nil
})
if err != nil { default:
return nil, err return nil, newNotAcceptableError(fmt.Sprintf("no PartialObjectMetadataList exists in group version %s", groupVersion))
} }
return list, nil
} }

View File

@ -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 { func (scope *RequestScope) AllowsConversion(gvk schema.GroupVersionKind, mimeType, mimeSubType string) bool {
// TODO: this is temporary, replace with an abstraction calculated at endpoint installation time // 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 { switch gvk.Kind {
case "Table": case "Table":
return scope.TableConvertor != nil && return scope.TableConvertor != nil &&