func TestBadJSONRejection(t *testing.T) { badJSONMissingKind := []byte(`{ }`) if _, err := runtime.Decode(testapi.Default.Codec(), badJSONMissingKind); err == nil { t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) } badJSONUnknownType := []byte(`{"kind": "bar"}`) if _, err1 := runtime.Decode(testapi.Default.Codec(), badJSONUnknownType); err1 == nil { t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) } /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) if err2 := DecodeInto(badJSONKindMismatch, &Minion{}); err2 == nil { t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) }*/ }
func TestExperimentalEncodeDecodeStatus(t *testing.T) { // TODO: caesarxuchao: use the testapi.Extensions.Codec() once the PR that // moves experimental from v1 to v1beta1 got merged. expCodec := api.Codecs.LegacyCodec(extensions.SchemeGroupVersion) encoded, err := runtime.Encode(expCodec, status) if err != nil { t.Errorf("unexpected error: %v", err) } typeMeta := unversioned.TypeMeta{} if err := json.Unmarshal(encoded, &typeMeta); err != nil { t.Errorf("unexpected error: %v", err) } if typeMeta.Kind != "Status" { t.Errorf("Kind is not set to \"Status\". Got %s", encoded) } if typeMeta.APIVersion != "v1" { t.Errorf("APIVersion is not set to \"\". Got %s", encoded) } decoded, err := runtime.Decode(expCodec, encoded) if err != nil { t.Errorf("unexpected error: %v", err) } if !reflect.DeepEqual(status, decoded) { t.Errorf("expected: %v, got: %v", status, decoded) } }
func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { t.Logf("codec: %#v", codec) printer := spew.ConfigState{DisableMethods: true} name := reflect.TypeOf(item).Elem().Name() data, err := runtime.Encode(codec, item) if err != nil { t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item)) return } obj2, err := runtime.Decode(codec, data) if err != nil { t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), printer.Sprintf("%#v", item)) return } if !api.Semantic.DeepEqual(item, obj2) { t.Errorf("\n1: %v: diff: %v\nCodec: %v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, util.ObjectGoPrintDiff(item, obj2), codec, printer.Sprintf("%#v", item), string(data), printer.Sprintf("%#v", obj2)) return } obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) if err := runtime.DecodeInto(codec, data, obj3); err != nil { t.Errorf("2: %v: %v", name, err) return } if !api.Semantic.DeepEqual(item, obj3) { t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(item, obj3), codec) return } }
func runTest(t *testing.T, source interface{}) { name := reflect.TypeOf(source).Elem().Name() TestObjectFuzzer.Fuzz(source) _, codec := GetTestScheme() data, err := runtime.Encode(codec, source.(runtime.Object)) if err != nil { t.Errorf("%v: %v (%#v)", name, err, source) return } obj2, err := runtime.Decode(codec, data) if err != nil { t.Errorf("%v: %v (%v)", name, err, string(data)) return } if !semantic.DeepEqual(source, obj2) { t.Errorf("1: %v: diff: %v", name, util.ObjectGoPrintSideBySide(source, obj2)) return } obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface() if err := runtime.DecodeInto(codec, data, obj3.(runtime.Object)); err != nil { t.Errorf("2: %v: %v", name, err) return } if !semantic.DeepEqual(source, obj3) { t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3)) return } }
func TestExternalToInternalMapping(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) table := []struct { obj runtime.Object encoded string }{ { &InternalOptionalExtensionType{Extension: nil}, `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } for i, item := range table { gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a)) } } }
func TestUnversionedTypes(t *testing.T) { testcases := []runtime.Object{ &unversioned.Status{Status: "Failure", Message: "something went wrong"}, &unversioned.APIVersions{Versions: []string{"A", "B", "C"}}, &unversioned.APIGroupList{Groups: []unversioned.APIGroup{{Name: "mygroup"}}}, &unversioned.APIGroup{Name: "mygroup"}, &unversioned.APIResourceList{GroupVersion: "mygroup/myversion"}, } for _, obj := range testcases { // Make sure the unversioned codec can encode unversionedJSON, err := runtime.Encode(testapi.Default.Codec(), obj) if err != nil { t.Errorf("%v: unexpected error: %v", obj, err) continue } // Make sure the versioned codec under test can decode versionDecodedObject, err := runtime.Decode(testapi.Default.Codec(), unversionedJSON) if err != nil { t.Errorf("%v: unexpected error: %v", obj, err) continue } // Make sure it decodes correctly if !reflect.DeepEqual(obj, versionDecodedObject) { t.Errorf("%v: expected %#v, got %#v", obj, obj, versionDecodedObject) continue } } }
func TestEncode_Ptr(t *testing.T) { grace := int64(30) pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{"name": "foo"}, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, SecurityContext: &api.PodSecurityContext{}, }, } obj := runtime.Object(pod) data, err := runtime.Encode(testapi.Default.Codec(), obj) obj2, err2 := runtime.Decode(testapi.Default.Codec(), data) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } if _, ok := obj2.(*api.Pod); !ok { t.Fatalf("Got wrong type") } if !api.Semantic.DeepEqual(obj2, pod) { t.Errorf("\nExpected:\n\n %#v,\n\nGot:\n\n %#vDiff: %v\n\n", pod, obj2, util.ObjectDiff(obj2, pod)) } }
func TestV1EncodeDecodeStatus(t *testing.T) { v1Codec := testapi.Default.Codec() encoded, err := runtime.Encode(v1Codec, status) if err != nil { t.Errorf("unexpected error: %v", err) } typeMeta := unversioned.TypeMeta{} if err := json.Unmarshal(encoded, &typeMeta); err != nil { t.Errorf("unexpected error: %v", err) } if typeMeta.Kind != "Status" { t.Errorf("Kind is not set to \"Status\". Got %v", string(encoded)) } if typeMeta.APIVersion != "v1" { t.Errorf("APIVersion is not set to \"v1\". Got %v", string(encoded)) } decoded, err := runtime.Decode(v1Codec, encoded) if err != nil { t.Errorf("unexpected error: %v", err) } if !reflect.DeepEqual(status, decoded) { t.Errorf("expected: %v, got: %v", status, decoded) } }
func TestBadJSONRejection(t *testing.T) { scheme := runtime.NewScheme() codecs := serializer.NewCodecFactory(scheme) jsonserializer, _ := codecs.SerializerForFileExtension("json") badJSONMissingKind := []byte(`{ }`) if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil { t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) } badJSONUnknownType := []byte(`{"kind": "bar"}`) if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil { t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) } /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) if err2 := DecodeInto(badJSONKindMismatch, &Minion{}); err2 == nil { t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) }*/ }
// Stream formats and executes the request, and offers streaming of the response. // Returns io.ReadCloser which could be used for streaming of the response, or an error // Any non-2xx http status code causes an error. If we get a non-2xx code, we try to convert the body into an APIStatus object. // If we can, we return that as an error. Otherwise, we create an error that lists the http status and the content of the response. func (r *Request) Stream() (io.ReadCloser, error) { if r.err != nil { return nil, r.err } url := r.URL().String() req, err := http.NewRequest(r.verb, url, nil) if err != nil { return nil, err } client := r.client if client == nil { client = http.DefaultClient } time.Sleep(r.backoffMgr.CalculateBackoff(r.URL())) resp, err := client.Do(req) if r.baseURL != nil { if err != nil { r.backoffMgr.UpdateBackoff(r.URL(), err, 0) } else { r.backoffMgr.UpdateBackoff(r.URL(), err, resp.StatusCode) } } if err != nil { return nil, err } switch { case (resp.StatusCode >= 200) && (resp.StatusCode < 300): return resp.Body, nil default: // ensure we close the body before returning the error defer resp.Body.Close() // we have a decent shot at taking the object returned, parsing it as a status object and returning a more normal error bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("%v while accessing %v", resp.Status, url) } if runtimeObject, err := runtime.Decode(r.content.Codec, bodyBytes); err == nil { statusError := errors.FromObject(runtimeObject) if _, ok := statusError.(errors.APIStatus); ok { return nil, statusError } } bodyText := string(bodyBytes) return nil, fmt.Errorf("%s while accessing %v: %s", resp.Status, url, bodyText) } }
func TestBadJSONRejection(t *testing.T) { log.SetOutput(os.Stderr) _, codec := GetTestScheme() badJSONs := [][]byte{ []byte(`{"myVersionKey":"v1"}`), // Missing kind []byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind []byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version []byte(`{"myKindKey":"TestType1"}`), // Missing version } for _, b := range badJSONs { if _, err := runtime.Decode(codec, b); err == nil { t.Errorf("Did not reject bad json: %s", string(b)) } } badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`) if err := runtime.DecodeInto(codec, badJSONKindMismatch, &TestType1{}); err == nil { t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) } if err := runtime.DecodeInto(codec, []byte(``), &TestType1{}); err != nil { t.Errorf("Should allow empty decode: %v", err) } if _, _, err := codec.Decode([]byte(``), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err == nil { t.Errorf("Did not give error for empty data with only kind default") } if _, _, err := codec.Decode([]byte(`{"myVersionKey":"v1"}`), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err != nil { t.Errorf("Gave error for version and kind default") } if _, _, err := codec.Decode([]byte(`{"myKindKey":"ExternalInternalSame"}`), &unversioned.GroupVersionKind{Version: "v1"}, nil); err != nil { t.Errorf("Gave error for version and kind default") } if _, _, err := codec.Decode([]byte(``), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame", Version: "v1"}, nil); err != nil { t.Errorf("Gave error for version and kind defaulted: %v", err) } if _, err := runtime.Decode(codec, []byte(``)); err == nil { t.Errorf("Did not give error for empty data") } }
func TestEncode_Ptr(t *testing.T) { _, codec := GetTestScheme() tt := &TestType1{A: "I am a pointer object"} data, err := runtime.Encode(codec, tt) obj2, err2 := runtime.Decode(codec, data) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'\n%s", err, err2, data) } if _, ok := obj2.(*TestType1); !ok { t.Fatalf("Got wrong type") } if !semantic.DeepEqual(obj2, tt) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", tt, obj2) } }
// Decode blocks until it can return the next object in the writer. Returns an error // if the writer is closed or an object can't be decoded. func (d *Decoder) Decode() (watch.EventType, runtime.Object, error) { var got WatchEvent if err := d.decoder.Decode(&got); err != nil { return "", nil, err } switch got.Type { case watch.Added, watch.Modified, watch.Deleted, watch.Error: default: return "", nil, fmt.Errorf("got invalid watch event type: %v", got.Type) } obj, err := runtime.Decode(d.codec, got.Object.RawJSON) if err != nil { return "", nil, fmt.Errorf("unable to decode watch event: %v", err) } return got.Type, obj, nil }
// AddObjectsFromPath loads the JSON or YAML file containing Kubernetes API resources // and adds them to the provided ObjectRetriever. func AddObjectsFromPath(path string, o ObjectRetriever, decoder runtime.Decoder) error { data, err := ioutil.ReadFile(path) if err != nil { return err } data, err = yaml.ToJSON(data) if err != nil { return err } obj, err := runtime.Decode(decoder, data) if err != nil { return err } if err := o.Add(obj); err != nil { return err } return nil }
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { data, err := runtime.Encode(api.Codecs.LegacyCodec(SchemeGroupVersion), obj) if err != nil { t.Errorf("%v\n %#v", err, obj) return nil } obj2, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) if err != nil { t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) return nil } obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) err = api.Scheme.Convert(obj2, obj3) if err != nil { t.Errorf("%v\nSource: %#v", err, obj2) return nil } return obj3 }
// transformResponse converts an API response into a structured API object func (r *Request) transformResponse(resp *http.Response, req *http.Request) Result { var body []byte if resp.Body != nil { if data, err := ioutil.ReadAll(resp.Body); err == nil { body = data } } glog.V(8).Infof("Response Body: %s", string(body)) // Did the server give us a status response? isStatusResponse := false var status *unversioned.Status result, err := runtime.Decode(r.content.Codec, body) if out, ok := result.(*unversioned.Status); err == nil && ok && len(out.Status) > 0 { status = out isStatusResponse = true } switch { case resp.StatusCode == http.StatusSwitchingProtocols: // no-op, we've been upgraded case resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent: if !isStatusResponse { return Result{err: r.transformUnstructuredResponseError(resp, req, body)} } return Result{err: errors.FromObject(status)} } // If the server gave us a status back, look at what it was. success := resp.StatusCode >= http.StatusOK && resp.StatusCode <= http.StatusPartialContent if isStatusResponse && (status.Status != unversioned.StatusSuccess && !success) { // "Failed" requests are clearly just an error and it makes sense to return them as such. return Result{err: errors.FromObject(status)} } return Result{ body: body, contentType: resp.Header.Get("Content-Type"), statusCode: resp.StatusCode, decoder: r.content.Codec, } }
func BenchmarkDecodeCodec(b *testing.B) { codec := testapi.Default.Codec() items := benchmarkItems() width := len(items) encoded := make([][]byte, width) for i := range items { data, err := runtime.Encode(codec, &items[i]) if err != nil { b.Fatal(err) } encoded[i] = data } b.ResetTimer() for i := 0; i < b.N; i++ { if _, err := runtime.Decode(codec, encoded[i%width]); err != nil { b.Fatal(err) } } b.StopTimer() }
func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) { internalGV := unversioned.GroupVersion{Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Version: "v1"} s := runtime.NewScheme() // create two names internally, with TestType1 being preferred s.AddKnownTypeWithName(internalGV.WithKind("TestType1"), &TestType1{}) s.AddKnownTypeWithName(internalGV.WithKind("OtherType1"), &TestType1{}) // create two names externally, with TestType1 being preferred s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("OtherType1"), &ExternalTestType1{}) ext := &ExternalTestType1{} ext.APIVersion = "v1" ext.ObjectKind = "OtherType1" ext.A = "test" data, err := json.Marshal(ext) if err != nil { t.Fatalf("unexpected error: %v", err) } expect := &TestType1{A: "test"} codec := newCodecFactory(s, testMetaFactory{}).LegacyCodec(unversioned.GroupVersion{Version: "v1"}) obj, err := runtime.Decode(codec, data) if err != nil { t.Fatalf("unexpected error: %v", err) } if !semantic.DeepEqual(expect, obj) { t.Errorf("unexpected object: %#v", obj) } into := &TestType1{} if err := runtime.DecodeInto(codec, data, into); err != nil { t.Fatalf("unexpected error: %v", err) } if !semantic.DeepEqual(expect, into) { t.Errorf("unexpected object: %#v", obj) } }
func TestDoRequestCreated(t *testing.T) { status := &unversioned.Status{Status: unversioned.StatusSuccess} expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) fakeHandler := utiltesting.FakeHandler{ StatusCode: 201, ResponseBody: string(expectedBody), T: t, } testServer := httptest.NewServer(&fakeHandler) // TODO: Uncomment when fix #19254 // defer testServer.Close() c, err := RESTClientFor(&Config{ Host: testServer.URL, ContentConfig: ContentConfig{ GroupVersion: testapi.Default.GroupVersion(), Codec: testapi.Default.Codec(), }, Username: "******", Password: "******", }) if err != nil { t.Fatalf("unexpected error: %v", err) } created := false body, err := c.Get().Prefix("test").Do().WasCreated(&created).Raw() if err != nil { t.Errorf("Unexpected error %#v", err) } if !created { t.Errorf("Expected object to be created") } statusOut, err := runtime.Decode(testapi.Default.Codec(), body) if err != nil { t.Errorf("Unexpected error %#v", err) } if !reflect.DeepEqual(status, statusOut) { t.Errorf("Unexpected mis-match. Expected %#v. Saw %#v", status, statusOut) } fakeHandler.ValidateRequest(t, "/"+testapi.Default.GroupVersion().String()+"/test", "GET", nil) }
func TestExperimentalEncodeDecodeStatus(t *testing.T) { extensionCodec := Extensions.Codec() encoded, err := runtime.Encode(extensionCodec, status) if err != nil { t.Errorf("unexpected error: %v", err) } typeMeta := unversioned.TypeMeta{} if err := json.Unmarshal(encoded, &typeMeta); err != nil { t.Errorf("unexpected error: %v", err) } if typeMeta.Kind != "Status" { t.Errorf("Kind is not set to \"Status\". Got %s", encoded) } if typeMeta.APIVersion != "v1" { t.Errorf("APIVersion is not set to \"\". Got %s", encoded) } decoded, err := runtime.Decode(extensionCodec, encoded) if err != nil { t.Errorf("unexpected error: %v", err) } if !reflect.DeepEqual(status, decoded) { t.Errorf("expected: %v, got: %v", status, decoded) } }
func TestScheme(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) // If set, would clear TypeMeta during conversion. //scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{}) // test that scheme is an ObjectTyper var _ runtime.ObjectTyper = scheme internalToExternalCalls := 0 externalToInternalCalls := 0 // Register functions to verify that scope.Meta() gets set correctly. err := scheme.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } if e, a := externalGV.String(), scope.Meta().DestVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) scope.Convert(&in.TestString, &out.TestString, 0) internalToExternalCalls++ return nil }, func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } if e, a := internalGV.String(), scope.Meta().DestVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) scope.Convert(&in.TestString, &out.TestString, 0) externalToInternalCalls++ return nil }, ) if err != nil { t.Fatalf("unexpected error: %v", err) } codecs := serializer.NewCodecFactory(scheme) codec := codecs.LegacyCodec(externalGV) jsonserializer, _ := codecs.SerializerForFileExtension("json") simple := &InternalSimple{ TestString: "foo", } // Test Encode, Decode, DecodeInto, and DecodeToVersion obj := runtime.Object(simple) data, err := runtime.Encode(codec, obj) if err != nil { t.Fatal(err) } obj2, err := runtime.Decode(codec, data) if err != nil { t.Fatal(err) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if e, a := simple, obj2; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } obj3 := &InternalSimple{} if err := runtime.DecodeInto(codec, data, obj3); err != nil { t.Fatal(err) } // clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion // does not automatically clear TypeMeta anymore). simple.TypeMeta = runtime.TypeMeta{Kind: "Simple", APIVersion: externalGV.String()} if e, a := simple, obj3; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } obj4, err := runtime.Decode(jsonserializer, data) if err != nil { t.Fatal(err) } if _, ok := obj4.(*ExternalSimple); !ok { t.Fatalf("Got wrong type") } // Test Convert external := &ExternalSimple{} err = scheme.Convert(simple, external) if err != nil { t.Fatalf("Unexpected error: %v", err) } if e, a := simple.TestString, external.TestString; e != a { t.Errorf("Expected %v, got %v", e, a) } // Encode and Convert should each have caused an increment. if e, a := 2, internalToExternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } // DecodeInto and Decode should each have caused an increment because of a conversion if e, a := 2, externalToInternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } }
func TestExtensionMapping(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{}) scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) // register external first when the object is the same in both schemes, so ObjectVersionAndKind reports the // external version. scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{}) scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{}) scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{}) scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) table := []struct { obj runtime.Object expected runtime.Object encoded string }{ { &InternalExtensionType{ Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}), }, &InternalExtensionType{ Extension: &runtime.Unknown{ RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`), }, }, // apiVersion is set in the serialized object for easier consumption by clients `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}} `, }, { &InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})}, &InternalExtensionType{ Extension: &runtime.Unknown{ RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`), }, }, // apiVersion is set in the serialized object for easier consumption by clients `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}} `, }, { &InternalExtensionType{Extension: nil}, &InternalExtensionType{ Extension: nil, }, `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":null} `, }, } for i, item := range table { gotEncoded, err := runtime.Encode(codec, item.obj) if err != nil { t.Errorf("unexpected error '%v' (%#v)", err, item.obj) } else if e, a := item.encoded, string(gotEncoded); e != a { t.Errorf("expected\n%#v\ngot\n%#v\n", e, a) } gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) { t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a)) } } }
func TestNestedObject(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") s := runtime.NewScheme() s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) inner := &EmbeddedTest{ ID: "inner", } outer := &EmbeddedTest{ ID: "outer", Object: runtime.NewEncodable(codec, inner), } wire, err := runtime.Encode(codec, outer) if err != nil { t.Fatalf("Unexpected encode error '%v'", err) } t.Logf("Wire format is:\n%v\n", string(wire)) decoded, err := runtime.Decode(codec, wire) if err != nil { t.Fatalf("Unexpected decode error %v", err) } // for later tests outer.Object = inner if e, a := outer, decoded; reflect.DeepEqual(e, a) { t.Errorf("Expected unequal %#v %#v", e, a) } obj, err := runtime.Decode(codec, decoded.(*EmbeddedTest).Object.(*runtime.Unknown).RawJSON) if err != nil { t.Fatal(err) } decoded.(*EmbeddedTest).Object = obj if e, a := outer, decoded; !reflect.DeepEqual(e, a) { t.Errorf("Expected equal %#v %#v", e, a) } // test JSON decoding of the external object, which should preserve // raw bytes var externalViaJSON EmbeddedTestExternal err = json.Unmarshal(wire, &externalViaJSON) if err != nil { t.Fatalf("Unexpected decode error %v", err) } if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.ID != "outer" { t.Errorf("Expected objects to have type info set, got %#v", externalViaJSON) } if !reflect.DeepEqual(externalViaJSON.EmptyObject.RawJSON, []byte("null")) || len(externalViaJSON.Object.RawJSON) == 0 { t.Errorf("Expected deserialization of nested objects into bytes, got %#v", externalViaJSON) } // test JSON decoding, too, since Decode uses yaml unmarshalling. // Generic Unmarshalling of JSON cannot load the nested objects because there is // no default schema set. Consumers wishing to get direct JSON decoding must use // the external representation var decodedViaJSON EmbeddedTest err = json.Unmarshal(wire, &decodedViaJSON) if err == nil || !strings.Contains(err.Error(), "unmarshal object into Go value of type runtime.Object") { t.Fatalf("Unexpected decode error %v", err) } if a := decodedViaJSON; a.Object != nil || a.EmptyObject != nil { t.Errorf("Expected embedded objects to be nil: %#v", a) } }
func TestArrayOfRuntimeObject(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} s := runtime.NewScheme() s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{}) s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{}) codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) innerItems := []runtime.Object{ &EmbeddedTest{ID: "baz"}, } items := []runtime.Object{ &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"}`)}, &ObjectTest{ Items: runtime.NewEncodableList(codec, innerItems), }, } internal := &ObjectTest{ Items: runtime.NewEncodableList(codec, items), } wire, err := runtime.Encode(codec, internal) if err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("Wire format is:\n%s\n", string(wire)) obj := &ObjectTestExternal{} if err := json.Unmarshal(wire, obj); err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON)) items[3] = &ObjectTest{Items: innerItems} internal.Items = items decoded, err := runtime.Decode(codec, wire) if err != nil { t.Fatalf("unexpected error: %v", err) } list, err := meta.ExtractList(decoded) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list, codec); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } list2, err := meta.ExtractList(list[3]) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list2, codec); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := meta.SetList(list[3], list2); err != nil { t.Fatalf("unexpected error: %v", err) } // we want DecodeList to set type meta if possible, even on runtime.Unknown objects internal.Items[2].(*runtime.Unknown).TypeMeta = runtime.TypeMeta{Kind: "OtherTest", APIVersion: "unknown.group/unknown"} if e, a := internal.Items, list; !reflect.DeepEqual(e, a) { t.Errorf("mismatched decoded: %s", util.ObjectGoPrintSideBySide(e, a)) } }
// Get returns the result as an object. func (r Result) Get() (runtime.Object, error) { if r.err != nil { return nil, r.err } return runtime.Decode(r.decoder, r.body) }