...

Source file src/k8s.io/apimachinery/pkg/runtime/scheme_test.go

Documentation: k8s.io/apimachinery/pkg/runtime

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    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  	// If set, would clear TypeMeta during conversion.
    80  	//scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{})
    81  
    82  	// test that scheme is an ObjectTyper
    83  	var _ runtime.ObjectTyper = scheme
    84  
    85  	conversions := &testConversions{
    86  		internalToExternalCalls: 0,
    87  		externalToInternalCalls: 0,
    88  	}
    89  
    90  	// Register functions to verify that scope.Meta() gets set correctly.
    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  		// clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion
   125  		// does not automatically clear TypeMeta anymore).
   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  	// register external first when the object is the same in both schemes, so ObjectVersionAndKind reports the
   302  	// external version.
   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  			// apiVersion is set in the serialized object for easier consumption by clients
   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  			// apiVersion is set in the serialized object for easier consumption by clients
   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  	// object is serialized as an unversioned object (in the group and version it was defined in)
   448  	if *gvk != externalGV.WithKind("InternalSimple") {
   449  		t.Errorf("unexpected gvk returned by decode: %#v", *gvk)
   450  	}
   451  
   452  	// when serialized to a different group, the object is kept in its preferred name
   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  // Returns a new Scheme set up with the test objects.
   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  	// Ordinarily, we wouldn't add TestType2, but because this is a test and
   473  	// both types are from the same package, we need to get it into the system
   474  	// so that converter will match it with ExternalType2.
   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  // redefine InternalSimple with the same name, but obviously as a different type than in runtimetesting
   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  		// errors if the type is not registered in the scheme
   628  		{
   629  			scheme: GetTestScheme(),
   630  			in:     &runtimetesting.UnknownType{},
   631  			errFn:  func(err error) bool { return err != nil && runtime.IsNotRegisteredError(err) },
   632  		},
   633  		// errors if the group versioner returns no target
   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  		// converts to internal
   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  		// converts from unstructured to internal
   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  		// converts from unstructured to external
   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  		// prefers the best match
   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  		// unversioned type returned as-is
   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  		// unversioned type returned when not included in the target types
   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  		// detected as already being in the target version
   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  		// detected as already being in the first target version
   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  		// detected as already being in the first target version
   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  		// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (1/3): different kind
   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  		// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (2/3): different gv
   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  		// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (3/3): different gvk
   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  		// multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
   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  		// multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
   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  		// 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)
   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  		// multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
   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  		// multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
   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  		// group versioner can choose a particular target kind for a given input when kind is the same across group versions
   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  		// group versioner can choose a different kind
   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  		// converts from internal to unstructured, given a target version
   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  	// Register functions to verify that scope.Meta() gets set correctly.
   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  	// We deliberately don't register the types.
   983  
   984  	internalToExternalCalls := 0
   985  
   986  	// Register functions to verify that scope.Meta() gets set correctly.
   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  	// Verify that our conversion handler got called.
  1008  	if e, a := 1, internalToExternalCalls; e != a {
  1009  		t.Errorf("Expected %v, got %v", e, a)
  1010  	}
  1011  }
  1012  

View as plain text