From 218d3e5923d0b1e35dd275321342ad24242d41dd Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Wed, 16 Mar 2016 09:03:33 +0100 Subject: [PATCH] Rename RawJSON to Raw in runtime.Unknown and add ContentType & ContentEncoding. --- pkg/api/meta/help.go | 3 ++- pkg/api/testing/fuzzer.go | 3 ++- pkg/kubectl/custom_column_printer.go | 6 +++--- pkg/kubectl/resource/result.go | 3 ++- pkg/kubectl/sorting_printer.go | 2 +- pkg/runtime/embedded.go | 21 +++++++++++++++------ pkg/runtime/embedded_test.go | 11 +++++++---- pkg/runtime/helper.go | 8 +++++--- pkg/runtime/helper_test.go | 6 +++++- pkg/runtime/scheme_test.go | 6 ++++-- pkg/runtime/serializer/json/json.go | 4 ++-- pkg/runtime/serializer/json/json_test.go | 11 +++++++---- pkg/runtime/types.go | 14 ++++++++++++-- pkg/runtime/unstructured.go | 3 ++- pkg/runtime/unstructured_test.go | 12 ++++++++++-- 15 files changed, 79 insertions(+), 34 deletions(-) diff --git a/pkg/api/meta/help.go b/pkg/api/meta/help.go index 7d1570bc202..f24c57ab1e3 100644 --- a/pkg/api/meta/help.go +++ b/pkg/api/meta/help.go @@ -77,7 +77,8 @@ func ExtractList(obj runtime.Object) ([]runtime.Object, error) { case item.Object != nil: list[i] = item.Object case item.RawJSON != nil: - list[i] = &runtime.Unknown{RawJSON: item.RawJSON} + // TODO: Set ContentEncoding and ContentType. + list[i] = &runtime.Unknown{Raw: item.RawJSON} default: list[i] = nil } diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index 4501014ac36..c4ca2d57ceb 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -180,7 +180,8 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) if true { //c.RandBool() { *j = &runtime.Unknown{ // We do not set TypeMeta here because it is not carried through a round trip - RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), + Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), + ContentType: runtime.ContentTypeJSON, } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} diff --git a/pkg/kubectl/custom_column_printer.go b/pkg/kubectl/custom_column_printer.go index fb29a0a7ed2..255ad1de84d 100644 --- a/pkg/kubectl/custom_column_printer.go +++ b/pkg/kubectl/custom_column_printer.go @@ -191,10 +191,10 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso columns := make([]string, len(parsers)) switch u := obj.(type) { case *runtime.Unknown: - if len(u.RawJSON) > 0 { + if len(u.Raw) > 0 { var err error - if obj, err = runtime.Decode(s.Decoder, u.RawJSON); err != nil { - return fmt.Errorf("can't decode object for printing: %v (%s)", err, u.RawJSON) + if obj, err = runtime.Decode(s.Decoder, u.Raw); err != nil { + return fmt.Errorf("can't decode object for printing: %v (%s)", err, u.Raw) } } } diff --git a/pkg/kubectl/resource/result.go b/pkg/kubectl/resource/result.go index 8d726ab7b73..7beaf74909b 100644 --- a/pkg/kubectl/resource/result.go +++ b/pkg/kubectl/resource/result.go @@ -251,7 +251,8 @@ func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder) if err != nil { return nil, err } - objects = append(objects, &runtime.Unknown{RawJSON: data}) + // TODO: Set ContentEncoding and ContentType. + objects = append(objects, &runtime.Unknown{Raw: data}) continue } } diff --git a/pkg/kubectl/sorting_printer.go b/pkg/kubectl/sorting_printer.go index 49e36b882e9..aefc2a96d69 100644 --- a/pkg/kubectl/sorting_printer.go +++ b/pkg/kubectl/sorting_printer.go @@ -97,7 +97,7 @@ func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput stri switch u := item.(type) { case *runtime.Unknown: var err error - if objs[ix], _, err = decoder.Decode(u.RawJSON, nil, nil); err != nil { + if objs[ix], _, err = decoder.Decode(u.Raw, nil, nil); err != nil { return nil, err } } diff --git a/pkg/runtime/embedded.go b/pkg/runtime/embedded.go index 0934d6837c2..e358df814e6 100644 --- a/pkg/runtime/embedded.go +++ b/pkg/runtime/embedded.go @@ -69,17 +69,23 @@ func (re *Unknown) UnmarshalJSON(in []byte) error { return errors.New("runtime.Unknown: UnmarshalJSON on nil pointer") } re.TypeMeta = TypeMeta{} - re.RawJSON = append(re.RawJSON[0:0], in...) + re.Raw = append(re.Raw[0:0], in...) + re.ContentEncoding = "" + re.ContentType = ContentTypeJSON return nil } // Marshal may get called on pointers or values, so implement MarshalJSON on value. // http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go func (re Unknown) MarshalJSON() ([]byte, error) { - if re.RawJSON == nil { + // If ContentType is unset, we assume this is JSON. + if re.ContentType != "" && re.ContentType != ContentTypeJSON { + return nil, errors.New("runtime.Unknown: MarshalJSON on non-json data") + } + if re.Raw == nil { return []byte("null"), nil } - return re.RawJSON, nil + return re.Raw, nil } func DefaultEmbeddedConversions() []interface{} { @@ -91,8 +97,8 @@ func DefaultEmbeddedConversions() []interface{} { } obj := *in if unk, ok := obj.(*Unknown); ok { - if unk.RawJSON != nil { - out.RawJSON = unk.RawJSON + if unk.Raw != nil { + out.RawJSON = unk.Raw return nil } obj = out.Object @@ -116,7 +122,10 @@ func DefaultEmbeddedConversions() []interface{} { return nil } *out = &Unknown{ - RawJSON: data, + Raw: data, + // TODO: Set ContentEncoding and ContentType appropriately. + // Currently we set ContentTypeJSON to make tests passing. + ContentType: ContentTypeJSON, } return nil }, diff --git a/pkg/runtime/embedded_test.go b/pkg/runtime/embedded_test.go index 64d6d74fb94..162e4d16979 100644 --- a/pkg/runtime/embedded_test.go +++ b/pkg/runtime/embedded_test.go @@ -78,7 +78,7 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { t.Fatalf("unexpected error: %v", err) } test := obj.(*ObjectTest) - if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != "{}" { + if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != "{}" || unk.ContentType != runtime.ContentTypeJSON { t.Fatalf("unexpected object: %#v", test.Items[0]) } if *gvk != externalGVK { @@ -90,7 +90,7 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { t.Fatalf("unexpected error: %v", err) } test = obj.(*ObjectTest) - if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` { + if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != `{"kind":"Other","apiVersion":"v1"}` || unk.ContentType != runtime.ContentTypeJSON { t.Fatalf("unexpected object: %#v", test.Items[0]) } if *gvk != externalGVK { @@ -117,7 +117,10 @@ func TestArrayOfRuntimeObject(t *testing.T) { &EmbeddedTest{ID: "foo"}, &EmbeddedTest{ID: "bar"}, // TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization - &runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)}, + &runtime.Unknown{ + Raw: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`), + ContentType: runtime.ContentTypeJSON, + }, &ObjectTest{ Items: runtime.NewEncodableList(codec, innerItems), }, @@ -208,7 +211,7 @@ func TestNestedObject(t *testing.T) { t.Errorf("Expected unequal %#v %#v", e, a) } - obj, err := runtime.Decode(codec, decoded.(*EmbeddedTest).Object.(*runtime.Unknown).RawJSON) + obj, err := runtime.Decode(codec, decoded.(*EmbeddedTest).Object.(*runtime.Unknown).Raw) if err != nil { t.Fatal(err) } diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index 4a76e81dc9f..351cef52a25 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -80,14 +80,16 @@ func EncodeList(e Encoder, objects []Object, overrides ...unversioned.GroupVersi errs = append(errs, err) continue } - objects[i] = &Unknown{RawJSON: data} + // TODO: Set ContentEncoding and ContentType. + objects[i] = &Unknown{Raw: data} } return errors.NewAggregate(errs) } func decodeListItem(obj *Unknown, decoders []Decoder) (Object, error) { for _, decoder := range decoders { - obj, err := Decode(decoder, obj.RawJSON) + // TODO: Decode based on ContentType. + obj, err := Decode(decoder, obj.Raw) if err != nil { if IsNotRegisteredError(err) { continue @@ -99,7 +101,7 @@ func decodeListItem(obj *Unknown, decoders []Decoder) (Object, error) { // could not decode, so leave the object as Unknown, but give the decoders the // chance to set Unknown.TypeMeta if it is available. for _, decoder := range decoders { - if err := DecodeInto(decoder, obj.RawJSON, obj); err == nil { + if err := DecodeInto(decoder, obj.Raw, obj); err == nil { return obj, nil } } diff --git a/pkg/runtime/helper_test.go b/pkg/runtime/helper_test.go index be7f0dedda1..a4ab5071c2d 100644 --- a/pkg/runtime/helper_test.go +++ b/pkg/runtime/helper_test.go @@ -28,7 +28,11 @@ func TestDecodeList(t *testing.T) { pl := &api.List{ Items: []runtime.Object{ &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: testapi.Default.GroupVersion().String()}, RawJSON: []byte(`{"kind":"Pod","apiVersion":"` + testapi.Default.GroupVersion().String() + `","metadata":{"name":"test"}}`)}, + &runtime.Unknown{ + TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: testapi.Default.GroupVersion().String()}, + Raw: []byte(`{"kind":"Pod","apiVersion":"` + testapi.Default.GroupVersion().String() + `","metadata":{"name":"test"}}`), + ContentType: runtime.ContentTypeJSON, + }, &runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}}, }, } diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index 5b933d84b62..4f48c0e39f1 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -274,7 +274,8 @@ func TestExtensionMapping(t *testing.T) { }, &InternalExtensionType{ Extension: &runtime.Unknown{ - RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`), + Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`), + ContentType: runtime.ContentTypeJSON, }, }, // apiVersion is set in the serialized object for easier consumption by clients @@ -284,7 +285,8 @@ func TestExtensionMapping(t *testing.T) { &InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})}, &InternalExtensionType{ Extension: &runtime.Unknown{ - RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`), + Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`), + ContentType: runtime.ContentTypeJSON, }, }, // apiVersion is set in the serialized object for easier consumption by clients diff --git a/pkg/runtime/serializer/json/json.go b/pkg/runtime/serializer/json/json.go index 299ae854eb5..4b65ce6e2f7 100644 --- a/pkg/runtime/serializer/json/json.go +++ b/pkg/runtime/serializer/json/json.go @@ -108,8 +108,8 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi } if unk, ok := into.(*runtime.Unknown); ok && unk != nil { - unk.RawJSON = originalData - // TODO: set content type here + unk.Raw = originalData + unk.ContentType = runtime.ContentTypeJSON unk.GetObjectKind().SetGroupVersionKind(actual) return unk, actual, nil } diff --git a/pkg/runtime/serializer/json/json_test.go b/pkg/runtime/serializer/json/json_test.go index b9e1319fabc..eb74a26c979 100644 --- a/pkg/runtime/serializer/json/json_test.go +++ b/pkg/runtime/serializer/json/json_test.go @@ -113,7 +113,8 @@ func TestDecode(t *testing.T) { expectedGVK: &unversioned.GroupVersionKind{}, expectedObject: &runtime.Unknown{ - RawJSON: []byte(`{}`), + Raw: []byte(`{}`), + ContentType: runtime.ContentTypeJSON, }, }, { @@ -122,7 +123,8 @@ func TestDecode(t *testing.T) { expectedGVK: &unversioned.GroupVersionKind{}, expectedObject: &runtime.Unknown{ - RawJSON: []byte(`{"test":"object"}`), + Raw: []byte(`{"test":"object"}`), + ContentType: runtime.ContentTypeJSON, }, }, { @@ -131,8 +133,9 @@ func TestDecode(t *testing.T) { defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, expectedObject: &runtime.Unknown{ - TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, - RawJSON: []byte(`{"test":"object"}`), + TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, + Raw: []byte(`{"test":"object"}`), + ContentType: runtime.ContentTypeJSON, }, }, diff --git a/pkg/runtime/types.go b/pkg/runtime/types.go index c2007cc92ee..b8de2fc21c0 100644 --- a/pkg/runtime/types.go +++ b/pkg/runtime/types.go @@ -87,6 +87,10 @@ type RawExtension struct { Object Object `json:"-"` } +// TODO; Where should it be? +// TODO; What value it should have? +const ContentTypeJSON string = "application/json" + // Unknown allows api objects with unknown types to be passed-through. This can be used // to deal with the API objects from a plug-in. Unknown objects still have functioning // TypeMeta features-- kind, version, etc. @@ -96,10 +100,16 @@ type RawExtension struct { // +protobuf=true type Unknown struct { TypeMeta `json:",inline"` - // RawJSON will hold the complete JSON of the object which couldn't be matched + // Raw will hold the complete serialized object which couldn't be matched // with a registered type. Most likely, nothing should be done with this // except for passing it through the system. - RawJSON []byte + Raw []byte + // ContentEncoding is encoding used to encode 'Raw' data. + // Unspecified mean no encoding. + ContentEncoding string + // ContentType is serialization method used to serialize 'Raw'. + // TODO: Define what unspecified means. + ContentType string } // Unstructured allows objects that do not have Golang structs registered to be manipulated diff --git a/pkg/runtime/unstructured.go b/pkg/runtime/unstructured.go index e4cdef8c921..1082ccee076 100644 --- a/pkg/runtime/unstructured.go +++ b/pkg/runtime/unstructured.go @@ -67,7 +67,8 @@ func (unstructuredJSONScheme) EncodeToStream(obj Object, w io.Writer, overrides } return json.NewEncoder(w).Encode(eList) case *Unknown: - _, err := w.Write(t.RawJSON) + // TODO: Unstructured needs to deal with ContentType. + _, err := w.Write(t.Raw) return err default: return json.NewEncoder(w).Encode(t) diff --git a/pkg/runtime/unstructured_test.go b/pkg/runtime/unstructured_test.go index ea1d874402e..9d6ea6303f9 100644 --- a/pkg/runtime/unstructured_test.go +++ b/pkg/runtime/unstructured_test.go @@ -32,8 +32,16 @@ func TestDecodeUnstructured(t *testing.T) { pl := &api.List{ Items: []runtime.Object{ &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: groupVersionString}, RawJSON: []byte(rawJson)}, - &runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "", APIVersion: groupVersionString}, RawJSON: []byte(rawJson)}, + &runtime.Unknown{ + TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: groupVersionString}, + Raw: []byte(rawJson), + ContentType: runtime.ContentTypeJSON, + }, + &runtime.Unknown{ + TypeMeta: runtime.TypeMeta{Kind: "", APIVersion: groupVersionString}, + Raw: []byte(rawJson), + ContentType: runtime.ContentTypeJSON, + }, &runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}}, }, }