1
16
17 package runtime_test
18
19 import (
20 "fmt"
21 "reflect"
22 "strings"
23 "testing"
24
25 "github.com/google/go-cmp/cmp"
26 "k8s.io/apimachinery/pkg/conversion"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/apimachinery/pkg/runtime/serializer"
30 runtimetesting "k8s.io/apimachinery/pkg/runtime/testing"
31 "k8s.io/apimachinery/pkg/util/diff"
32 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
33 )
34
35 type testConversions struct {
36 internalToExternalCalls int
37 externalToInternalCalls int
38 }
39
40 func (c *testConversions) internalToExternalSimple(in *runtimetesting.InternalSimple, out *runtimetesting.ExternalSimple, scope conversion.Scope) error {
41 out.TypeMeta = in.TypeMeta
42 out.TestString = in.TestString
43 c.internalToExternalCalls++
44 return nil
45 }
46
47 func (c *testConversions) externalToInternalSimple(in *runtimetesting.ExternalSimple, out *runtimetesting.InternalSimple, scope conversion.Scope) error {
48 out.TypeMeta = in.TypeMeta
49 out.TestString = in.TestString
50 c.externalToInternalCalls++
51 return nil
52 }
53
54 func (c *testConversions) registerConversions(s *runtime.Scheme) error {
55 if err := s.AddConversionFunc((*runtimetesting.InternalSimple)(nil), (*runtimetesting.ExternalSimple)(nil), func(a, b interface{}, scope conversion.Scope) error {
56 return c.internalToExternalSimple(a.(*runtimetesting.InternalSimple), b.(*runtimetesting.ExternalSimple), scope)
57 }); err != nil {
58 return err
59 }
60 if err := s.AddConversionFunc((*runtimetesting.ExternalSimple)(nil), (*runtimetesting.InternalSimple)(nil), func(a, b interface{}, scope conversion.Scope) error {
61 return c.externalToInternalSimple(a.(*runtimetesting.ExternalSimple), b.(*runtimetesting.InternalSimple), scope)
62 }); err != nil {
63 return err
64 }
65 return nil
66 }
67
68 func TestScheme(t *testing.T) {
69 internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
70 internalGVK := internalGV.WithKind("Simple")
71 externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
72 externalGVK := externalGV.WithKind("Simple")
73
74 scheme := runtime.NewScheme()
75 scheme.AddKnownTypeWithName(internalGVK, &runtimetesting.InternalSimple{})
76 scheme.AddKnownTypeWithName(externalGVK, &runtimetesting.ExternalSimple{})
77 utilruntime.Must(runtimetesting.RegisterConversions(scheme))
78
79
80
81
82
83 var _ runtime.ObjectTyper = scheme
84
85 conversions := &testConversions{
86 internalToExternalCalls: 0,
87 externalToInternalCalls: 0,
88 }
89
90
91 utilruntime.Must(conversions.registerConversions(scheme))
92
93 t.Run("Encode, Decode, DecodeInto, and DecodeToVersion", func(t *testing.T) {
94 simple := &runtimetesting.InternalSimple{
95 TestString: "foo",
96 }
97
98 codecs := serializer.NewCodecFactory(scheme)
99 codec := codecs.LegacyCodec(externalGV)
100 info, _ := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
101 jsonserializer := info.Serializer
102
103 obj := runtime.Object(simple)
104 data, err := runtime.Encode(codec, obj)
105 if err != nil {
106 t.Fatal(err)
107 }
108
109 obj2, err := runtime.Decode(codec, data)
110 if err != nil {
111 t.Fatal(err)
112 }
113 if _, ok := obj2.(*runtimetesting.InternalSimple); !ok {
114 t.Fatalf("Got wrong type")
115 }
116 if e, a := simple, obj2; !reflect.DeepEqual(e, a) {
117 t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
118 }
119
120 obj3 := &runtimetesting.InternalSimple{}
121 if err := runtime.DecodeInto(codec, data, obj3); err != nil {
122 t.Fatal(err)
123 }
124
125
126 simple.TypeMeta = runtime.TypeMeta{Kind: "Simple", APIVersion: externalGV.String()}
127 if e, a := simple, obj3; !reflect.DeepEqual(e, a) {
128 t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
129 }
130
131 obj4, err := runtime.Decode(jsonserializer, data)
132 if err != nil {
133 t.Fatal(err)
134 }
135 if _, ok := obj4.(*runtimetesting.ExternalSimple); !ok {
136 t.Fatalf("Got wrong type")
137 }
138 })
139 t.Run("Convert", func(t *testing.T) {
140 simple := &runtimetesting.InternalSimple{
141 TestString: "foo",
142 }
143
144 external := &runtimetesting.ExternalSimple{}
145 if err := scheme.Convert(simple, external, nil); err != nil {
146 t.Fatalf("Unexpected error: %v", err)
147 }
148 if e, a := simple.TestString, external.TestString; e != a {
149 t.Errorf("Expected %q, got %q", e, a)
150 }
151 })
152 t.Run("Convert internal to unstructured", func(t *testing.T) {
153 simple := &runtimetesting.InternalSimple{
154 TestString: "foo",
155 }
156
157 unstructuredObj := &runtimetesting.Unstructured{}
158 err := scheme.Convert(simple, unstructuredObj, nil)
159 if err == nil || !strings.Contains(err.Error(), "to Unstructured without providing a preferred version to convert to") {
160 t.Fatalf("Unexpected non-error: %v", err)
161 }
162 if err := scheme.Convert(simple, unstructuredObj, externalGV); err != nil {
163 t.Fatalf("Unexpected error: %v", err)
164 }
165 if e, a := simple.TestString, unstructuredObj.Object["testString"].(string); e != a {
166 t.Errorf("Expected %q, got %q", e, a)
167 }
168 if e := unstructuredObj.GetObjectKind().GroupVersionKind(); e != externalGVK {
169 t.Errorf("Unexpected object kind: %#v", e)
170 }
171 if gvks, unversioned, err := scheme.ObjectKinds(unstructuredObj); err != nil || gvks[0] != externalGVK || unversioned {
172 t.Errorf("Scheme did not recognize unversioned: %v, %#v %t", err, gvks, unversioned)
173 }
174 })
175 t.Run("Convert external to unstructured", func(t *testing.T) {
176 unstructuredObj := &runtimetesting.Unstructured{}
177 external := &runtimetesting.ExternalSimple{
178 TestString: "foo",
179 }
180
181 if err := scheme.Convert(external, unstructuredObj, nil); err != nil {
182 t.Fatalf("Unexpected error: %v", err)
183 }
184 if e, a := external.TestString, unstructuredObj.Object["testString"].(string); e != a {
185 t.Errorf("Expected %q, got %q", e, a)
186 }
187 if e := unstructuredObj.GetObjectKind().GroupVersionKind(); e != externalGVK {
188 t.Errorf("Unexpected object kind: %#v", e)
189 }
190 })
191 t.Run("Convert unstructured to unstructured", func(t *testing.T) {
192 uIn := &runtimetesting.Unstructured{Object: map[string]interface{}{
193 "test": []interface{}{"other", "test"},
194 }}
195 uOut := &runtimetesting.Unstructured{}
196 if err := scheme.Convert(uIn, uOut, nil); err != nil {
197 t.Fatalf("Unexpected error: %v", err)
198 }
199 if !reflect.DeepEqual(uIn.Object, uOut.Object) {
200 t.Errorf("Unexpected object contents: %#v", uOut.Object)
201 }
202 })
203 t.Run("Convert unstructured to structured", func(t *testing.T) {
204 unstructuredObj := &runtimetesting.Unstructured{
205 Object: map[string]interface{}{
206 "testString": "bla",
207 },
208 }
209 unstructuredObj.SetGroupVersionKind(externalGV.WithKind("Simple"))
210 externalOut := &runtimetesting.ExternalSimple{}
211 if err := scheme.Convert(unstructuredObj, externalOut, nil); err != nil {
212 t.Fatalf("Unexpected error: %v", err)
213 }
214 if externalOut.TestString != "bla" {
215 t.Errorf("Unexpected object contents: %#v", externalOut)
216 }
217 })
218 t.Run("Encode and Convert should each have caused an increment", func(t *testing.T) {
219 if e, a := 3, conversions.internalToExternalCalls; e != a {
220 t.Errorf("Expected %v, got %v", e, a)
221 }
222 })
223 t.Run("DecodeInto and Decode should each have caused an increment because of a conversion", func(t *testing.T) {
224 if e, a := 2, conversions.externalToInternalCalls; e != a {
225 t.Errorf("Expected %v, got %v", e, a)
226 }
227 })
228 t.Run("Verify that unstructured types must have V and K set", func(t *testing.T) {
229 emptyObj := &runtimetesting.Unstructured{Object: make(map[string]interface{})}
230 if _, _, err := scheme.ObjectKinds(emptyObj); !runtime.IsMissingKind(err) {
231 t.Errorf("unexpected error: %v", err)
232 }
233 emptyObj.SetGroupVersionKind(schema.GroupVersionKind{Kind: "Test"})
234 if _, _, err := scheme.ObjectKinds(emptyObj); !runtime.IsMissingVersion(err) {
235 t.Errorf("unexpected error: %v", err)
236 }
237 emptyObj.SetGroupVersionKind(schema.GroupVersionKind{Kind: "Test", Version: "v1"})
238 if _, _, err := scheme.ObjectKinds(emptyObj); err != nil {
239 t.Errorf("unexpected error: %v", err)
240 }
241 })
242 }
243
244 func TestBadJSONRejection(t *testing.T) {
245 scheme := runtime.NewScheme()
246 codecs := serializer.NewCodecFactory(scheme)
247 info, _ := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
248 jsonserializer := info.Serializer
249
250 badJSONMissingKind := []byte(`{ }`)
251 if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil {
252 t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
253 }
254 badJSONUnknownType := []byte(`{"kind": "bar"}`)
255 if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil {
256 t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
257 }
258 }
259
260 func TestExternalToInternalMapping(t *testing.T) {
261 internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
262 externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
263
264 scheme := runtime.NewScheme()
265 scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &runtimetesting.InternalOptionalExtensionType{})
266 scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &runtimetesting.ExternalOptionalExtensionType{})
267 utilruntime.Must(runtimetesting.RegisterConversions(scheme))
268
269 codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
270
271 table := []struct {
272 obj runtime.Object
273 encoded string
274 }{
275 {
276 &runtimetesting.InternalOptionalExtensionType{Extension: nil},
277 `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`,
278 },
279 }
280
281 for i, item := range table {
282 gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
283 if err != nil {
284 t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
285 } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) {
286 t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a))
287 }
288 }
289 }
290
291 func TestExtensionMapping(t *testing.T) {
292 internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
293 externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
294
295 scheme := runtime.NewScheme()
296 scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &runtimetesting.InternalExtensionType{})
297 scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &runtimetesting.InternalOptionalExtensionType{})
298 scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &runtimetesting.ExternalExtensionType{})
299 scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &runtimetesting.ExternalOptionalExtensionType{})
300
301
302
303 scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &runtimetesting.ExtensionA{})
304 scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &runtimetesting.ExtensionB{})
305 scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &runtimetesting.ExtensionA{})
306 scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &runtimetesting.ExtensionB{})
307 utilruntime.Must(runtimetesting.RegisterConversions(scheme))
308
309 codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
310
311 table := []struct {
312 obj runtime.Object
313 expected runtime.Object
314 encoded string
315 }{
316 {
317 &runtimetesting.InternalExtensionType{
318 Extension: runtime.NewEncodable(codec, &runtimetesting.ExtensionA{TestString: "foo"}),
319 },
320 &runtimetesting.InternalExtensionType{
321 Extension: &runtime.Unknown{
322 Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`),
323 ContentType: runtime.ContentTypeJSON,
324 },
325 },
326
327 `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}}
328 `,
329 }, {
330 &runtimetesting.InternalExtensionType{Extension: runtime.NewEncodable(codec, &runtimetesting.ExtensionB{TestString: "bar"})},
331 &runtimetesting.InternalExtensionType{
332 Extension: &runtime.Unknown{
333 Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`),
334 ContentType: runtime.ContentTypeJSON,
335 },
336 },
337
338 `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}}
339 `,
340 }, {
341 &runtimetesting.InternalExtensionType{Extension: nil},
342 &runtimetesting.InternalExtensionType{
343 Extension: nil,
344 },
345 `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":null}
346 `,
347 },
348 }
349
350 for i, item := range table {
351 gotEncoded, err := runtime.Encode(codec, item.obj)
352 if err != nil {
353 t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
354 } else if e, a := item.encoded, string(gotEncoded); e != a {
355 t.Errorf("expected\n%#v\ngot\n%#v\n", e, a)
356 }
357
358 gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
359 if err != nil {
360 t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
361 } else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) {
362 t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a))
363 }
364 }
365 }
366
367 func TestEncode(t *testing.T) {
368 internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
369 internalGVK := internalGV.WithKind("Simple")
370 externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
371 externalGVK := externalGV.WithKind("Simple")
372
373 scheme := runtime.NewScheme()
374 scheme.AddKnownTypeWithName(internalGVK, &runtimetesting.InternalSimple{})
375 scheme.AddKnownTypeWithName(externalGVK, &runtimetesting.ExternalSimple{})
376 utilruntime.Must(runtimetesting.RegisterConversions(scheme))
377
378 codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
379
380 test := &runtimetesting.InternalSimple{
381 TestString: "I'm the same",
382 }
383 obj := runtime.Object(test)
384 data, err := runtime.Encode(codec, obj)
385 obj2, gvk, err2 := codec.Decode(data, nil, nil)
386 if err != nil || err2 != nil {
387 t.Fatalf("Failure: '%v' '%v'", err, err2)
388 }
389 if _, ok := obj2.(*runtimetesting.InternalSimple); !ok {
390 t.Fatalf("Got wrong type")
391 }
392 if !reflect.DeepEqual(obj2, test) {
393 t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2)
394 }
395 if *gvk != externalGVK {
396 t.Errorf("unexpected gvk returned by decode: %#v", *gvk)
397 }
398 }
399
400 func TestUnversionedTypes(t *testing.T) {
401 internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
402 internalGVK := internalGV.WithKind("Simple")
403 externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
404 externalGVK := externalGV.WithKind("Simple")
405 otherGV := schema.GroupVersion{Group: "group", Version: "other"}
406
407 scheme := runtime.NewScheme()
408 scheme.AddUnversionedTypes(externalGV, &runtimetesting.InternalSimple{})
409 scheme.AddKnownTypeWithName(internalGVK, &runtimetesting.InternalSimple{})
410 scheme.AddKnownTypeWithName(externalGVK, &runtimetesting.ExternalSimple{})
411 scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &runtimetesting.ExternalSimple{})
412 utilruntime.Must(runtimetesting.RegisterConversions(scheme))
413
414 codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
415
416 if unv, ok := scheme.IsUnversioned(&runtimetesting.InternalSimple{}); !unv || !ok {
417 t.Fatalf("type not unversioned and in scheme: %t %t", unv, ok)
418 }
419
420 kinds, _, err := scheme.ObjectKinds(&runtimetesting.InternalSimple{})
421 if err != nil {
422 t.Fatal(err)
423 }
424 kind := kinds[0]
425 if kind != externalGV.WithKind("InternalSimple") {
426 t.Fatalf("unexpected: %#v", kind)
427 }
428
429 test := &runtimetesting.InternalSimple{
430 TestString: "I'm the same",
431 }
432 obj := runtime.Object(test)
433 data, err := runtime.Encode(codec, obj)
434 if err != nil {
435 t.Fatal(err)
436 }
437 obj2, gvk, err := codec.Decode(data, nil, nil)
438 if err != nil {
439 t.Fatal(err)
440 }
441 if _, ok := obj2.(*runtimetesting.InternalSimple); !ok {
442 t.Fatalf("Got wrong type")
443 }
444 if !reflect.DeepEqual(obj2, test) {
445 t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2)
446 }
447
448 if *gvk != externalGV.WithKind("InternalSimple") {
449 t.Errorf("unexpected gvk returned by decode: %#v", *gvk)
450 }
451
452
453 codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV)
454 data, err = runtime.Encode(codec, obj)
455 if err != nil {
456 t.Fatal(err)
457 }
458 if string(data) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" {
459 t.Errorf("unexpected data: %s", data)
460 }
461 }
462
463
464 func GetTestScheme() *runtime.Scheme {
465 internalGV := schema.GroupVersion{Version: runtime.APIVersionInternal}
466 externalGV := schema.GroupVersion{Version: "v1"}
467 alternateExternalGV := schema.GroupVersion{Group: "custom", Version: "v1"}
468 alternateInternalGV := schema.GroupVersion{Group: "custom", Version: runtime.APIVersionInternal}
469 differentExternalGV := schema.GroupVersion{Group: "other", Version: "v2"}
470
471 s := runtime.NewScheme()
472
473
474
475 s.AddKnownTypes(internalGV, &runtimetesting.TestType1{}, &runtimetesting.TestType2{}, &runtimetesting.ExternalInternalSame{})
476 s.AddKnownTypes(externalGV, &runtimetesting.ExternalInternalSame{})
477 s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &runtimetesting.ExternalTestType1{})
478 s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &runtimetesting.ExternalTestType2{})
479 s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &runtimetesting.TestType1{})
480 s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &runtimetesting.ExternalTestType1{})
481 s.AddKnownTypeWithName(externalGV.WithKind("TestType4"), &runtimetesting.ExternalTestType1{})
482 s.AddKnownTypeWithName(alternateInternalGV.WithKind("TestType3"), &runtimetesting.TestType1{})
483 s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType3"), &runtimetesting.ExternalTestType1{})
484 s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType5"), &runtimetesting.ExternalTestType1{})
485 s.AddKnownTypeWithName(differentExternalGV.WithKind("TestType1"), &runtimetesting.ExternalTestType1{})
486 s.AddUnversionedTypes(externalGV, &runtimetesting.UnversionedType{})
487 utilruntime.Must(runtimetesting.RegisterConversions(s))
488
489 return s
490 }
491
492 func TestKnownTypes(t *testing.T) {
493 s := GetTestScheme()
494 if len(s.KnownTypes(schema.GroupVersion{Group: "group", Version: "v2"})) != 0 {
495 t.Errorf("should have no known types for v2")
496 }
497
498 types := s.KnownTypes(schema.GroupVersion{Version: "v1"})
499 for _, s := range []string{"TestType1", "TestType2", "TestType3", "ExternalInternalSame"} {
500 if _, ok := types[s]; !ok {
501 t.Errorf("missing type %q", s)
502 }
503 }
504 }
505
506 func TestAddKnownTypesIdemPotent(t *testing.T) {
507 s := runtime.NewScheme()
508
509 gv := schema.GroupVersion{Group: "foo", Version: "v1"}
510 s.AddKnownTypes(gv, &runtimetesting.InternalSimple{})
511 s.AddKnownTypes(gv, &runtimetesting.InternalSimple{})
512 if len(s.KnownTypes(gv)) != 1 {
513 t.Errorf("expected only one %v type after double registration", gv)
514 }
515 if len(s.AllKnownTypes()) != 1 {
516 t.Errorf("expected only one type after double registration")
517 }
518
519 s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &runtimetesting.InternalSimple{})
520 s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &runtimetesting.InternalSimple{})
521 if len(s.KnownTypes(gv)) != 1 {
522 t.Errorf("expected only one %v type after double registration with custom name", gv)
523 }
524 if len(s.AllKnownTypes()) != 1 {
525 t.Errorf("expected only one type after double registration with custom name")
526 }
527
528 s.AddUnversionedTypes(gv, &runtimetesting.InternalSimple{})
529 s.AddUnversionedTypes(gv, &runtimetesting.InternalSimple{})
530 if len(s.KnownTypes(gv)) != 1 {
531 t.Errorf("expected only one %v type after double registration with custom name", gv)
532 }
533 if len(s.AllKnownTypes()) != 1 {
534 t.Errorf("expected only one type after double registration with custom name")
535 }
536
537 kinds, _, err := s.ObjectKinds(&runtimetesting.InternalSimple{})
538 if err != nil {
539 t.Fatalf("unexpected error: %v", err)
540 }
541 if len(kinds) != 1 {
542 t.Errorf("expected only one kind for InternalSimple after double registration")
543 }
544 }
545
546
547 type InternalSimple struct {
548 runtime.TypeMeta `json:",inline"`
549 TestString string `json:"testString"`
550 }
551
552 func (s *InternalSimple) DeepCopyObject() runtime.Object { return nil }
553
554 func TestConflictingAddKnownTypes(t *testing.T) {
555 s := runtime.NewScheme()
556 gv := schema.GroupVersion{Group: "foo", Version: "v1"}
557
558 panicked := make(chan bool)
559 go func() {
560 defer func() {
561 if recover() != nil {
562 panicked <- true
563 }
564 }()
565 s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &runtimetesting.InternalSimple{})
566 s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &runtimetesting.ExternalSimple{})
567 panicked <- false
568 }()
569 if !<-panicked {
570 t.Errorf("Expected AddKnownTypesWithName to panic with conflicting type registrations")
571 }
572
573 go func() {
574 defer func() {
575 if recover() != nil {
576 panicked <- true
577 }
578 }()
579
580 s.AddUnversionedTypes(gv, &runtimetesting.InternalSimple{})
581 s.AddUnversionedTypes(gv, &InternalSimple{})
582 panicked <- false
583 }()
584 if !<-panicked {
585 t.Errorf("Expected AddUnversionedTypes to panic with conflicting type registrations")
586 }
587 }
588
589 func TestConvertToVersionBasic(t *testing.T) {
590 s := GetTestScheme()
591 tt := &runtimetesting.TestType1{A: "I'm not a pointer object"}
592 other, err := s.ConvertToVersion(tt, schema.GroupVersion{Version: "v1"})
593 if err != nil {
594 t.Fatalf("Failure: %v", err)
595 }
596 converted, ok := other.(*runtimetesting.ExternalTestType1)
597 if !ok {
598 t.Fatalf("Got wrong type: %T", other)
599 }
600 if tt.A != converted.A {
601 t.Fatalf("Failed to convert object correctly: %#v", converted)
602 }
603 }
604
605 type testGroupVersioner struct {
606 target schema.GroupVersionKind
607 ok bool
608 }
609
610 func (m testGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
611 return m.target, m.ok
612 }
613
614 func (m testGroupVersioner) Identifier() string {
615 return "testGroupVersioner"
616 }
617
618 func TestConvertToVersion(t *testing.T) {
619 testCases := []struct {
620 scheme *runtime.Scheme
621 in runtime.Object
622 gv runtime.GroupVersioner
623 same bool
624 out runtime.Object
625 errFn func(error) bool
626 }{
627
628 {
629 scheme: GetTestScheme(),
630 in: &runtimetesting.UnknownType{},
631 errFn: func(err error) bool { return err != nil && runtime.IsNotRegisteredError(err) },
632 },
633
634 {
635 scheme: GetTestScheme(),
636 in: &runtimetesting.ExternalTestType1{A: "test"},
637 gv: testGroupVersioner{},
638 errFn: func(err error) bool {
639 return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
640 },
641 },
642
643 {
644 scheme: GetTestScheme(),
645 in: &runtimetesting.ExternalTestType1{A: "test"},
646 gv: schema.GroupVersion{Version: runtime.APIVersionInternal},
647 out: &runtimetesting.TestType1{A: "test"},
648 },
649
650 {
651 scheme: GetTestScheme(),
652 in: &runtimetesting.Unstructured{Object: map[string]interface{}{
653 "apiVersion": "custom/v1",
654 "kind": "TestType3",
655 "A": "test",
656 }},
657 gv: schema.GroupVersion{Version: runtime.APIVersionInternal},
658 out: &runtimetesting.TestType1{A: "test"},
659 },
660
661 {
662 scheme: GetTestScheme(),
663 in: &runtimetesting.Unstructured{Object: map[string]interface{}{
664 "apiVersion": "custom/v1",
665 "kind": "TestType3",
666 "A": "test",
667 }},
668 gv: schema.GroupVersion{Group: "custom", Version: "v1"},
669 out: &runtimetesting.ExternalTestType1{MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType3"}, A: "test"},
670 },
671
672 {
673 scheme: GetTestScheme(),
674 in: &runtimetesting.ExternalTestType1{A: "test"},
675 gv: schema.GroupVersions{{Version: runtime.APIVersionInternal}, {Version: "v1"}},
676 out: &runtimetesting.ExternalTestType1{
677 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
678 A: "test",
679 },
680 },
681
682 {
683 scheme: GetTestScheme(),
684 in: &runtimetesting.UnversionedType{A: "test"},
685 gv: schema.GroupVersions{{Version: "v1"}},
686 same: true,
687 out: &runtimetesting.UnversionedType{
688 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
689 A: "test",
690 },
691 },
692
693 {
694 scheme: GetTestScheme(),
695 in: &runtimetesting.UnversionedType{A: "test"},
696 gv: schema.GroupVersions{{Group: "other", Version: "v2"}},
697 same: true,
698 out: &runtimetesting.UnversionedType{
699 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
700 A: "test",
701 },
702 },
703
704 {
705 scheme: GetTestScheme(),
706 in: &runtimetesting.ExternalTestType1{A: "test"},
707 gv: schema.GroupVersions{{Version: "v1"}},
708 same: true,
709 out: &runtimetesting.ExternalTestType1{
710 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
711 A: "test",
712 },
713 },
714
715 {
716 scheme: GetTestScheme(),
717 in: &runtimetesting.ExternalTestType1{A: "test"},
718 gv: schema.GroupVersions{{Version: "v1"}, {Version: runtime.APIVersionInternal}},
719 same: true,
720 out: &runtimetesting.ExternalTestType1{
721 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
722 A: "test",
723 },
724 },
725
726 {
727 scheme: GetTestScheme(),
728 in: &runtimetesting.ExternalTestType1{A: "test"},
729 gv: schema.GroupVersions{{Version: "v1"}, {Version: runtime.APIVersionInternal}},
730 same: true,
731 out: &runtimetesting.ExternalTestType1{
732 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
733 A: "test",
734 },
735 },
736
737 {
738 scheme: GetTestScheme(),
739 in: &runtimetesting.ExternalTestType1{A: "test"},
740 gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType3", Version: "v1"}},
741 same: true,
742 out: &runtimetesting.ExternalTestType1{
743 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
744 A: "test",
745 },
746 },
747
748 {
749 scheme: GetTestScheme(),
750 in: &runtimetesting.ExternalTestType1{A: "test"},
751 gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType3", Group: "custom", Version: "v1"}},
752 same: true,
753 out: &runtimetesting.ExternalTestType1{
754 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType3"},
755 A: "test",
756 },
757 },
758
759 {
760 scheme: GetTestScheme(),
761 in: &runtimetesting.ExternalTestType1{A: "test"},
762 gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Group: "custom", Version: "v1", Kind: "TestType5"}},
763 same: true,
764 out: &runtimetesting.ExternalTestType1{
765 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
766 A: "test",
767 },
768 },
769
770 {
771 scheme: GetTestScheme(),
772 in: &runtimetesting.ExternalTestType1{A: "test"},
773 gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "other", Version: "v2"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}, schema.GroupKind{Kind: "TestType1"}),
774 out: &runtimetesting.ExternalTestType1{
775 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
776 A: "test",
777 },
778 },
779
780 {
781 scheme: GetTestScheme(),
782 in: &runtimetesting.ExternalTestType1{A: "test"},
783 gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "other", Version: "v2"}, schema.GroupKind{Kind: "TestType1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}),
784 out: &runtimetesting.ExternalTestType1{
785 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
786 A: "test",
787 },
788 },
789
790 {
791 scheme: GetTestScheme(),
792 in: &runtimetesting.TestType1{A: "test"},
793 gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "custom", Version: "v1"}, schema.GroupKind{Group: "other"}, schema.GroupKind{Kind: "TestType5"}),
794 errFn: func(err error) bool {
795 return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
796 },
797 },
798
799 {
800 scheme: GetTestScheme(),
801 in: &runtimetesting.ExternalTestType1{A: "test"},
802 gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "", Version: "v1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}, schema.GroupKind{Kind: "TestType1"}),
803 same: true,
804 out: &runtimetesting.ExternalTestType1{
805 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
806 A: "test",
807 },
808 },
809
810 {
811 scheme: GetTestScheme(),
812 in: &runtimetesting.ExternalTestType1{A: "test"},
813 gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "", Version: "v1"}, schema.GroupKind{Kind: "TestType1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}),
814 same: true,
815 out: &runtimetesting.ExternalTestType1{
816 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
817 A: "test",
818 },
819 },
820
821 {
822 scheme: GetTestScheme(),
823 in: &runtimetesting.TestType1{A: "test"},
824 gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Version: "v1", Kind: "TestType3"}},
825 out: &runtimetesting.ExternalTestType1{
826 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
827 A: "test",
828 },
829 },
830
831 {
832 scheme: GetTestScheme(),
833 in: &runtimetesting.TestType1{A: "test"},
834 gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType5", Group: "custom", Version: "v1"}},
835 out: &runtimetesting.ExternalTestType1{
836 MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
837 A: "test",
838 },
839 },
840 }
841 for i, test := range testCases {
842 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
843 original := test.in.DeepCopyObject()
844 out, err := test.scheme.ConvertToVersion(test.in, test.gv)
845 switch {
846 case test.errFn != nil:
847 if !test.errFn(err) {
848 t.Fatalf("unexpected error: %v", err)
849 }
850 return
851 case err != nil:
852 t.Fatalf("unexpected error: %v", err)
853 }
854 if out == test.in {
855 t.Fatalf("ConvertToVersion should always copy out: %#v", out)
856 }
857
858 if test.same {
859 if !reflect.DeepEqual(original, test.in) {
860 t.Fatalf("unexpected mutation of input: %s", cmp.Diff(original, test.in))
861 }
862 if !reflect.DeepEqual(out, test.out) {
863 t.Fatalf("unexpected out: %s", cmp.Diff(out, test.out))
864 }
865 unsafe, err := test.scheme.UnsafeConvertToVersion(test.in, test.gv)
866 if err != nil {
867 t.Fatalf("unexpected error: %v", err)
868 }
869 if !reflect.DeepEqual(unsafe, test.out) {
870 t.Fatalf("unexpected unsafe: %s", cmp.Diff(unsafe, test.out))
871 }
872 if unsafe != test.in {
873 t.Fatalf("UnsafeConvertToVersion should return same object: %#v", unsafe)
874 }
875 return
876 }
877 if !reflect.DeepEqual(out, test.out) {
878 t.Fatalf("unexpected out: %s", cmp.Diff(out, test.out))
879 }
880 })
881 }
882 }
883
884 func TestConvert(t *testing.T) {
885 testCases := []struct {
886 scheme *runtime.Scheme
887 in runtime.Object
888 into runtime.Object
889 gv runtime.GroupVersioner
890 out runtime.Object
891 errFn func(error) bool
892 }{
893
894 {
895 scheme: GetTestScheme(),
896 in: &runtimetesting.TestType1{A: "test"},
897 into: &runtimetesting.Unstructured{},
898 out: &runtimetesting.Unstructured{Object: map[string]interface{}{
899 "myVersionKey": "custom/v1",
900 "myKindKey": "TestType3",
901 "A": "test",
902 }},
903 gv: schema.GroupVersion{Group: "custom", Version: "v1"},
904 },
905 }
906 for i, test := range testCases {
907 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
908 err := test.scheme.Convert(test.in, test.into, test.gv)
909 switch {
910 case test.errFn != nil:
911 if !test.errFn(err) {
912 t.Fatalf("unexpected error: %v", err)
913 }
914 return
915 case err != nil:
916 t.Fatalf("unexpected error: %v", err)
917 return
918 }
919
920 if !reflect.DeepEqual(test.into, test.out) {
921 t.Fatalf("unexpected out: %s", cmp.Diff(test.into, test.out))
922 }
923 })
924 }
925 }
926
927 func TestMetaValues(t *testing.T) {
928 internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
929 externalGV := schema.GroupVersion{Group: "test.group", Version: "externalVersion"}
930
931 s := runtime.NewScheme()
932 s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &runtimetesting.InternalSimple{})
933 s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &runtimetesting.ExternalSimple{})
934 utilruntime.Must(runtimetesting.RegisterConversions(s))
935
936 conversions := &testConversions{
937 internalToExternalCalls: 0,
938 externalToInternalCalls: 0,
939 }
940
941
942 utilruntime.Must(conversions.registerConversions(s))
943
944 simple := &runtimetesting.InternalSimple{
945 TestString: "foo",
946 }
947
948 out, err := s.ConvertToVersion(simple, externalGV)
949 if err != nil {
950 t.Fatalf("unexpected error: %v", err)
951 }
952
953 internal, err := s.ConvertToVersion(out, internalGV)
954 if err != nil {
955 t.Fatalf("unexpected error: %v", err)
956 }
957
958 if e, a := simple, internal; !reflect.DeepEqual(e, a) {
959 t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
960 }
961
962 if e, a := 1, conversions.internalToExternalCalls; e != a {
963 t.Errorf("Expected %v, got %v", e, a)
964 }
965 if e, a := 1, conversions.externalToInternalCalls; e != a {
966 t.Errorf("Expected %v, got %v", e, a)
967 }
968 }
969
970 func TestMetaValuesUnregisteredConvert(t *testing.T) {
971 type InternalSimple struct {
972 Version string `json:"apiVersion,omitempty"`
973 Kind string `json:"kind,omitempty"`
974 TestString string `json:"testString"`
975 }
976 type ExternalSimple struct {
977 Version string `json:"apiVersion,omitempty"`
978 Kind string `json:"kind,omitempty"`
979 TestString string `json:"testString"`
980 }
981 s := runtime.NewScheme()
982
983
984 internalToExternalCalls := 0
985
986
987 convertSimple := func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
988 out.TestString = in.TestString
989 internalToExternalCalls++
990 return nil
991 }
992 if err := s.AddConversionFunc((*InternalSimple)(nil), (*ExternalSimple)(nil), func(a, b interface{}, scope conversion.Scope) error {
993 return convertSimple(a.(*InternalSimple), b.(*ExternalSimple), scope)
994 }); err != nil {
995 t.Fatalf("unexpected error: %v", err)
996 }
997
998 simple := &InternalSimple{TestString: "foo"}
999 external := &ExternalSimple{}
1000 if err := s.Convert(simple, external, nil); err != nil {
1001 t.Fatalf("Unexpected error: %v", err)
1002 }
1003 if e, a := simple.TestString, external.TestString; e != a {
1004 t.Errorf("Expected %v, got %v", e, a)
1005 }
1006
1007
1008 if e, a := 1, internalToExternalCalls; e != a {
1009 t.Errorf("Expected %v, got %v", e, a)
1010 }
1011 }
1012
View as plain text