scheme_test.go 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package runtime_test
  14. import (
  15. "reflect"
  16. "strings"
  17. "testing"
  18. "github.com/google/gofuzz"
  19. flag "github.com/spf13/pflag"
  20. "k8s.io/kubernetes/pkg/api/unversioned"
  21. "k8s.io/kubernetes/pkg/conversion"
  22. "k8s.io/kubernetes/pkg/runtime"
  23. "k8s.io/kubernetes/pkg/runtime/serializer"
  24. "k8s.io/kubernetes/pkg/util/diff"
  25. )
  26. var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
  27. type InternalSimple struct {
  28. runtime.TypeMeta `json:",inline"`
  29. TestString string `json:"testString"`
  30. }
  31. type ExternalSimple struct {
  32. runtime.TypeMeta `json:",inline"`
  33. TestString string `json:"testString"`
  34. }
  35. func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  36. func (obj *ExternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  37. func TestScheme(t *testing.T) {
  38. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  39. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
  40. scheme := runtime.NewScheme()
  41. scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
  42. scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
  43. // If set, would clear TypeMeta during conversion.
  44. //scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{})
  45. // test that scheme is an ObjectTyper
  46. var _ runtime.ObjectTyper = scheme
  47. internalToExternalCalls := 0
  48. externalToInternalCalls := 0
  49. // Register functions to verify that scope.Meta() gets set correctly.
  50. err := scheme.AddConversionFuncs(
  51. func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
  52. scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
  53. scope.Convert(&in.TestString, &out.TestString, 0)
  54. internalToExternalCalls++
  55. return nil
  56. },
  57. func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
  58. scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
  59. scope.Convert(&in.TestString, &out.TestString, 0)
  60. externalToInternalCalls++
  61. return nil
  62. },
  63. )
  64. if err != nil {
  65. t.Fatalf("unexpected error: %v", err)
  66. }
  67. codecs := serializer.NewCodecFactory(scheme)
  68. codec := codecs.LegacyCodec(externalGV)
  69. jsonserializer, _ := codecs.SerializerForFileExtension("json")
  70. simple := &InternalSimple{
  71. TestString: "foo",
  72. }
  73. // Test Encode, Decode, DecodeInto, and DecodeToVersion
  74. obj := runtime.Object(simple)
  75. data, err := runtime.Encode(codec, obj)
  76. if err != nil {
  77. t.Fatal(err)
  78. }
  79. obj2, err := runtime.Decode(codec, data)
  80. if err != nil {
  81. t.Fatal(err)
  82. }
  83. if _, ok := obj2.(*InternalSimple); !ok {
  84. t.Fatalf("Got wrong type")
  85. }
  86. if e, a := simple, obj2; !reflect.DeepEqual(e, a) {
  87. t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
  88. }
  89. obj3 := &InternalSimple{}
  90. if err := runtime.DecodeInto(codec, data, obj3); err != nil {
  91. t.Fatal(err)
  92. }
  93. // clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion
  94. // does not automatically clear TypeMeta anymore).
  95. simple.TypeMeta = runtime.TypeMeta{Kind: "Simple", APIVersion: externalGV.String()}
  96. if e, a := simple, obj3; !reflect.DeepEqual(e, a) {
  97. t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
  98. }
  99. obj4, err := runtime.Decode(jsonserializer, data)
  100. if err != nil {
  101. t.Fatal(err)
  102. }
  103. if _, ok := obj4.(*ExternalSimple); !ok {
  104. t.Fatalf("Got wrong type")
  105. }
  106. // Test Convert
  107. external := &ExternalSimple{}
  108. err = scheme.Convert(simple, external, nil)
  109. if err != nil {
  110. t.Fatalf("Unexpected error: %v", err)
  111. }
  112. if e, a := simple.TestString, external.TestString; e != a {
  113. t.Errorf("Expected %v, got %v", e, a)
  114. }
  115. // Encode and Convert should each have caused an increment.
  116. if e, a := 2, internalToExternalCalls; e != a {
  117. t.Errorf("Expected %v, got %v", e, a)
  118. }
  119. // DecodeInto and Decode should each have caused an increment because of a conversion
  120. if e, a := 2, externalToInternalCalls; e != a {
  121. t.Errorf("Expected %v, got %v", e, a)
  122. }
  123. }
  124. func TestBadJSONRejection(t *testing.T) {
  125. scheme := runtime.NewScheme()
  126. codecs := serializer.NewCodecFactory(scheme)
  127. jsonserializer, _ := codecs.SerializerForFileExtension("json")
  128. badJSONMissingKind := []byte(`{ }`)
  129. if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil {
  130. t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
  131. }
  132. badJSONUnknownType := []byte(`{"kind": "bar"}`)
  133. if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil {
  134. t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
  135. }
  136. /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
  137. if err2 := DecodeInto(badJSONKindMismatch, &Minion{}); err2 == nil {
  138. t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
  139. }*/
  140. }
  141. type ExtensionA struct {
  142. runtime.TypeMeta `json:",inline"`
  143. TestString string `json:"testString"`
  144. }
  145. type ExtensionB struct {
  146. runtime.TypeMeta `json:",inline"`
  147. TestString string `json:"testString"`
  148. }
  149. type ExternalExtensionType struct {
  150. runtime.TypeMeta `json:",inline"`
  151. Extension runtime.RawExtension `json:"extension"`
  152. }
  153. type InternalExtensionType struct {
  154. runtime.TypeMeta `json:",inline"`
  155. Extension runtime.Object `json:"extension"`
  156. }
  157. type ExternalOptionalExtensionType struct {
  158. runtime.TypeMeta `json:",inline"`
  159. Extension runtime.RawExtension `json:"extension,omitempty"`
  160. }
  161. type InternalOptionalExtensionType struct {
  162. runtime.TypeMeta `json:",inline"`
  163. Extension runtime.Object `json:"extension,omitempty"`
  164. }
  165. func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  166. func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  167. func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  168. func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  169. func (obj *ExternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  170. func (obj *InternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
  171. func TestExternalToInternalMapping(t *testing.T) {
  172. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  173. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
  174. scheme := runtime.NewScheme()
  175. scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
  176. scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
  177. codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
  178. table := []struct {
  179. obj runtime.Object
  180. encoded string
  181. }{
  182. {
  183. &InternalOptionalExtensionType{Extension: nil},
  184. `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`,
  185. },
  186. }
  187. for i, item := range table {
  188. gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
  189. if err != nil {
  190. t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
  191. } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) {
  192. t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a))
  193. }
  194. }
  195. }
  196. func TestExtensionMapping(t *testing.T) {
  197. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  198. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
  199. scheme := runtime.NewScheme()
  200. scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{})
  201. scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
  202. scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{})
  203. scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
  204. // register external first when the object is the same in both schemes, so ObjectVersionAndKind reports the
  205. // external version.
  206. scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{})
  207. scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{})
  208. scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{})
  209. scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{})
  210. codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
  211. table := []struct {
  212. obj runtime.Object
  213. expected runtime.Object
  214. encoded string
  215. }{
  216. {
  217. &InternalExtensionType{
  218. Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}),
  219. },
  220. &InternalExtensionType{
  221. Extension: &runtime.Unknown{
  222. Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`),
  223. ContentType: runtime.ContentTypeJSON,
  224. },
  225. },
  226. // apiVersion is set in the serialized object for easier consumption by clients
  227. `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}}
  228. `,
  229. }, {
  230. &InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})},
  231. &InternalExtensionType{
  232. Extension: &runtime.Unknown{
  233. Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`),
  234. ContentType: runtime.ContentTypeJSON,
  235. },
  236. },
  237. // apiVersion is set in the serialized object for easier consumption by clients
  238. `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}}
  239. `,
  240. }, {
  241. &InternalExtensionType{Extension: nil},
  242. &InternalExtensionType{
  243. Extension: nil,
  244. },
  245. `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":null}
  246. `,
  247. },
  248. }
  249. for i, item := range table {
  250. gotEncoded, err := runtime.Encode(codec, item.obj)
  251. if err != nil {
  252. t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
  253. } else if e, a := item.encoded, string(gotEncoded); e != a {
  254. t.Errorf("expected\n%#v\ngot\n%#v\n", e, a)
  255. }
  256. gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
  257. if err != nil {
  258. t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
  259. } else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) {
  260. t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a))
  261. }
  262. }
  263. }
  264. func TestEncode(t *testing.T) {
  265. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  266. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
  267. scheme := runtime.NewScheme()
  268. scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
  269. scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
  270. codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
  271. test := &InternalSimple{
  272. TestString: "I'm the same",
  273. }
  274. obj := runtime.Object(test)
  275. data, err := runtime.Encode(codec, obj)
  276. obj2, gvk, err2 := codec.Decode(data, nil, nil)
  277. if err != nil || err2 != nil {
  278. t.Fatalf("Failure: '%v' '%v'", err, err2)
  279. }
  280. if _, ok := obj2.(*InternalSimple); !ok {
  281. t.Fatalf("Got wrong type")
  282. }
  283. if !reflect.DeepEqual(obj2, test) {
  284. t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2)
  285. }
  286. if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) {
  287. t.Errorf("unexpected gvk returned by decode: %#v", gvk)
  288. }
  289. }
  290. func TestUnversionedTypes(t *testing.T) {
  291. internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
  292. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
  293. otherGV := unversioned.GroupVersion{Group: "group", Version: "other"}
  294. scheme := runtime.NewScheme()
  295. scheme.AddUnversionedTypes(externalGV, &InternalSimple{})
  296. scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
  297. scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
  298. scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{})
  299. codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
  300. if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok {
  301. t.Fatalf("type not unversioned and in scheme: %t %t", unv, ok)
  302. }
  303. kinds, _, err := scheme.ObjectKinds(&InternalSimple{})
  304. if err != nil {
  305. t.Fatal(err)
  306. }
  307. kind := kinds[0]
  308. if kind != externalGV.WithKind("InternalSimple") {
  309. t.Fatalf("unexpected: %#v", kind)
  310. }
  311. test := &InternalSimple{
  312. TestString: "I'm the same",
  313. }
  314. obj := runtime.Object(test)
  315. data, err := runtime.Encode(codec, obj)
  316. if err != nil {
  317. t.Fatal(err)
  318. }
  319. obj2, gvk, err := codec.Decode(data, nil, nil)
  320. if err != nil {
  321. t.Fatal(err)
  322. }
  323. if _, ok := obj2.(*InternalSimple); !ok {
  324. t.Fatalf("Got wrong type")
  325. }
  326. if !reflect.DeepEqual(obj2, test) {
  327. t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2)
  328. }
  329. // object is serialized as an unversioned object (in the group and version it was defined in)
  330. if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) {
  331. t.Errorf("unexpected gvk returned by decode: %#v", gvk)
  332. }
  333. // when serialized to a different group, the object is kept in its preferred name
  334. codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV)
  335. data, err = runtime.Encode(codec, obj)
  336. if err != nil {
  337. t.Fatal(err)
  338. }
  339. if string(data) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" {
  340. t.Errorf("unexpected data: %s", data)
  341. }
  342. }
  343. // Test a weird version/kind embedding format.
  344. type MyWeirdCustomEmbeddedVersionKindField struct {
  345. ID string `json:"ID,omitempty"`
  346. APIVersion string `json:"myVersionKey,omitempty"`
  347. ObjectKind string `json:"myKindKey,omitempty"`
  348. Z string `json:"Z,omitempty"`
  349. Y uint64 `json:"Y,omitempty"`
  350. }
  351. type TestType1 struct {
  352. MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
  353. A string `json:"A,omitempty"`
  354. B int `json:"B,omitempty"`
  355. C int8 `json:"C,omitempty"`
  356. D int16 `json:"D,omitempty"`
  357. E int32 `json:"E,omitempty"`
  358. F int64 `json:"F,omitempty"`
  359. G uint `json:"G,omitempty"`
  360. H uint8 `json:"H,omitempty"`
  361. I uint16 `json:"I,omitempty"`
  362. J uint32 `json:"J,omitempty"`
  363. K uint64 `json:"K,omitempty"`
  364. L bool `json:"L,omitempty"`
  365. M map[string]int `json:"M,omitempty"`
  366. N map[string]TestType2 `json:"N,omitempty"`
  367. O *TestType2 `json:"O,omitempty"`
  368. P []TestType2 `json:"Q,omitempty"`
  369. }
  370. type TestType2 struct {
  371. A string `json:"A,omitempty"`
  372. B int `json:"B,omitempty"`
  373. }
  374. type ExternalTestType2 struct {
  375. A string `json:"A,omitempty"`
  376. B int `json:"B,omitempty"`
  377. }
  378. type ExternalTestType1 struct {
  379. MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
  380. A string `json:"A,omitempty"`
  381. B int `json:"B,omitempty"`
  382. C int8 `json:"C,omitempty"`
  383. D int16 `json:"D,omitempty"`
  384. E int32 `json:"E,omitempty"`
  385. F int64 `json:"F,omitempty"`
  386. G uint `json:"G,omitempty"`
  387. H uint8 `json:"H,omitempty"`
  388. I uint16 `json:"I,omitempty"`
  389. J uint32 `json:"J,omitempty"`
  390. K uint64 `json:"K,omitempty"`
  391. L bool `json:"L,omitempty"`
  392. M map[string]int `json:"M,omitempty"`
  393. N map[string]ExternalTestType2 `json:"N,omitempty"`
  394. O *ExternalTestType2 `json:"O,omitempty"`
  395. P []ExternalTestType2 `json:"Q,omitempty"`
  396. }
  397. type ExternalInternalSame struct {
  398. MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
  399. A TestType2 `json:"A,omitempty"`
  400. }
  401. type UnversionedType struct {
  402. MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
  403. A string `json:"A,omitempty"`
  404. }
  405. type UnknownType struct {
  406. MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
  407. A string `json:"A,omitempty"`
  408. }
  409. func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj }
  410. func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
  411. obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
  412. }
  413. func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() unversioned.GroupVersionKind {
  414. return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
  415. }
  416. func (obj *ExternalInternalSame) GetObjectKind() unversioned.ObjectKind {
  417. return &obj.MyWeirdCustomEmbeddedVersionKindField
  418. }
  419. func (obj *TestType1) GetObjectKind() unversioned.ObjectKind {
  420. return &obj.MyWeirdCustomEmbeddedVersionKindField
  421. }
  422. func (obj *ExternalTestType1) GetObjectKind() unversioned.ObjectKind {
  423. return &obj.MyWeirdCustomEmbeddedVersionKindField
  424. }
  425. func (obj *TestType2) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
  426. func (obj *ExternalTestType2) GetObjectKind() unversioned.ObjectKind {
  427. return unversioned.EmptyObjectKind
  428. }
  429. // TestObjectFuzzer can randomly populate all the above objects.
  430. var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
  431. func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) {
  432. // We have to customize the randomization of MyWeirdCustomEmbeddedVersionKindFields because their
  433. // APIVersion and Kind must remain blank in memory.
  434. j.APIVersion = ""
  435. j.ObjectKind = ""
  436. j.ID = c.RandString()
  437. },
  438. )
  439. // Returns a new Scheme set up with the test objects.
  440. func GetTestScheme() *runtime.Scheme {
  441. internalGV := unversioned.GroupVersion{Version: "__internal"}
  442. externalGV := unversioned.GroupVersion{Version: "v1"}
  443. alternateExternalGV := unversioned.GroupVersion{Group: "custom", Version: "v1"}
  444. differentExternalGV := unversioned.GroupVersion{Group: "other", Version: "v2"}
  445. s := runtime.NewScheme()
  446. // Ordinarily, we wouldn't add TestType2, but because this is a test and
  447. // both types are from the same package, we need to get it into the system
  448. // so that converter will match it with ExternalType2.
  449. s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{})
  450. s.AddKnownTypes(externalGV, &ExternalInternalSame{})
  451. s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
  452. s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{})
  453. s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{})
  454. s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{})
  455. s.AddKnownTypeWithName(externalGV.WithKind("TestType4"), &ExternalTestType1{})
  456. s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType3"), &ExternalTestType1{})
  457. s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType5"), &ExternalTestType1{})
  458. s.AddKnownTypeWithName(differentExternalGV.WithKind("TestType1"), &ExternalTestType1{})
  459. s.AddUnversionedTypes(externalGV, &UnversionedType{})
  460. return s
  461. }
  462. func TestKnownTypes(t *testing.T) {
  463. s := GetTestScheme()
  464. if len(s.KnownTypes(unversioned.GroupVersion{Group: "group", Version: "v2"})) != 0 {
  465. t.Errorf("should have no known types for v2")
  466. }
  467. types := s.KnownTypes(unversioned.GroupVersion{Version: "v1"})
  468. for _, s := range []string{"TestType1", "TestType2", "TestType3", "ExternalInternalSame"} {
  469. if _, ok := types[s]; !ok {
  470. t.Errorf("missing type %q", s)
  471. }
  472. }
  473. }
  474. func TestConvertToVersionBasic(t *testing.T) {
  475. s := GetTestScheme()
  476. tt := &TestType1{A: "I'm not a pointer object"}
  477. other, err := s.ConvertToVersion(tt, unversioned.GroupVersion{Version: "v1"})
  478. if err != nil {
  479. t.Fatalf("Failure: %v", err)
  480. }
  481. converted, ok := other.(*ExternalTestType1)
  482. if !ok {
  483. t.Fatalf("Got wrong type: %T", other)
  484. }
  485. if tt.A != converted.A {
  486. t.Fatalf("Failed to convert object correctly: %#v", converted)
  487. }
  488. }
  489. type testGroupVersioner struct {
  490. target unversioned.GroupVersionKind
  491. ok bool
  492. }
  493. func (m testGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
  494. return m.target, m.ok
  495. }
  496. func TestConvertToVersion(t *testing.T) {
  497. testCases := []struct {
  498. scheme *runtime.Scheme
  499. in runtime.Object
  500. gv runtime.GroupVersioner
  501. same bool
  502. out runtime.Object
  503. errFn func(error) bool
  504. }{
  505. // errors if the type is not registered in the scheme
  506. {
  507. scheme: GetTestScheme(),
  508. in: &UnknownType{},
  509. errFn: func(err error) bool { return err != nil && runtime.IsNotRegisteredError(err) },
  510. },
  511. // errors if the group versioner returns no target
  512. {
  513. scheme: GetTestScheme(),
  514. in: &ExternalTestType1{A: "test"},
  515. gv: testGroupVersioner{},
  516. errFn: func(err error) bool {
  517. return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
  518. },
  519. },
  520. // converts to internal
  521. {
  522. scheme: GetTestScheme(),
  523. in: &ExternalTestType1{A: "test"},
  524. gv: unversioned.GroupVersion{Version: "__internal"},
  525. out: &TestType1{A: "test"},
  526. },
  527. // prefers the first group version in the list
  528. {
  529. scheme: GetTestScheme(),
  530. in: &ExternalTestType1{A: "test"},
  531. gv: unversioned.GroupVersions{{Version: "__internal"}, {Version: "v1"}},
  532. out: &TestType1{A: "test"},
  533. },
  534. // unversioned type returned as-is
  535. {
  536. scheme: GetTestScheme(),
  537. in: &UnversionedType{A: "test"},
  538. gv: unversioned.GroupVersions{{Version: "v1"}},
  539. same: true,
  540. out: &UnversionedType{
  541. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
  542. A: "test",
  543. },
  544. },
  545. // detected as already being in the target version
  546. {
  547. scheme: GetTestScheme(),
  548. in: &ExternalTestType1{A: "test"},
  549. gv: unversioned.GroupVersions{{Version: "v1"}},
  550. same: true,
  551. out: &ExternalTestType1{
  552. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
  553. A: "test",
  554. },
  555. },
  556. // detected as already being in the first target version
  557. {
  558. scheme: GetTestScheme(),
  559. in: &ExternalTestType1{A: "test"},
  560. gv: unversioned.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
  561. same: true,
  562. out: &ExternalTestType1{
  563. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
  564. A: "test",
  565. },
  566. },
  567. // detected as already being in the first target version
  568. {
  569. scheme: GetTestScheme(),
  570. in: &ExternalTestType1{A: "test"},
  571. gv: unversioned.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
  572. same: true,
  573. out: &ExternalTestType1{
  574. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
  575. A: "test",
  576. },
  577. },
  578. // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (1/3): different kind
  579. {
  580. scheme: GetTestScheme(),
  581. in: &ExternalTestType1{A: "test"},
  582. gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType3", Version: "v1"}},
  583. same: true,
  584. out: &ExternalTestType1{
  585. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
  586. A: "test",
  587. },
  588. },
  589. // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (2/3): different gv
  590. {
  591. scheme: GetTestScheme(),
  592. in: &ExternalTestType1{A: "test"},
  593. gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType3", Group: "custom", Version: "v1"}},
  594. same: true,
  595. out: &ExternalTestType1{
  596. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType3"},
  597. A: "test",
  598. },
  599. },
  600. // the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (3/3): different gvk
  601. {
  602. scheme: GetTestScheme(),
  603. in: &ExternalTestType1{A: "test"},
  604. gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Group: "custom", Version: "v1", Kind: "TestType5"}},
  605. same: true,
  606. out: &ExternalTestType1{
  607. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
  608. A: "test",
  609. },
  610. },
  611. // multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
  612. {
  613. scheme: GetTestScheme(),
  614. in: &ExternalTestType1{A: "test"},
  615. gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "other", Version: "v2"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}, unversioned.GroupKind{Kind: "TestType1"}),
  616. out: &ExternalTestType1{
  617. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
  618. A: "test",
  619. },
  620. },
  621. // multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
  622. {
  623. scheme: GetTestScheme(),
  624. in: &ExternalTestType1{A: "test"},
  625. gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "other", Version: "v2"}, unversioned.GroupKind{Kind: "TestType1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}),
  626. out: &ExternalTestType1{
  627. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
  628. A: "test",
  629. },
  630. },
  631. // 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)
  632. {
  633. scheme: GetTestScheme(),
  634. in: &TestType1{A: "test"},
  635. gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "custom", Version: "v1"}, unversioned.GroupKind{Group: "other"}, unversioned.GroupKind{Kind: "TestType5"}),
  636. errFn: func(err error) bool {
  637. return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
  638. },
  639. },
  640. // multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
  641. {
  642. scheme: GetTestScheme(),
  643. in: &ExternalTestType1{A: "test"},
  644. gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "", Version: "v1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}, unversioned.GroupKind{Kind: "TestType1"}),
  645. same: true,
  646. out: &ExternalTestType1{
  647. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
  648. A: "test",
  649. },
  650. },
  651. // multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
  652. {
  653. scheme: GetTestScheme(),
  654. in: &ExternalTestType1{A: "test"},
  655. gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "", Version: "v1"}, unversioned.GroupKind{Kind: "TestType1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}),
  656. same: true,
  657. out: &ExternalTestType1{
  658. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
  659. A: "test",
  660. },
  661. },
  662. // group versioner can choose a particular target kind for a given input when kind is the same across group versions
  663. {
  664. scheme: GetTestScheme(),
  665. in: &TestType1{A: "test"},
  666. gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Version: "v1", Kind: "TestType3"}},
  667. out: &ExternalTestType1{
  668. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
  669. A: "test",
  670. },
  671. },
  672. // group versioner can choose a different kind
  673. {
  674. scheme: GetTestScheme(),
  675. in: &TestType1{A: "test"},
  676. gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType5", Group: "custom", Version: "v1"}},
  677. out: &ExternalTestType1{
  678. MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
  679. A: "test",
  680. },
  681. },
  682. }
  683. for i, test := range testCases {
  684. original, _ := test.scheme.DeepCopy(test.in)
  685. out, err := test.scheme.ConvertToVersion(test.in, test.gv)
  686. switch {
  687. case test.errFn != nil:
  688. if !test.errFn(err) {
  689. t.Errorf("%d: unexpected error: %v", i, err)
  690. }
  691. continue
  692. case err != nil:
  693. t.Errorf("%d: unexpected error: %v", i, err)
  694. continue
  695. }
  696. if out == test.in {
  697. t.Errorf("%d: ConvertToVersion should always copy out: %#v", i, out)
  698. continue
  699. }
  700. if test.same {
  701. if !reflect.DeepEqual(original, test.in) {
  702. t.Errorf("%d: unexpected mutation of input: %s", i, diff.ObjectReflectDiff(original, test.in))
  703. continue
  704. }
  705. if !reflect.DeepEqual(out, test.out) {
  706. t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
  707. continue
  708. }
  709. unsafe, err := test.scheme.UnsafeConvertToVersion(test.in, test.gv)
  710. if err != nil {
  711. t.Errorf("%d: unexpected error: %v", i, err)
  712. continue
  713. }
  714. if !reflect.DeepEqual(unsafe, test.out) {
  715. t.Errorf("%d: unexpected unsafe: %s", i, diff.ObjectReflectDiff(unsafe, test.out))
  716. continue
  717. }
  718. if unsafe != test.in {
  719. t.Errorf("%d: UnsafeConvertToVersion should return same object: %#v", i, unsafe)
  720. continue
  721. }
  722. continue
  723. }
  724. if !reflect.DeepEqual(out, test.out) {
  725. t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
  726. continue
  727. }
  728. }
  729. }
  730. func TestMetaValues(t *testing.T) {
  731. internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"}
  732. externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"}
  733. s := runtime.NewScheme()
  734. s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
  735. s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
  736. internalToExternalCalls := 0
  737. externalToInternalCalls := 0
  738. // Register functions to verify that scope.Meta() gets set correctly.
  739. err := s.AddConversionFuncs(
  740. func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
  741. t.Logf("internal -> external")
  742. scope.Convert(&in.TestString, &out.TestString, 0)
  743. internalToExternalCalls++
  744. return nil
  745. },
  746. func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
  747. t.Logf("external -> internal")
  748. scope.Convert(&in.TestString, &out.TestString, 0)
  749. externalToInternalCalls++
  750. return nil
  751. },
  752. )
  753. if err != nil {
  754. t.Fatalf("unexpected error: %v", err)
  755. }
  756. simple := &InternalSimple{
  757. TestString: "foo",
  758. }
  759. s.Log(t)
  760. out, err := s.ConvertToVersion(simple, externalGV)
  761. if err != nil {
  762. t.Fatalf("unexpected error: %v", err)
  763. }
  764. internal, err := s.ConvertToVersion(out, internalGV)
  765. if err != nil {
  766. t.Fatalf("unexpected error: %v", err)
  767. }
  768. if e, a := simple, internal; !reflect.DeepEqual(e, a) {
  769. t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
  770. }
  771. if e, a := 1, internalToExternalCalls; e != a {
  772. t.Errorf("Expected %v, got %v", e, a)
  773. }
  774. if e, a := 1, externalToInternalCalls; e != a {
  775. t.Errorf("Expected %v, got %v", e, a)
  776. }
  777. }
  778. func TestMetaValuesUnregisteredConvert(t *testing.T) {
  779. type InternalSimple struct {
  780. Version string `json:"apiVersion,omitempty"`
  781. Kind string `json:"kind,omitempty"`
  782. TestString string `json:"testString"`
  783. }
  784. type ExternalSimple struct {
  785. Version string `json:"apiVersion,omitempty"`
  786. Kind string `json:"kind,omitempty"`
  787. TestString string `json:"testString"`
  788. }
  789. s := runtime.NewScheme()
  790. // We deliberately don't register the types.
  791. internalToExternalCalls := 0
  792. // Register functions to verify that scope.Meta() gets set correctly.
  793. err := s.AddConversionFuncs(
  794. func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
  795. scope.Convert(&in.TestString, &out.TestString, 0)
  796. internalToExternalCalls++
  797. return nil
  798. },
  799. )
  800. if err != nil {
  801. t.Fatalf("unexpected error: %v", err)
  802. }
  803. simple := &InternalSimple{TestString: "foo"}
  804. external := &ExternalSimple{}
  805. err = s.Convert(simple, external, nil)
  806. if err != nil {
  807. t.Fatalf("Unexpected error: %v", err)
  808. }
  809. if e, a := simple.TestString, external.TestString; e != a {
  810. t.Errorf("Expected %v, got %v", e, a)
  811. }
  812. // Verify that our conversion handler got called.
  813. if e, a := 1, internalToExternalCalls; e != a {
  814. t.Errorf("Expected %v, got %v", e, a)
  815. }
  816. }