123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909 |
- /*
- Copyright 2014 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package runtime_test
- import (
- "reflect"
- "strings"
- "testing"
- "github.com/google/gofuzz"
- flag "github.com/spf13/pflag"
- "k8s.io/kubernetes/pkg/api/unversioned"
- "k8s.io/kubernetes/pkg/conversion"
- "k8s.io/kubernetes/pkg/runtime"
- "k8s.io/kubernetes/pkg/runtime/serializer"
- "k8s.io/kubernetes/pkg/util/diff"
- )
- var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
- type InternalSimple struct {
- runtime.TypeMeta `json:",inline"`
- TestString string `json:"testString"`
- }
- type ExternalSimple struct {
- runtime.TypeMeta `json:",inline"`
- TestString string `json:"testString"`
- }
- func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- func (obj *ExternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- 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 {
- 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 {
- 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, nil)
- 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 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)
- }*/
- }
- type ExtensionA struct {
- runtime.TypeMeta `json:",inline"`
- TestString string `json:"testString"`
- }
- type ExtensionB struct {
- runtime.TypeMeta `json:",inline"`
- TestString string `json:"testString"`
- }
- type ExternalExtensionType struct {
- runtime.TypeMeta `json:",inline"`
- Extension runtime.RawExtension `json:"extension"`
- }
- type InternalExtensionType struct {
- runtime.TypeMeta `json:",inline"`
- Extension runtime.Object `json:"extension"`
- }
- type ExternalOptionalExtensionType struct {
- runtime.TypeMeta `json:",inline"`
- Extension runtime.RawExtension `json:"extension,omitempty"`
- }
- type InternalOptionalExtensionType struct {
- runtime.TypeMeta `json:",inline"`
- Extension runtime.Object `json:"extension,omitempty"`
- }
- func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- func (obj *ExternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- func (obj *InternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- 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, diff.ObjectGoPrintSideBySide(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{
- 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
- `{"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{
- 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
- `{"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, diff.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 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.Fatalf("type not unversioned and in scheme: %t %t", unv, ok)
- }
- kinds, _, err := scheme.ObjectKinds(&InternalSimple{})
- if err != nil {
- t.Fatal(err)
- }
- kind := kinds[0]
- 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)
- }
- }
- // Test a weird version/kind embedding format.
- type MyWeirdCustomEmbeddedVersionKindField struct {
- ID string `json:"ID,omitempty"`
- APIVersion string `json:"myVersionKey,omitempty"`
- ObjectKind string `json:"myKindKey,omitempty"`
- Z string `json:"Z,omitempty"`
- Y uint64 `json:"Y,omitempty"`
- }
- type TestType1 struct {
- MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
- A string `json:"A,omitempty"`
- B int `json:"B,omitempty"`
- C int8 `json:"C,omitempty"`
- D int16 `json:"D,omitempty"`
- E int32 `json:"E,omitempty"`
- F int64 `json:"F,omitempty"`
- G uint `json:"G,omitempty"`
- H uint8 `json:"H,omitempty"`
- I uint16 `json:"I,omitempty"`
- J uint32 `json:"J,omitempty"`
- K uint64 `json:"K,omitempty"`
- L bool `json:"L,omitempty"`
- M map[string]int `json:"M,omitempty"`
- N map[string]TestType2 `json:"N,omitempty"`
- O *TestType2 `json:"O,omitempty"`
- P []TestType2 `json:"Q,omitempty"`
- }
- type TestType2 struct {
- A string `json:"A,omitempty"`
- B int `json:"B,omitempty"`
- }
- type ExternalTestType2 struct {
- A string `json:"A,omitempty"`
- B int `json:"B,omitempty"`
- }
- type ExternalTestType1 struct {
- MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
- A string `json:"A,omitempty"`
- B int `json:"B,omitempty"`
- C int8 `json:"C,omitempty"`
- D int16 `json:"D,omitempty"`
- E int32 `json:"E,omitempty"`
- F int64 `json:"F,omitempty"`
- G uint `json:"G,omitempty"`
- H uint8 `json:"H,omitempty"`
- I uint16 `json:"I,omitempty"`
- J uint32 `json:"J,omitempty"`
- K uint64 `json:"K,omitempty"`
- L bool `json:"L,omitempty"`
- M map[string]int `json:"M,omitempty"`
- N map[string]ExternalTestType2 `json:"N,omitempty"`
- O *ExternalTestType2 `json:"O,omitempty"`
- P []ExternalTestType2 `json:"Q,omitempty"`
- }
- type ExternalInternalSame struct {
- MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
- A TestType2 `json:"A,omitempty"`
- }
- type UnversionedType struct {
- MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
- A string `json:"A,omitempty"`
- }
- type UnknownType struct {
- MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
- A string `json:"A,omitempty"`
- }
- func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj }
- func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
- obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
- }
- func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() unversioned.GroupVersionKind {
- return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
- }
- func (obj *ExternalInternalSame) GetObjectKind() unversioned.ObjectKind {
- return &obj.MyWeirdCustomEmbeddedVersionKindField
- }
- func (obj *TestType1) GetObjectKind() unversioned.ObjectKind {
- return &obj.MyWeirdCustomEmbeddedVersionKindField
- }
- func (obj *ExternalTestType1) GetObjectKind() unversioned.ObjectKind {
- return &obj.MyWeirdCustomEmbeddedVersionKindField
- }
- func (obj *TestType2) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
- func (obj *ExternalTestType2) GetObjectKind() unversioned.ObjectKind {
- return unversioned.EmptyObjectKind
- }
- // TestObjectFuzzer can randomly populate all the above objects.
- var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
- func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) {
- // We have to customize the randomization of MyWeirdCustomEmbeddedVersionKindFields because their
- // APIVersion and Kind must remain blank in memory.
- j.APIVersion = ""
- j.ObjectKind = ""
- j.ID = c.RandString()
- },
- )
- // Returns a new Scheme set up with the test objects.
- func GetTestScheme() *runtime.Scheme {
- internalGV := unversioned.GroupVersion{Version: "__internal"}
- externalGV := unversioned.GroupVersion{Version: "v1"}
- alternateExternalGV := unversioned.GroupVersion{Group: "custom", Version: "v1"}
- differentExternalGV := unversioned.GroupVersion{Group: "other", 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(externalGV.WithKind("TestType4"), &ExternalTestType1{})
- s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType3"), &ExternalTestType1{})
- s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType5"), &ExternalTestType1{})
- s.AddKnownTypeWithName(differentExternalGV.WithKind("TestType1"), &ExternalTestType1{})
- s.AddUnversionedTypes(externalGV, &UnversionedType{})
- return s
- }
- func TestKnownTypes(t *testing.T) {
- s := GetTestScheme()
- if len(s.KnownTypes(unversioned.GroupVersion{Group: "group", Version: "v2"})) != 0 {
- t.Errorf("should have no known types for v2")
- }
- types := s.KnownTypes(unversioned.GroupVersion{Version: "v1"})
- for _, s := range []string{"TestType1", "TestType2", "TestType3", "ExternalInternalSame"} {
- if _, ok := types[s]; !ok {
- t.Errorf("missing type %q", s)
- }
- }
- }
- func TestConvertToVersionBasic(t *testing.T) {
- s := GetTestScheme()
- tt := &TestType1{A: "I'm not a pointer object"}
- other, err := s.ConvertToVersion(tt, unversioned.GroupVersion{Version: "v1"})
- if err != nil {
- t.Fatalf("Failure: %v", err)
- }
- converted, ok := other.(*ExternalTestType1)
- if !ok {
- t.Fatalf("Got wrong type: %T", other)
- }
- if tt.A != converted.A {
- t.Fatalf("Failed to convert object correctly: %#v", converted)
- }
- }
- type testGroupVersioner struct {
- target unversioned.GroupVersionKind
- ok bool
- }
- func (m testGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
- return m.target, m.ok
- }
- func TestConvertToVersion(t *testing.T) {
- testCases := []struct {
- scheme *runtime.Scheme
- in runtime.Object
- gv runtime.GroupVersioner
- same bool
- out runtime.Object
- errFn func(error) bool
- }{
- // errors if the type is not registered in the scheme
- {
- scheme: GetTestScheme(),
- in: &UnknownType{},
- errFn: func(err error) bool { return err != nil && runtime.IsNotRegisteredError(err) },
- },
- // errors if the group versioner returns no target
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: testGroupVersioner{},
- errFn: func(err error) bool {
- return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
- },
- },
- // converts to internal
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: unversioned.GroupVersion{Version: "__internal"},
- out: &TestType1{A: "test"},
- },
- // prefers the first group version in the list
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: unversioned.GroupVersions{{Version: "__internal"}, {Version: "v1"}},
- out: &TestType1{A: "test"},
- },
- // unversioned type returned as-is
- {
- scheme: GetTestScheme(),
- in: &UnversionedType{A: "test"},
- gv: unversioned.GroupVersions{{Version: "v1"}},
- same: true,
- out: &UnversionedType{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
- A: "test",
- },
- },
- // detected as already being in the target version
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: unversioned.GroupVersions{{Version: "v1"}},
- same: true,
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
- A: "test",
- },
- },
- // detected as already being in the first target version
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: unversioned.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
- same: true,
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
- A: "test",
- },
- },
- // detected as already being in the first target version
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: unversioned.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
- same: true,
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
- A: "test",
- },
- },
- // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (1/3): different kind
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType3", Version: "v1"}},
- same: true,
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
- A: "test",
- },
- },
- // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (2/3): different gv
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType3", Group: "custom", Version: "v1"}},
- same: true,
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType3"},
- A: "test",
- },
- },
- // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (3/3): different gvk
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Group: "custom", Version: "v1", Kind: "TestType5"}},
- same: true,
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
- A: "test",
- },
- },
- // multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "other", Version: "v2"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}, unversioned.GroupKind{Kind: "TestType1"}),
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
- A: "test",
- },
- },
- // multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "other", Version: "v2"}, unversioned.GroupKind{Kind: "TestType1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}),
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
- A: "test",
- },
- },
- // multi group versioner is unable to find a match when kind AND group don't match (there is no TestType1 kind in group "other", and no kind "TestType5" in the default group)
- {
- scheme: GetTestScheme(),
- in: &TestType1{A: "test"},
- gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "custom", Version: "v1"}, unversioned.GroupKind{Group: "other"}, unversioned.GroupKind{Kind: "TestType5"}),
- errFn: func(err error) bool {
- return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
- },
- },
- // multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "", Version: "v1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}, unversioned.GroupKind{Kind: "TestType1"}),
- same: true,
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
- A: "test",
- },
- },
- // multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
- {
- scheme: GetTestScheme(),
- in: &ExternalTestType1{A: "test"},
- gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "", Version: "v1"}, unversioned.GroupKind{Kind: "TestType1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}),
- same: true,
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
- A: "test",
- },
- },
- // group versioner can choose a particular target kind for a given input when kind is the same across group versions
- {
- scheme: GetTestScheme(),
- in: &TestType1{A: "test"},
- gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Version: "v1", Kind: "TestType3"}},
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
- A: "test",
- },
- },
- // group versioner can choose a different kind
- {
- scheme: GetTestScheme(),
- in: &TestType1{A: "test"},
- gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType5", Group: "custom", Version: "v1"}},
- out: &ExternalTestType1{
- MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
- A: "test",
- },
- },
- }
- for i, test := range testCases {
- original, _ := test.scheme.DeepCopy(test.in)
- out, err := test.scheme.ConvertToVersion(test.in, test.gv)
- switch {
- case test.errFn != nil:
- if !test.errFn(err) {
- t.Errorf("%d: unexpected error: %v", i, err)
- }
- continue
- case err != nil:
- t.Errorf("%d: unexpected error: %v", i, err)
- continue
- }
- if out == test.in {
- t.Errorf("%d: ConvertToVersion should always copy out: %#v", i, out)
- continue
- }
- if test.same {
- if !reflect.DeepEqual(original, test.in) {
- t.Errorf("%d: unexpected mutation of input: %s", i, diff.ObjectReflectDiff(original, test.in))
- continue
- }
- if !reflect.DeepEqual(out, test.out) {
- t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
- continue
- }
- unsafe, err := test.scheme.UnsafeConvertToVersion(test.in, test.gv)
- if err != nil {
- t.Errorf("%d: unexpected error: %v", i, err)
- continue
- }
- if !reflect.DeepEqual(unsafe, test.out) {
- t.Errorf("%d: unexpected unsafe: %s", i, diff.ObjectReflectDiff(unsafe, test.out))
- continue
- }
- if unsafe != test.in {
- t.Errorf("%d: UnsafeConvertToVersion should return same object: %#v", i, unsafe)
- continue
- }
- continue
- }
- if !reflect.DeepEqual(out, test.out) {
- t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
- continue
- }
- }
- }
- 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")
- scope.Convert(&in.TestString, &out.TestString, 0)
- internalToExternalCalls++
- return nil
- },
- func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
- t.Logf("external -> internal")
- 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)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- internal, err := s.ConvertToVersion(out, internalGV)
- 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 TestMetaValuesUnregisteredConvert(t *testing.T) {
- type InternalSimple struct {
- Version string `json:"apiVersion,omitempty"`
- Kind string `json:"kind,omitempty"`
- TestString string `json:"testString"`
- }
- type ExternalSimple struct {
- Version string `json:"apiVersion,omitempty"`
- Kind string `json:"kind,omitempty"`
- TestString string `json:"testString"`
- }
- s := runtime.NewScheme()
- // We deliberately don't register the types.
- internalToExternalCalls := 0
- // Register functions to verify that scope.Meta() gets set correctly.
- err := s.AddConversionFuncs(
- func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
- scope.Convert(&in.TestString, &out.TestString, 0)
- internalToExternalCalls++
- return nil
- },
- )
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- simple := &InternalSimple{TestString: "foo"}
- external := &ExternalSimple{}
- err = s.Convert(simple, external, nil)
- 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)
- }
- // Verify that our conversion handler got called.
- if e, a := 1, internalToExternalCalls; e != a {
- t.Errorf("Expected %v, got %v", e, a)
- }
- }
|