mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
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.
This commit is contained in:
parent
e256c15df3
commit
33a3e325f7
@ -57,7 +57,8 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
// Register external types for Scheme
|
// Register external types for Scheme
|
||||||
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
|
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
|
||||||
utilruntime.Must(metav1beta1.AddToScheme(Scheme))
|
utilruntime.Must(metav1beta1.AddMetaToScheme(Scheme))
|
||||||
|
utilruntime.Must(metav1.AddMetaToScheme(Scheme))
|
||||||
utilruntime.Must(scheme.AddToScheme(Scheme))
|
utilruntime.Must(scheme.AddToScheme(Scheme))
|
||||||
|
|
||||||
utilruntime.Must(Scheme.SetVersionPriority(corev1.SchemeGroupVersion))
|
utilruntime.Must(Scheme.SetVersionPriority(corev1.SchemeGroupVersion))
|
||||||
|
@ -539,12 +539,12 @@ func translateTimestampUntil(timestamp metav1.Time) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
podSuccessConditions = []metav1beta1.TableRowCondition{{Type: metav1beta1.RowCompleted, Status: metav1beta1.ConditionTrue, Reason: string(api.PodSucceeded), Message: "The pod has completed successfully."}}
|
podSuccessConditions = []metav1.TableRowCondition{{Type: metav1.RowCompleted, Status: metav1.ConditionTrue, Reason: string(api.PodSucceeded), Message: "The pod has completed successfully."}}
|
||||||
podFailedConditions = []metav1beta1.TableRowCondition{{Type: metav1beta1.RowCompleted, Status: metav1beta1.ConditionTrue, Reason: string(api.PodFailed), Message: "The pod failed."}}
|
podFailedConditions = []metav1.TableRowCondition{{Type: metav1.RowCompleted, Status: metav1.ConditionTrue, Reason: string(api.PodFailed), Message: "The pod failed."}}
|
||||||
)
|
)
|
||||||
|
|
||||||
func printPodList(podList *api.PodList, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
|
func printPodList(podList *api.PodList, options printers.PrintOptions) ([]metav1.TableRow, error) {
|
||||||
rows := make([]metav1beta1.TableRow, 0, len(podList.Items))
|
rows := make([]metav1.TableRow, 0, len(podList.Items))
|
||||||
for i := range podList.Items {
|
for i := range podList.Items {
|
||||||
r, err := printPod(&podList.Items[i], options)
|
r, err := printPod(&podList.Items[i], options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -555,7 +555,7 @@ func printPodList(podList *api.PodList, options printers.PrintOptions) ([]metav1
|
|||||||
return rows, nil
|
return rows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
|
func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1.TableRow, error) {
|
||||||
restarts := 0
|
restarts := 0
|
||||||
totalContainers := len(pod.Spec.Containers)
|
totalContainers := len(pod.Spec.Containers)
|
||||||
readyContainers := 0
|
readyContainers := 0
|
||||||
@ -565,7 +565,7 @@ func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1beta1.TableR
|
|||||||
reason = pod.Status.Reason
|
reason = pod.Status.Reason
|
||||||
}
|
}
|
||||||
|
|
||||||
row := metav1beta1.TableRow{
|
row := metav1.TableRow{
|
||||||
Object: runtime.RawExtension{Object: pod},
|
Object: runtime.RawExtension{Object: pod},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +148,10 @@ func TestTableGet(t *testing.T) {
|
|||||||
codecs := serializer.NewCodecFactory(scheme)
|
codecs := serializer.NewCodecFactory(scheme)
|
||||||
parameterCodec := runtime.NewParameterCodec(scheme)
|
parameterCodec := runtime.NewParameterCodec(scheme)
|
||||||
metav1.AddToGroupVersion(scheme, gv)
|
metav1.AddToGroupVersion(scheme, gv)
|
||||||
scheme.AddKnownTypes(gv, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
scheme.AddKnownTypes(gv, &metav1beta1.TableOptions{})
|
||||||
|
scheme.AddKnownTypes(gv, &metav1.TableOptions{})
|
||||||
scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||||
|
scheme.AddKnownTypes(metav1.SchemeGroupVersion, &metav1.Table{}, &metav1.TableOptions{})
|
||||||
|
|
||||||
crConfig := *config
|
crConfig := *config
|
||||||
crConfig.GroupVersion = &gv
|
crConfig.GroupVersion = &gv
|
||||||
|
@ -42,7 +42,6 @@ go_library(
|
|||||||
importpath = "k8s.io/apimachinery/pkg/api/meta",
|
importpath = "k8s.io/apimachinery/pkg/api/meta",
|
||||||
deps = [
|
deps = [
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
|
||||||
"k8s.io/apimachinery/pkg/conversion"
|
"k8s.io/apimachinery/pkg/conversion"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
@ -114,12 +113,12 @@ func Accessor(obj interface{}) (metav1.Object, error) {
|
|||||||
|
|
||||||
// AsPartialObjectMetadata takes the metav1 interface and returns a partial object.
|
// AsPartialObjectMetadata takes the metav1 interface and returns a partial object.
|
||||||
// TODO: consider making this solely a conversion action.
|
// TODO: consider making this solely a conversion action.
|
||||||
func AsPartialObjectMetadata(m metav1.Object) *metav1beta1.PartialObjectMetadata {
|
func AsPartialObjectMetadata(m metav1.Object) *metav1.PartialObjectMetadata {
|
||||||
switch t := m.(type) {
|
switch t := m.(type) {
|
||||||
case *metav1.ObjectMeta:
|
case *metav1.ObjectMeta:
|
||||||
return &metav1beta1.PartialObjectMetadata{ObjectMeta: *t}
|
return &metav1.PartialObjectMetadata{ObjectMeta: *t}
|
||||||
default:
|
default:
|
||||||
return &metav1beta1.PartialObjectMetadata{
|
return &metav1.PartialObjectMetadata{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: m.GetName(),
|
Name: m.GetName(),
|
||||||
GenerateName: m.GetGenerateName(),
|
GenerateName: m.GetGenerateName(),
|
||||||
|
@ -113,7 +113,4 @@ func AddMetaToScheme(scheme *runtime.Scheme) error {
|
|||||||
return scheme.AddConversionFuncs(
|
return scheme.AddConversionFuncs(
|
||||||
Convert_Slice_string_To_v1_IncludeObjectPolicy,
|
Convert_Slice_string_To_v1_IncludeObjectPolicy,
|
||||||
)
|
)
|
||||||
|
|
||||||
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
|
|
||||||
//scheme.AddGeneratedDeepCopyFuncs(GetGeneratedDeepCopyFuncs()...)
|
|
||||||
}
|
}
|
||||||
|
@ -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,6 +1818,8 @@ func TestGetTable(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
var encodedV1Beta1Body []byte
|
||||||
|
{
|
||||||
partial := meta.AsPartialObjectMetadata(m)
|
partial := meta.AsPartialObjectMetadata(m)
|
||||||
partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
|
partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
|
||||||
encodedBody, err := runtime.Encode(metainternalversion.Codecs.LegacyCodec(metav1beta1.SchemeGroupVersion), partial)
|
encodedBody, err := runtime.Encode(metainternalversion.Codecs.LegacyCodec(metav1beta1.SchemeGroupVersion), partial)
|
||||||
@ -1825,7 +1827,19 @@ func TestGetTable(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// 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)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
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",
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gvk := groupVersion.WithKind("PartialObjectMetadata")
|
||||||
|
switch {
|
||||||
|
case groupVersion == metav1beta1.SchemeGroupVersion:
|
||||||
list := &metav1beta1.PartialObjectMetadataList{}
|
list := &metav1beta1.PartialObjectMetadataList{}
|
||||||
if li, ok := result.(metav1.ListInterface); ok {
|
|
||||||
list.SelfLink = li.GetSelfLink()
|
|
||||||
list.ResourceVersion = li.GetResourceVersion()
|
|
||||||
list.Continue = li.GetContinue()
|
|
||||||
}
|
|
||||||
err := meta.EachListItem(result, func(obj runtime.Object) error {
|
err := meta.EachListItem(result, func(obj runtime.Object) error {
|
||||||
m, err := meta.Accessor(obj)
|
m, err := meta.Accessor(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
partial := meta.AsPartialObjectMetadata(m)
|
partial := meta.AsPartialObjectMetadata(m)
|
||||||
partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
|
partial.GetObjectKind().SetGroupVersionKind(gvk)
|
||||||
list.Items = append(list.Items, partial)
|
list.Items = append(list.Items, partial)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
list.SelfLink = li.GetSelfLink()
|
||||||
|
list.ResourceVersion = li.GetResourceVersion()
|
||||||
|
list.Continue = li.GetContinue()
|
||||||
return list, nil
|
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 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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 &&
|
||||||
|
@ -548,7 +548,7 @@ func TestAPICRDProtobuf(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTransformOnWatch(t *testing.T) {
|
func TestTransform(t *testing.T) {
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -592,23 +592,23 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
accept string
|
accept string
|
||||||
includeObject metav1beta1.IncludeObjectPolicy
|
includeObject metav1.IncludeObjectPolicy
|
||||||
object func(*testing.T) (metav1.Object, string, string)
|
object func(*testing.T) (metav1.Object, string, string)
|
||||||
wantErr func(*testing.T, error)
|
wantErr func(*testing.T, error)
|
||||||
wantBody func(*testing.T, io.Reader)
|
wantBody func(*testing.T, io.Reader)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "verify columns on cluster scoped resources",
|
name: "v1beta1 verify columns on cluster scoped resources",
|
||||||
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
return &metav1.ObjectMeta{Name: "default", Namespace: ""}, "", "namespaces"
|
return &metav1.ObjectMeta{Name: "default", Namespace: ""}, "", "namespaces"
|
||||||
},
|
},
|
||||||
wantBody: func(t *testing.T, w io.Reader) {
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
expectTableWatchEvents(t, 1, 3, metav1beta1.IncludeMetadata, json.NewDecoder(w))
|
expectTableWatchEvents(t, 1, 3, metav1.IncludeMetadata, json.NewDecoder(w))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify columns on CRDs in json",
|
name: "v1beta1 verify columns on CRDs in json",
|
||||||
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-1"}}}, metav1.CreateOptions{})
|
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-1"}}}, metav1.CreateOptions{})
|
||||||
@ -621,11 +621,11 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
return cr, crdGVR.Group, "foos"
|
return cr, crdGVR.Group, "foos"
|
||||||
},
|
},
|
||||||
wantBody: func(t *testing.T, w io.Reader) {
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
expectTableWatchEvents(t, 2, 2, metav1beta1.IncludeMetadata, json.NewDecoder(w))
|
expectTableWatchEvents(t, 2, 2, metav1.IncludeMetadata, json.NewDecoder(w))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify columns on CRDs in json;stream=watch",
|
name: "v1beta1 verify columns on CRDs in json;stream=watch",
|
||||||
accept: "application/json;stream=watch;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;stream=watch;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-2"}}}, metav1.CreateOptions{})
|
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-2"}}}, metav1.CreateOptions{})
|
||||||
@ -638,11 +638,11 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
return cr, crdGVR.Group, "foos"
|
return cr, crdGVR.Group, "foos"
|
||||||
},
|
},
|
||||||
wantBody: func(t *testing.T, w io.Reader) {
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
expectTableWatchEvents(t, 2, 2, metav1beta1.IncludeMetadata, json.NewDecoder(w))
|
expectTableWatchEvents(t, 2, 2, metav1.IncludeMetadata, json.NewDecoder(w))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify columns on CRDs in yaml",
|
name: "v1beta1 verify columns on CRDs in yaml",
|
||||||
accept: "application/yaml;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/yaml;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-3"}}}, metav1.CreateOptions{})
|
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-3"}}}, metav1.CreateOptions{})
|
||||||
@ -665,7 +665,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify columns on services",
|
name: "v1beta1 verify columns on services",
|
||||||
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
ns := "default"
|
ns := "default"
|
||||||
@ -679,13 +679,13 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
return svc, "", "services"
|
return svc, "", "services"
|
||||||
},
|
},
|
||||||
wantBody: func(t *testing.T, w io.Reader) {
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
expectTableWatchEvents(t, 2, 7, metav1beta1.IncludeMetadata, json.NewDecoder(w))
|
expectTableWatchEvents(t, 2, 7, metav1.IncludeMetadata, json.NewDecoder(w))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify columns on services with no object",
|
name: "v1beta1 verify columns on services with no object",
|
||||||
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
includeObject: metav1beta1.IncludeNone,
|
includeObject: metav1.IncludeNone,
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
ns := "default"
|
ns := "default"
|
||||||
obj, err := clientset.CoreV1().Services(ns).Create(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "test-2"}, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1000}}}})
|
obj, err := clientset.CoreV1().Services(ns).Create(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "test-2"}, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1000}}}})
|
||||||
@ -698,13 +698,13 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
return obj, "", "services"
|
return obj, "", "services"
|
||||||
},
|
},
|
||||||
wantBody: func(t *testing.T, w io.Reader) {
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
expectTableWatchEvents(t, 2, 7, metav1beta1.IncludeNone, json.NewDecoder(w))
|
expectTableWatchEvents(t, 2, 7, metav1.IncludeNone, json.NewDecoder(w))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify columns on services with full object",
|
name: "v1beta1 verify columns on services with full object",
|
||||||
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
includeObject: metav1beta1.IncludeObject,
|
includeObject: metav1.IncludeObject,
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
ns := "default"
|
ns := "default"
|
||||||
obj, err := clientset.CoreV1().Services(ns).Create(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "test-3"}, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1000}}}})
|
obj, err := clientset.CoreV1().Services(ns).Create(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "test-3"}, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1000}}}})
|
||||||
@ -717,7 +717,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
return obj, "", "services"
|
return obj, "", "services"
|
||||||
},
|
},
|
||||||
wantBody: func(t *testing.T, w io.Reader) {
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
objects := expectTableWatchEvents(t, 2, 7, metav1beta1.IncludeObject, json.NewDecoder(w))
|
objects := expectTableWatchEvents(t, 2, 7, metav1.IncludeObject, json.NewDecoder(w))
|
||||||
var svc v1.Service
|
var svc v1.Service
|
||||||
if err := json.Unmarshal(objects[1], &svc); err != nil {
|
if err := json.Unmarshal(objects[1], &svc); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -728,7 +728,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify partial metadata object on config maps",
|
name: "v1beta1 verify partial metadata object on config maps",
|
||||||
accept: "application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
ns := "default"
|
ns := "default"
|
||||||
@ -746,7 +746,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify partial metadata object on config maps in protobuf",
|
name: "v1beta1 verify partial metadata object on config maps in protobuf",
|
||||||
accept: "application/vnd.kubernetes.protobuf;as=PartialObjectMetadata;g=meta.k8s.io;v=v1beta1",
|
accept: "application/vnd.kubernetes.protobuf;as=PartialObjectMetadata;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
ns := "default"
|
ns := "default"
|
||||||
@ -764,7 +764,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify error on unsupported mimetype protobuf for table conversion",
|
name: "v1beta1 verify error on unsupported mimetype protobuf for table conversion",
|
||||||
accept: "application/vnd.kubernetes.protobuf;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/vnd.kubernetes.protobuf;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
@ -781,7 +781,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify error on invalid mimetype - bad version",
|
name: "verify error on invalid mimetype - bad version",
|
||||||
accept: "application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1",
|
accept: "application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1alpha1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
},
|
},
|
||||||
@ -792,7 +792,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify error on invalid mimetype - bad group",
|
name: "v1beta1 verify error on invalid mimetype - bad group",
|
||||||
accept: "application/json;as=PartialObjectMetadata;g=k8s.io;v=v1beta1",
|
accept: "application/json;as=PartialObjectMetadata;g=k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
@ -804,7 +804,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify error on invalid mimetype - bad kind",
|
name: "v1beta1 verify error on invalid mimetype - bad kind",
|
||||||
accept: "application/json;as=PartialObject;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;as=PartialObject;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
@ -816,7 +816,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify error on invalid mimetype - missing kind",
|
name: "v1beta1 verify error on invalid mimetype - missing kind",
|
||||||
accept: "application/json;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;g=meta.k8s.io;v=v1beta1",
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
@ -828,9 +828,241 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "verify error on invalid transform parameter",
|
name: "v1beta1 verify error on invalid transform parameter",
|
||||||
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1beta1",
|
||||||
includeObject: metav1beta1.IncludeObjectPolicy("unrecognized"),
|
includeObject: metav1.IncludeObjectPolicy("unrecognized"),
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
|
},
|
||||||
|
wantErr: func(t *testing.T, err error) {
|
||||||
|
if !apierrors.IsBadRequest(err) || !strings.Contains(err.Error(), `Invalid value: "unrecognized": must be 'Metadata', 'Object', 'None', or empty`) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "v1 verify columns on cluster scoped resources",
|
||||||
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
return &metav1.ObjectMeta{Name: "default", Namespace: ""}, "", "namespaces"
|
||||||
|
},
|
||||||
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
|
expectTableV1WatchEvents(t, 1, 3, metav1.IncludeMetadata, json.NewDecoder(w))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify columns on CRDs in json",
|
||||||
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-4"}}}, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create cr: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := crclient.Patch("test-4", types.MergePatchType, []byte(`{"metadata":{"annotations":{"test":"1"}}}`), metav1.PatchOptions{}); err != nil {
|
||||||
|
t.Fatalf("unable to patch cr: %v", err)
|
||||||
|
}
|
||||||
|
return cr, crdGVR.Group, "foos"
|
||||||
|
},
|
||||||
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
|
expectTableV1WatchEvents(t, 2, 2, metav1.IncludeMetadata, json.NewDecoder(w))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify columns on CRDs in json;stream=watch",
|
||||||
|
accept: "application/json;stream=watch;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-5"}}}, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create cr: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := crclient.Patch("test-5", types.MergePatchType, []byte(`{"metadata":{"annotations":{"test":"1"}}}`), metav1.PatchOptions{}); err != nil {
|
||||||
|
t.Fatalf("unable to patch cr: %v", err)
|
||||||
|
}
|
||||||
|
return cr, crdGVR.Group, "foos"
|
||||||
|
},
|
||||||
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
|
expectTableV1WatchEvents(t, 2, 2, metav1.IncludeMetadata, json.NewDecoder(w))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify columns on CRDs in yaml",
|
||||||
|
accept: "application/yaml;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
cr, err := crclient.Create(&unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "cr.bar.com/v1", "kind": "Foo", "metadata": map[string]interface{}{"name": "test-6"}}}, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create cr: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := crclient.Patch("test-6", types.MergePatchType, []byte(`{"metadata":{"annotations":{"test":"1"}}}`), metav1.PatchOptions{}); err != nil {
|
||||||
|
t.Fatalf("unable to patch cr: %v", err)
|
||||||
|
}
|
||||||
|
return cr, crdGVR.Group, "foos"
|
||||||
|
},
|
||||||
|
wantErr: func(t *testing.T, err error) {
|
||||||
|
if !apierrors.IsNotAcceptable(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// TODO: this should be a more specific error
|
||||||
|
if err.Error() != "only the following media types are accepted: application/json;stream=watch" {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify columns on services",
|
||||||
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
ns := "default"
|
||||||
|
svc, err := clientset.CoreV1().Services(ns).Create(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "test-4"}, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1000}}}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create service: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := clientset.CoreV1().Services(ns).Patch(svc.Name, types.MergePatchType, []byte(`{"metadata":{"annotations":{"test":"1"}}}`)); err != nil {
|
||||||
|
t.Fatalf("unable to update service: %v", err)
|
||||||
|
}
|
||||||
|
return svc, "", "services"
|
||||||
|
},
|
||||||
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
|
expectTableV1WatchEvents(t, 2, 7, metav1.IncludeMetadata, json.NewDecoder(w))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify columns on services with no object",
|
||||||
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
includeObject: metav1.IncludeNone,
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
ns := "default"
|
||||||
|
obj, err := clientset.CoreV1().Services(ns).Create(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "test-5"}, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1000}}}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create object: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := clientset.CoreV1().Services(ns).Patch(obj.Name, types.MergePatchType, []byte(`{"metadata":{"annotations":{"test":"1"}}}`)); err != nil {
|
||||||
|
t.Fatalf("unable to update object: %v", err)
|
||||||
|
}
|
||||||
|
return obj, "", "services"
|
||||||
|
},
|
||||||
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
|
expectTableV1WatchEvents(t, 2, 7, metav1.IncludeNone, json.NewDecoder(w))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify columns on services with full object",
|
||||||
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
includeObject: metav1.IncludeObject,
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
ns := "default"
|
||||||
|
obj, err := clientset.CoreV1().Services(ns).Create(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "test-6"}, Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1000}}}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create object: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := clientset.CoreV1().Services(ns).Patch(obj.Name, types.MergePatchType, []byte(`{"metadata":{"annotations":{"test":"1"}}}`)); err != nil {
|
||||||
|
t.Fatalf("unable to update object: %v", err)
|
||||||
|
}
|
||||||
|
return obj, "", "services"
|
||||||
|
},
|
||||||
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
|
objects := expectTableV1WatchEvents(t, 2, 7, metav1.IncludeObject, json.NewDecoder(w))
|
||||||
|
var svc v1.Service
|
||||||
|
if err := json.Unmarshal(objects[1], &svc); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if svc.Annotations["test"] != "1" || svc.Spec.Ports[0].Port != 1000 {
|
||||||
|
t.Fatalf("unexpected object: %#v", svc)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify partial metadata object on config maps",
|
||||||
|
accept: "application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
ns := "default"
|
||||||
|
obj, err := clientset.CoreV1().ConfigMaps(ns).Create(&v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "test-3", Annotations: map[string]string{"test": "0"}}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create object: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := clientset.CoreV1().ConfigMaps(ns).Patch(obj.Name, types.MergePatchType, []byte(`{"metadata":{"annotations":{"test":"1"}}}`)); err != nil {
|
||||||
|
t.Fatalf("unable to update object: %v", err)
|
||||||
|
}
|
||||||
|
return obj, "", "configmaps"
|
||||||
|
},
|
||||||
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
|
expectPartialObjectMetaV1Events(t, json.NewDecoder(w), "0", "1")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify partial metadata object on config maps in protobuf",
|
||||||
|
accept: "application/vnd.kubernetes.protobuf;as=PartialObjectMetadata;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
ns := "default"
|
||||||
|
obj, err := clientset.CoreV1().ConfigMaps(ns).Create(&v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "test-4", Annotations: map[string]string{"test": "0"}}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create object: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := clientset.CoreV1().ConfigMaps(ns).Patch(obj.Name, types.MergePatchType, []byte(`{"metadata":{"annotations":{"test":"1"}}}`)); err != nil {
|
||||||
|
t.Fatalf("unable to update object: %v", err)
|
||||||
|
}
|
||||||
|
return obj, "", "configmaps"
|
||||||
|
},
|
||||||
|
wantBody: func(t *testing.T, w io.Reader) {
|
||||||
|
expectPartialObjectMetaV1EventsProtobuf(t, w, "0", "1")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify error on unsupported mimetype protobuf for table conversion",
|
||||||
|
accept: "application/vnd.kubernetes.protobuf;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
|
},
|
||||||
|
wantErr: func(t *testing.T, err error) {
|
||||||
|
if !apierrors.IsNotAcceptable(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// TODO: this should be a more specific error
|
||||||
|
if err.Error() != "only the following media types are accepted: application/json, application/yaml, application/vnd.kubernetes.protobuf" {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify error on invalid mimetype - bad group",
|
||||||
|
accept: "application/json;as=PartialObjectMetadata;g=k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
|
},
|
||||||
|
wantErr: func(t *testing.T, err error) {
|
||||||
|
if !apierrors.IsNotAcceptable(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify error on invalid mimetype - bad kind",
|
||||||
|
accept: "application/json;as=PartialObject;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
|
},
|
||||||
|
wantErr: func(t *testing.T, err error) {
|
||||||
|
if !apierrors.IsNotAcceptable(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify error on invalid mimetype - missing kind",
|
||||||
|
accept: "application/json;g=meta.k8s.io;v=v1",
|
||||||
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
|
},
|
||||||
|
wantErr: func(t *testing.T, err error) {
|
||||||
|
if !apierrors.IsNotAcceptable(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 verify error on invalid transform parameter",
|
||||||
|
accept: "application/json;as=Table;g=meta.k8s.io;v=v1",
|
||||||
|
includeObject: metav1.IncludeObjectPolicy("unrecognized"),
|
||||||
object: func(t *testing.T) (metav1.Object, string, string) {
|
object: func(t *testing.T) (metav1.Object, string, string) {
|
||||||
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
return &metav1.ObjectMeta{Name: "kubernetes", Namespace: "default"}, "", "services"
|
||||||
},
|
},
|
||||||
@ -892,7 +1124,7 @@ func TestTransformOnWatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectTableWatchEvents(t *testing.T, count, columns int, policy metav1beta1.IncludeObjectPolicy, d *json.Decoder) [][]byte {
|
func expectTableWatchEvents(t *testing.T, count, columns int, policy metav1.IncludeObjectPolicy, d *json.Decoder) [][]byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var objects [][]byte
|
var objects [][]byte
|
||||||
@ -923,7 +1155,7 @@ func expectTableWatchEvents(t *testing.T, count, columns int, policy metav1beta1
|
|||||||
t.Fatalf("Invalid row width: %#v", row.Cells)
|
t.Fatalf("Invalid row width: %#v", row.Cells)
|
||||||
}
|
}
|
||||||
switch policy {
|
switch policy {
|
||||||
case metav1beta1.IncludeMetadata:
|
case metav1.IncludeMetadata:
|
||||||
var meta metav1beta1.PartialObjectMetadata
|
var meta metav1beta1.PartialObjectMetadata
|
||||||
if err := json.Unmarshal(row.Object.Raw, &meta); err != nil {
|
if err := json.Unmarshal(row.Object.Raw, &meta); err != nil {
|
||||||
t.Fatalf("expected partial object: %v", err)
|
t.Fatalf("expected partial object: %v", err)
|
||||||
@ -932,11 +1164,11 @@ func expectTableWatchEvents(t *testing.T, count, columns int, policy metav1beta1
|
|||||||
if meta.TypeMeta != partialObj {
|
if meta.TypeMeta != partialObj {
|
||||||
t.Fatalf("expected partial object: %#v", meta)
|
t.Fatalf("expected partial object: %#v", meta)
|
||||||
}
|
}
|
||||||
case metav1beta1.IncludeNone:
|
case metav1.IncludeNone:
|
||||||
if len(row.Object.Raw) != 0 {
|
if len(row.Object.Raw) != 0 {
|
||||||
t.Fatalf("Expected no object: %s", string(row.Object.Raw))
|
t.Fatalf("Expected no object: %s", string(row.Object.Raw))
|
||||||
}
|
}
|
||||||
case metav1beta1.IncludeObject:
|
case metav1.IncludeObject:
|
||||||
if len(row.Object.Raw) == 0 {
|
if len(row.Object.Raw) == 0 {
|
||||||
t.Fatalf("Expected object: %s", string(row.Object.Raw))
|
t.Fatalf("Expected object: %s", string(row.Object.Raw))
|
||||||
}
|
}
|
||||||
@ -1000,3 +1232,112 @@ func expectPartialObjectMetaEventsProtobuf(t *testing.T, r io.Reader, values ...
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expectTableV1WatchEvents(t *testing.T, count, columns int, policy metav1.IncludeObjectPolicy, d *json.Decoder) [][]byte {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var objects [][]byte
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
var evt metav1.WatchEvent
|
||||||
|
if err := d.Decode(&evt); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var table metav1.Table
|
||||||
|
if err := json.Unmarshal(evt.Object.Raw, &table); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
if len(table.ColumnDefinitions) != columns {
|
||||||
|
t.Fatalf("Got unexpected columns on first watch event: %d vs %#v", columns, table.ColumnDefinitions)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(table.ColumnDefinitions) != 0 {
|
||||||
|
t.Fatalf("Expected no columns on second watch event: %#v", table.ColumnDefinitions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(table.Rows) != 1 {
|
||||||
|
t.Fatalf("Invalid rows: %#v", table.Rows)
|
||||||
|
}
|
||||||
|
row := table.Rows[0]
|
||||||
|
if len(row.Cells) != columns {
|
||||||
|
t.Fatalf("Invalid row width: %#v", row.Cells)
|
||||||
|
}
|
||||||
|
switch policy {
|
||||||
|
case metav1.IncludeMetadata:
|
||||||
|
var meta metav1.PartialObjectMetadata
|
||||||
|
if err := json.Unmarshal(row.Object.Raw, &meta); err != nil {
|
||||||
|
t.Fatalf("expected partial object: %v", err)
|
||||||
|
}
|
||||||
|
partialObj := metav1.TypeMeta{Kind: "PartialObjectMetadata", APIVersion: "meta.k8s.io/v1"}
|
||||||
|
if meta.TypeMeta != partialObj {
|
||||||
|
t.Fatalf("expected partial object: %#v", meta)
|
||||||
|
}
|
||||||
|
case metav1.IncludeNone:
|
||||||
|
if len(row.Object.Raw) != 0 {
|
||||||
|
t.Fatalf("Expected no object: %s", string(row.Object.Raw))
|
||||||
|
}
|
||||||
|
case metav1.IncludeObject:
|
||||||
|
if len(row.Object.Raw) == 0 {
|
||||||
|
t.Fatalf("Expected object: %s", string(row.Object.Raw))
|
||||||
|
}
|
||||||
|
objects = append(objects, row.Object.Raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objects
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectPartialObjectMetaV1Events(t *testing.T, d *json.Decoder, values ...string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
for i, value := range values {
|
||||||
|
var evt metav1.WatchEvent
|
||||||
|
if err := d.Decode(&evt); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var meta metav1.PartialObjectMetadata
|
||||||
|
if err := json.Unmarshal(evt.Object.Raw, &meta); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
typeMeta := metav1.TypeMeta{Kind: "PartialObjectMetadata", APIVersion: "meta.k8s.io/v1"}
|
||||||
|
if meta.TypeMeta != typeMeta {
|
||||||
|
t.Fatalf("expected partial object: %#v", meta)
|
||||||
|
}
|
||||||
|
if meta.Annotations["test"] != value {
|
||||||
|
t.Fatalf("expected event %d to have value %q instead of %q", i+1, value, meta.Annotations["test"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectPartialObjectMetaV1EventsProtobuf(t *testing.T, r io.Reader, values ...string) {
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
|
metav1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
|
||||||
|
rs := protobuf.NewRawSerializer(scheme, scheme)
|
||||||
|
d := streaming.NewDecoder(
|
||||||
|
protobuf.LengthDelimitedFramer.NewFrameReader(ioutil.NopCloser(r)),
|
||||||
|
rs,
|
||||||
|
)
|
||||||
|
ds := metainternalversion.Codecs.UniversalDeserializer()
|
||||||
|
|
||||||
|
for i, value := range values {
|
||||||
|
var evt metav1.WatchEvent
|
||||||
|
if _, _, err := d.Decode(nil, &evt); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
obj, gvk, err := ds.Decode(evt.Object.Raw, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
meta, ok := obj.(*metav1.PartialObjectMetadata)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("unexpected watch object %T", obj)
|
||||||
|
}
|
||||||
|
expected := &schema.GroupVersionKind{Kind: "PartialObjectMetadata", Version: "v1", Group: "meta.k8s.io"}
|
||||||
|
if !reflect.DeepEqual(expected, gvk) {
|
||||||
|
t.Fatalf("expected partial object: %#v", meta)
|
||||||
|
}
|
||||||
|
if meta.Annotations["test"] != value {
|
||||||
|
t.Fatalf("expected event %d to have value %q instead of %q", i+1, value, meta.Annotations["test"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user