func TestKindToResource(t *testing.T) { testCases := []struct { Kind string MixedCase bool Plural, Singular string }{ {Kind: "Pod", MixedCase: true, Plural: "pods", Singular: "pod"}, {Kind: "Pod", MixedCase: true, Plural: "pods", Singular: "pod"}, {Kind: "Pod", MixedCase: false, Plural: "pods", Singular: "pod"}, {Kind: "ReplicationController", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"}, {Kind: "ReplicationController", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"}, {Kind: "ReplicationController", MixedCase: false, Plural: "replicationcontrollers", Singular: "replicationcontroller"}, // Add "ies" when ending with "y" {Kind: "ImageRepository", MixedCase: true, Plural: "imageRepositories", Singular: "imageRepository"}, // Add "es" when ending with "s" {Kind: "miss", MixedCase: false, Plural: "misses", Singular: "miss"}, // Add "s" otherwise {Kind: "lowercase", MixedCase: false, Plural: "lowercases", Singular: "lowercase"}, } for i, testCase := range testCases { version := unversioned.GroupVersion{} plural, singular := KindToResource(version.WithKind(testCase.Kind), testCase.MixedCase) if singular != version.WithResource(testCase.Singular) || plural != version.WithResource(testCase.Plural) { t.Errorf("%d: unexpected plural and singular: %v %v", i, plural, singular) } } }
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} externalGVK := externalGV.WithKind("ObjectTest") s := runtime.NewScheme() s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{}) codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil) if err != nil { 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) != "{}" { t.Fatalf("unexpected object: %#v", test.Items[0]) } if *gvk != externalGVK { t.Fatalf("unexpected kind: %#v", gvk) } obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil) if err != nil { 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"}` { t.Fatalf("unexpected object: %#v", test.Items[0]) } if *gvk != externalGVK { t.Fatalf("unexpected kind: %#v", gvk) } }
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 TestEncode(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{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) obj2, gvk, err2 := codec.Decode(data, nil, nil) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) } if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) { t.Errorf("unexpected gvk returned by decode: %#v", gvk) } }
func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) { expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"} expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"} expectedGroupVersion3 := unversioned.GroupVersion{Group: "tgroup", Version: "test3"} internalObjectGK := unversioned.GroupKind{Group: "tgroup", Kind: "InternalObject"} otherObjectGK := unversioned.GroupKind{Group: "tgroup", Kind: "OtherObject"} mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, fakeInterfaces) mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace, false) mapper.Add(expectedGroupVersion2.WithKind("OtherObject"), RESTScopeNamespace, false) // pick default matching object kind based on search order mapping, err := mapper.RESTMapping(otherObjectGK) if err != nil { t.Fatalf("unexpected error: %v", err) } if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 { t.Errorf("unexpected mapping: %#v", mapping) } mapping, err = mapper.RESTMapping(internalObjectGK) if err != nil { t.Fatalf("unexpected error: %v", err) } if mapping.Resource != "internalobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion1 { t.Errorf("unexpected mapping: %#v", mapping) } // mismatch of version mapping, err = mapper.RESTMapping(internalObjectGK, expectedGroupVersion2.Version) if err == nil { t.Errorf("unexpected non-error") } mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion1.Version) if err == nil { t.Errorf("unexpected non-error") } // not in the search versions mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version) if err == nil { t.Errorf("unexpected non-error") } // explicit search order mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version, expectedGroupVersion1.Version) if err == nil { t.Errorf("unexpected non-error") } mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version, expectedGroupVersion2.Version) if err != nil { t.Fatalf("unexpected error: %v", err) } if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 { t.Errorf("unexpected mapping: %#v", mapping) } }
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) { expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"} expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"} internalObjectGK := unversioned.GroupKind{Group: "tgroup", Kind: "InternalObject"} mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, unmatchedVersionInterfaces) mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace, false) _, err := mapper.RESTMapping(internalObjectGK, expectedGroupVersion1.Version) if err == nil { t.Errorf("unexpected non-error") } }
// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules. // Whenever an object of this type is serialized, it is serialized with the provided group version and is not // converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an // API group and version that would never be updated. // // TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into // every version with particular schemas. Resolve tihs method at that point. func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...Object) { s.AddKnownTypes(version, types...) for _, obj := range types { t := reflect.TypeOf(obj).Elem() gvk := version.WithKind(t.Name()) s.unversionedTypes[t] = gvk if _, ok := s.unversionedKinds[gvk.Kind]; ok { panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind)) } s.unversionedKinds[gvk.Kind] = t } }
func TestUnversionedTypes(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} otherGV := unversioned.GroupVersion{Group: "group", Version: "other"} scheme := runtime.NewScheme() scheme.AddUnversionedTypes(externalGV, &InternalSimple{}) scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok { t.Fatal("type not unversioned and in scheme: %t %t", unv, ok) } kind, err := scheme.ObjectKind(&InternalSimple{}) if err != nil { t.Fatal(err) } if kind != externalGV.WithKind("InternalSimple") { t.Fatalf("unexpected: %#v", kind) } test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) if err != nil { t.Fatal(err) } obj2, gvk, err := codec.Decode(data, nil, nil) if err != nil { t.Fatal(err) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) } // object is serialized as an unversioned object (in the group and version it was defined in) if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) { t.Errorf("unexpected gvk returned by decode: %#v", gvk) } // when serialized to a different group, the object is kept in its preferred name codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV) data, err = runtime.Encode(codec, obj) if err != nil { t.Fatal(err) } if string(data) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" { t.Errorf("unexpected data: %s", data) } }
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'. // All objects passed to types should be pointers to structs. The name that go reports for // the struct becomes the "kind" field when encoding. Version may not be empty - use the // APIVersionInternal constant if you have a type that does not have a formal version. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { if len(gv.Version) == 0 { panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0])) } for _, obj := range types { t := reflect.TypeOf(obj) if t.Kind() != reflect.Ptr { panic("All types must be pointers to structs.") } t = t.Elem() if t.Kind() != reflect.Struct { panic("All types must be pointers to structs.") } gvk := gv.WithKind(t.Name()) s.gvkToType[gvk] = t s.typeToGVK[t] = append(s.typeToGVK[t], gvk) } }
// DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then // converts that object to into (if necessary). Returns an error if the operation cannot be completed. func (c *parameterCodec) DecodeParameters(parameters url.Values, from unversioned.GroupVersion, into Object) error { if len(parameters) == 0 { return nil } targetGVK, _, err := c.typer.ObjectKind(into) if err != nil { return err } if targetGVK.GroupVersion() == from { return c.convertor.Convert(¶meters, into) } input, err := c.creator.New(from.WithKind(targetGVK.Kind)) if err != nil { return err } if err := c.convertor.Convert(¶meters, input); err != nil { return err } return c.convertor.Convert(input, into) }
func TestRESTMapperResourceSingularizer(t *testing.T) { testGroupVersion := unversioned.GroupVersion{Group: "tgroup", Version: "test"} testCases := []struct { Kind string MixedCase bool Plural string Singular string }{ {Kind: "Pod", MixedCase: true, Plural: "pods", Singular: "pod"}, {Kind: "Pod", MixedCase: false, Plural: "pods", Singular: "pod"}, {Kind: "ReplicationController", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"}, {Kind: "ReplicationController", MixedCase: false, Plural: "replicationcontrollers", Singular: "replicationcontroller"}, {Kind: "ImageRepository", MixedCase: true, Plural: "imageRepositories", Singular: "imageRepository"}, {Kind: "ImageRepository", MixedCase: false, Plural: "imagerepositories", Singular: "imagerepository"}, {Kind: "Status", MixedCase: true, Plural: "statuses", Singular: "status"}, {Kind: "Status", MixedCase: false, Plural: "statuses", Singular: "status"}, {Kind: "lowercase", MixedCase: false, Plural: "lowercases", Singular: "lowercase"}, // TODO this test is broken. This updates to reflect actual behavior. Kinds are expected to be singular // old (incorrect), coment: Don't add extra s if the original object is already plural {Kind: "lowercases", MixedCase: false, Plural: "lowercaseses", Singular: "lowercases"}, } for i, testCase := range testCases { mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{testGroupVersion}, fakeInterfaces) // create singular/plural mapping mapper.Add(testGroupVersion.WithKind(testCase.Kind), RESTScopeNamespace, testCase.MixedCase) singular, err := mapper.ResourceSingularizer(testCase.Plural) if err != nil { t.Errorf("%d: unexpected error: %v", i, err) } if singular != testCase.Singular { t.Errorf("%d: mismatched singular: got %v, expected %v", i, singular, testCase.Singular) } } }
// TestDeepCopyOfRuntimeObject checks to make sure that runtime.Objects's can be passed through DeepCopy with fidelity func TestDeepCopyOfRuntimeObject(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{}) original := &EmbeddedTest{ ID: "outer", Object: &EmbeddedTest{ ID: "inner", }, } codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) originalData, err := runtime.Encode(codec, original) if err != nil { t.Errorf("unexpected error: %v", err) } t.Logf("originalRole = %v\n", string(originalData)) copyOfOriginal, err := s.DeepCopy(original) if err != nil { t.Fatalf("unexpected error: %v", err) } copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object)) if err != nil { t.Errorf("unexpected error: %v", err) } t.Logf("copyOfRole = %v\n", string(copiedData)) if !reflect.DeepEqual(original, copyOfOriginal) { t.Errorf("expected \n%v\n, got \n%v", string(originalData), string(copiedData)) } }
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) } }
// Returns a new Scheme set up with the test objects. func GetTestScheme() (*runtime.Scheme, runtime.Codec) { internalGV := unversioned.GroupVersion{Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Version: "v1"} externalGV2 := unversioned.GroupVersion{Version: "v2"} s := runtime.NewScheme() // Ordinarily, we wouldn't add TestType2, but because this is a test and // both types are from the same package, we need to get it into the system // so that converter will match it with ExternalType2. s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{}) s.AddKnownTypes(externalGV, &ExternalInternalSame{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{}) s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{}) s.AddKnownTypeWithName(externalGV2.WithKind("TestType1"), &ExternalTestType1{}) s.AddUnversionedTypes(externalGV, &unversioned.Status{}) cf := newCodecFactory(s, testMetaFactory{}) codec := cf.LegacyCodec(unversioned.GroupVersion{Version: "v1"}) return s, codec }
// Returns a new Scheme set up with the test objects. func GetTestScheme() *runtime.Scheme { internalGV := unversioned.GroupVersion{Version: "__internal"} externalGV := unversioned.GroupVersion{Version: "v1"} s := runtime.NewScheme() // Ordinarily, we wouldn't add TestType2, but because this is a test and // both types are from the same package, we need to get it into the system // so that converter will match it with ExternalType2. s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{}) s.AddKnownTypes(externalGV, &ExternalInternalSame{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{}) s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{}) return s }
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 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)) } }
func TestStringMapConversion(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"} scheme := runtime.NewScheme() scheme.Log(t) scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{}) testCases := map[string]struct { input map[string][]string errFn func(error) bool expected runtime.Object }{ "ignores omitempty": { input: map[string][]string{ "String": {"not_used"}, "string": {"value"}, "int": {"1"}, "Integer64": {"2"}, }, expected: &ExternalComplex{String: "value", Integer: 1}, }, "returns error on bad int": { input: map[string][]string{ "int": {"a"}, }, errFn: func(err error) bool { return err != nil }, expected: &ExternalComplex{}, }, "parses int64": { input: map[string][]string{ "Int64": {"-1"}, }, expected: &ExternalComplex{Int64: -1}, }, "returns error on bad int64": { input: map[string][]string{ "Int64": {"a"}, }, errFn: func(err error) bool { return err != nil }, expected: &ExternalComplex{}, }, "parses boolean true": { input: map[string][]string{ "bool": {"true"}, }, expected: &ExternalComplex{Bool: true}, }, "parses boolean any value": { input: map[string][]string{ "bool": {"foo"}, }, expected: &ExternalComplex{Bool: true}, }, "parses boolean false": { input: map[string][]string{ "bool": {"false"}, }, expected: &ExternalComplex{Bool: false}, }, "parses boolean empty value": { input: map[string][]string{ "bool": {""}, }, expected: &ExternalComplex{Bool: true}, }, "parses boolean no value": { input: map[string][]string{ "bool": {}, }, expected: &ExternalComplex{Bool: false}, }, } for k, tc := range testCases { out := &ExternalComplex{} if err := scheme.Convert(&tc.input, out); (tc.errFn == nil && err != nil) || (tc.errFn != nil && !tc.errFn(err)) { t.Errorf("%s: unexpected error: %v", k, err) continue } else if err != nil { continue } if !reflect.DeepEqual(out, tc.expected) { t.Errorf("%s: unexpected output: %#v", k, out) } } }
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 TestRESTMapperVersionAndKindForResource(t *testing.T) { testGroup := "test.group" testVersion := "test" testGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: testVersion} testCases := []struct { Resource unversioned.GroupVersionResource GroupVersionToRegister unversioned.GroupVersion ExpectedGVK unversioned.GroupVersionKind MixedCase bool Err bool }{ {Resource: unversioned.GroupVersionResource{Resource: "internalobjec"}, Err: true}, {Resource: unversioned.GroupVersionResource{Resource: "internalObjec"}, Err: true}, {Resource: unversioned.GroupVersionResource{Resource: "internalobject"}, ExpectedGVK: testGroupVersion.WithKind("InternalObject")}, {Resource: unversioned.GroupVersionResource{Resource: "internalobjects"}, ExpectedGVK: testGroupVersion.WithKind("InternalObject")}, {Resource: unversioned.GroupVersionResource{Resource: "internalobject"}, MixedCase: true, ExpectedGVK: testGroupVersion.WithKind("InternalObject")}, {Resource: unversioned.GroupVersionResource{Resource: "internalobjects"}, MixedCase: true, ExpectedGVK: testGroupVersion.WithKind("InternalObject")}, {Resource: unversioned.GroupVersionResource{Resource: "internalObject"}, MixedCase: true, ExpectedGVK: testGroupVersion.WithKind("InternalObject")}, {Resource: unversioned.GroupVersionResource{Resource: "internalObjects"}, MixedCase: true, ExpectedGVK: testGroupVersion.WithKind("InternalObject")}, } for i, testCase := range testCases { mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{testGroupVersion}, fakeInterfaces) if len(testCase.ExpectedGVK.Kind) != 0 { mapper.Add(testCase.ExpectedGVK, RESTScopeNamespace, testCase.MixedCase) } actualGVK, err := mapper.KindFor(testCase.Resource) hasErr := err != nil if hasErr != testCase.Err { t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err) continue } if err != nil { continue } if actualGVK != testCase.ExpectedGVK { t.Errorf("%d: unexpected version and kind: e=%s a=%s", i, testCase.ExpectedGVK, actualGVK) } } }
func TestMetaValues(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"} s := runtime.NewScheme() s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) internalToExternalCalls := 0 externalToInternalCalls := 0 // Register functions to verify that scope.Meta() gets set correctly. err := s.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { t.Logf("internal -> external") if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a { t.Fatalf("Expected '%v', got '%v'", e, a) } if e, a := externalGV.String(), scope.Meta().DestVersion; e != a { t.Fatalf("Expected '%v', got '%v'", e, a) } scope.Convert(&in.TestString, &out.TestString, 0) internalToExternalCalls++ return nil }, func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { t.Logf("external -> internal") 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.Fatalf("Expected '%v', got '%v'", e, a) } scope.Convert(&in.TestString, &out.TestString, 0) externalToInternalCalls++ return nil }, ) if err != nil { t.Fatalf("unexpected error: %v", err) } simple := &InternalSimple{ TestString: "foo", } s.Log(t) out, err := s.ConvertToVersion(simple, externalGV.String()) if err != nil { t.Fatalf("unexpected error: %v", err) } internal, err := s.ConvertToVersion(out, internalGV.String()) if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := simple, internal; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } if e, a := 1, internalToExternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } if e, a := 1, externalToInternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } }
func TestRESTMapperRESTMapping(t *testing.T) { testGroup := "tgroup" testGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: "test"} internalGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: "test"} testCases := []struct { Kind string APIGroupVersions []unversioned.GroupVersion MixedCase bool DefaultVersions []unversioned.GroupVersion Resource string ExpectedGroupVersion *unversioned.GroupVersion Err bool }{ {Kind: "Unknown", Err: true}, {Kind: "InternalObject", Err: true}, {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "Unknown", Err: true}, {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"}, {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"}, {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"}, {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{}, Resource: "internalobjects", ExpectedGroupVersion: &unversioned.GroupVersion{Group: testGroup, Version: "test"}}, {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"}, {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, MixedCase: true, Resource: "internalObjects"}, // TODO: add test for a resource that exists in one version but not another } for i, testCase := range testCases { mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces) mapper.Add(internalGroupVersion.WithKind("InternalObject"), RESTScopeNamespace, testCase.MixedCase) preferredVersions := []string{} for _, gv := range testCase.APIGroupVersions { preferredVersions = append(preferredVersions, gv.Version) } gk := unversioned.GroupKind{Group: testGroup, Kind: testCase.Kind} mapping, err := mapper.RESTMapping(gk, preferredVersions...) hasErr := err != nil if hasErr != testCase.Err { t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err) } if hasErr { continue } if mapping.Resource != testCase.Resource { t.Errorf("%d: unexpected resource: %#v", i, mapping) } if mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil { t.Errorf("%d: missing codec and accessor: %#v", i, mapping) } groupVersion := testCase.ExpectedGroupVersion if groupVersion == nil { groupVersion = &testCase.APIGroupVersions[0] } if mapping.GroupVersionKind.GroupVersion() != *groupVersion { t.Errorf("%d: unexpected version: %#v", i, mapping) } } }