github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/runtime/serializer/codec_test.go (about)

     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 serializer
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"log"
    23  	"os"
    24  	"reflect"
    25  	"strings"
    26  	"testing"
    27  
    28  	metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1"
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/conversion"
    30  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    31  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    32  	runtimetesting "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/testing"
    33  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/diff"
    34  	utilruntime "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/runtime"
    35  
    36  	fuzz "github.com/google/gofuzz"
    37  	flag "github.com/spf13/pflag"
    38  	"sigs.k8s.io/yaml"
    39  )
    40  
    41  var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
    42  
    43  type testMetaFactory struct{}
    44  
    45  func (testMetaFactory) Interpret(data []byte) (*schema.GroupVersionKind, error) {
    46  	findKind := struct {
    47  		APIVersion string `json:"myVersionKey,omitempty"`
    48  		ObjectKind string `json:"myKindKey,omitempty"`
    49  	}{}
    50  	// yaml is a superset of json, so we use it to decode here. That way,
    51  	// we understand both.
    52  	if err := yaml.Unmarshal(data, &findKind); err != nil {
    53  		return nil, fmt.Errorf("couldn't get version/kind: %v", err)
    54  	}
    55  	gv, err := schema.ParseGroupVersion(findKind.APIVersion)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	return &schema.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: findKind.ObjectKind}, nil
    60  }
    61  
    62  // TestObjectFuzzer can randomly populate all the above objects.
    63  var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
    64  	func(j *runtimetesting.MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) {
    65  		c.FuzzNoCustom(j)
    66  		j.APIVersion = ""
    67  		j.ObjectKind = ""
    68  	},
    69  )
    70  
    71  // Returns a new Scheme set up with the test objects.
    72  func GetTestScheme() (*runtime.Scheme, runtime.Codec) {
    73  	internalGV := schema.GroupVersion{Version: runtime.APIVersionInternal}
    74  	externalGV := schema.GroupVersion{Version: "v1"}
    75  	externalGV2 := schema.GroupVersion{Version: "v2"}
    76  
    77  	s := runtime.NewScheme()
    78  	// Ordinarily, we wouldn't add TestType2, but because this is a test and
    79  	// both types are from the same package, we need to get it into the system
    80  	// so that converter will match it with ExternalType2.
    81  	s.AddKnownTypes(internalGV, &runtimetesting.TestType1{}, &runtimetesting.TestType2{}, &runtimetesting.ExternalInternalSame{})
    82  	s.AddKnownTypes(externalGV, &runtimetesting.ExternalInternalSame{})
    83  	s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &runtimetesting.ExternalTestType1{})
    84  	s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &runtimetesting.ExternalTestType2{})
    85  	s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &runtimetesting.TestType1{})
    86  	s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &runtimetesting.ExternalTestType1{})
    87  	s.AddKnownTypeWithName(externalGV2.WithKind("TestType1"), &runtimetesting.ExternalTestType1{})
    88  
    89  	s.AddUnversionedTypes(externalGV, &metav1.Status{})
    90  
    91  	utilruntime.Must(runtimetesting.RegisterConversions(s))
    92  
    93  	cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}, CodecFactoryOptions{Pretty: true, Strict: true}))
    94  	codec := cf.LegacyCodec(schema.GroupVersion{Version: "v1"})
    95  	return s, codec
    96  }
    97  
    98  var semantic = conversion.EqualitiesOrDie(
    99  	func(a, b runtimetesting.MyWeirdCustomEmbeddedVersionKindField) bool {
   100  		a.APIVersion, a.ObjectKind = "", ""
   101  		b.APIVersion, b.ObjectKind = "", ""
   102  		return a == b
   103  	},
   104  )
   105  
   106  func runTest(t *testing.T, source interface{}) {
   107  	name := reflect.TypeOf(source).Elem().Name()
   108  	TestObjectFuzzer.Fuzz(source)
   109  
   110  	_, codec := GetTestScheme()
   111  	data, err := runtime.Encode(codec, source.(runtime.Object))
   112  	if err != nil {
   113  		t.Errorf("%v: %v (%#v)", name, err, source)
   114  		return
   115  	}
   116  	obj2, err := runtime.Decode(codec, data)
   117  	if err != nil {
   118  		t.Errorf("%v: %v (%v)", name, err, string(data))
   119  		return
   120  	}
   121  	if !semantic.DeepEqual(source, obj2) {
   122  		t.Errorf("1: %v: diff: %v", name, diff.ObjectGoPrintSideBySide(source, obj2))
   123  		return
   124  	}
   125  	obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface()
   126  	if err := runtime.DecodeInto(codec, data, obj3.(runtime.Object)); err != nil {
   127  		t.Errorf("2: %v: %v", name, err)
   128  		return
   129  	}
   130  	if !semantic.DeepEqual(source, obj3) {
   131  		t.Errorf("3: %v: diff: %v", name, diff.ObjectDiff(source, obj3))
   132  		return
   133  	}
   134  }
   135  
   136  func TestTypes(t *testing.T) {
   137  	table := []interface{}{
   138  		&runtimetesting.TestType1{},
   139  		&runtimetesting.ExternalInternalSame{},
   140  	}
   141  	for _, item := range table {
   142  		// Try a few times, since runTest uses random values.
   143  		for i := 0; i < *fuzzIters; i++ {
   144  			runTest(t, item)
   145  		}
   146  	}
   147  }
   148  
   149  func TestVersionedEncoding(t *testing.T) {
   150  	s, _ := GetTestScheme()
   151  	cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}, CodecFactoryOptions{Pretty: true, Strict: true}))
   152  	info, _ := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeJSON)
   153  	encoder := info.Serializer
   154  
   155  	codec := cf.EncoderForVersion(encoder, schema.GroupVersion{Version: "v2"})
   156  	out, err := runtime.Encode(codec, &runtimetesting.TestType1{})
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  	if string(out) != `{"myVersionKey":"v2","myKindKey":"TestType1"}`+"\n" {
   161  		t.Fatal(string(out))
   162  	}
   163  
   164  	codec = cf.EncoderForVersion(encoder, schema.GroupVersion{Version: "v3"})
   165  	_, err = runtime.Encode(codec, &runtimetesting.TestType1{})
   166  	if err == nil {
   167  		t.Fatal(err)
   168  	}
   169  
   170  	// unversioned encode with no versions is written directly to wire
   171  	codec = cf.EncoderForVersion(encoder, runtime.InternalGroupVersioner)
   172  	out, err = runtime.Encode(codec, &runtimetesting.TestType1{})
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  	if string(out) != `{}`+"\n" {
   177  		t.Fatal(string(out))
   178  	}
   179  }
   180  
   181  func TestMultipleNames(t *testing.T) {
   182  	_, codec := GetTestScheme()
   183  
   184  	obj, _, err := codec.Decode([]byte(`{"myKindKey":"TestType3","myVersionKey":"v1","A":"value"}`), nil, nil)
   185  	if err != nil {
   186  		t.Fatalf("unexpected error: %v", err)
   187  	}
   188  	internal := obj.(*runtimetesting.TestType1)
   189  	if internal.A != "value" {
   190  		t.Fatalf("unexpected decoded object: %#v", internal)
   191  	}
   192  
   193  	out, err := runtime.Encode(codec, internal)
   194  	if err != nil {
   195  		t.Fatalf("unexpected error: %v", err)
   196  	}
   197  	if !strings.Contains(string(out), `"myKindKey":"TestType1"`) {
   198  		t.Errorf("unexpected encoded output: %s", string(out))
   199  	}
   200  }
   201  
   202  func TestStrictOption(t *testing.T) {
   203  	s, _ := GetTestScheme()
   204  	duplicateKeys := `{"myKindKey":"TestType3","myVersionKey":"v1","myVersionKey":"v1","A":"value"}`
   205  
   206  	strictCodec := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}, CodecFactoryOptions{Pretty: true, Strict: true})).LegacyCodec()
   207  	_, _, err := strictCodec.Decode([]byte(duplicateKeys), nil, nil)
   208  	if !runtime.IsStrictDecodingError(err) {
   209  		t.Fatalf("StrictDecodingError not returned on object with duplicate keys: %v, type: %v", err, reflect.TypeOf(err))
   210  	}
   211  
   212  	nonStrictCodec := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}, CodecFactoryOptions{Pretty: true, Strict: false})).LegacyCodec()
   213  	_, _, err = nonStrictCodec.Decode([]byte(duplicateKeys), nil, nil)
   214  	if runtime.IsStrictDecodingError(err) {
   215  		t.Fatalf("Non-Strict decoder returned a StrictDecodingError: %v", err)
   216  	}
   217  }
   218  
   219  func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) {
   220  	internalGV := schema.GroupVersion{Version: runtime.APIVersionInternal}
   221  	externalGV := schema.GroupVersion{Version: "v1"}
   222  
   223  	s := runtime.NewScheme()
   224  	// create two names internally, with TestType1 being preferred
   225  	s.AddKnownTypeWithName(internalGV.WithKind("TestType1"), &runtimetesting.TestType1{})
   226  	s.AddKnownTypeWithName(internalGV.WithKind("OtherType1"), &runtimetesting.TestType1{})
   227  	// create two names externally, with TestType1 being preferred
   228  	s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &runtimetesting.ExternalTestType1{})
   229  	s.AddKnownTypeWithName(externalGV.WithKind("OtherType1"), &runtimetesting.ExternalTestType1{})
   230  	if err := runtimetesting.RegisterConversions(s); err != nil {
   231  		t.Fatalf("unexpected error; %v", err)
   232  	}
   233  
   234  	ext := &runtimetesting.ExternalTestType1{}
   235  	ext.APIVersion = "v1"
   236  	ext.ObjectKind = "OtherType1"
   237  	ext.A = "test"
   238  	data, err := json.Marshal(ext)
   239  	if err != nil {
   240  		t.Fatalf("unexpected error: %v", err)
   241  	}
   242  	expect := &runtimetesting.TestType1{A: "test"}
   243  
   244  	codec := newCodecFactory(
   245  		s, newSerializersForScheme(s, testMetaFactory{}, CodecFactoryOptions{Pretty: true, Strict: true}),
   246  	).LegacyCodec(schema.GroupVersion{Version: "v1"})
   247  
   248  	obj, err := runtime.Decode(codec, data)
   249  	if err != nil {
   250  		t.Fatalf("unexpected error: %v", err)
   251  	}
   252  	if !semantic.DeepEqual(expect, obj) {
   253  		t.Errorf("unexpected object: %#v", obj)
   254  	}
   255  
   256  	into := &runtimetesting.TestType1{}
   257  	if err := runtime.DecodeInto(codec, data, into); err != nil {
   258  		t.Fatalf("unexpected error: %v", err)
   259  	}
   260  	if !semantic.DeepEqual(expect, into) {
   261  		t.Errorf("unexpected object: %#v", obj)
   262  	}
   263  }
   264  
   265  func TestEncode_Ptr(t *testing.T) {
   266  	_, codec := GetTestScheme()
   267  	tt := &runtimetesting.TestType1{A: "I am a pointer object"}
   268  	data, err := runtime.Encode(codec, tt)
   269  	obj2, err2 := runtime.Decode(codec, data)
   270  	if err != nil || err2 != nil {
   271  		t.Fatalf("Failure: '%v' '%v'\n%s", err, err2, data)
   272  	}
   273  	if _, ok := obj2.(*runtimetesting.TestType1); !ok {
   274  		t.Fatalf("Got wrong type")
   275  	}
   276  	if !semantic.DeepEqual(obj2, tt) {
   277  		t.Errorf("Expected:\n %#v,\n Got:\n %#v", tt, obj2)
   278  	}
   279  }
   280  
   281  func TestBadJSONRejection(t *testing.T) {
   282  	log.SetOutput(os.Stderr)
   283  	_, codec := GetTestScheme()
   284  	badJSONs := [][]byte{
   285  		[]byte(`{"myVersionKey":"v1"}`),                          // Missing kind
   286  		[]byte(`{"myVersionKey":"v1","myKindKey":"bar"}`),        // Unknown kind
   287  		[]byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version
   288  		[]byte(`{"myKindKey":"TestType1"}`),                      // Missing version
   289  	}
   290  	for _, b := range badJSONs {
   291  		if _, err := runtime.Decode(codec, b); err == nil {
   292  			t.Errorf("Did not reject bad json: %s", string(b))
   293  		}
   294  	}
   295  	badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`)
   296  	if err := runtime.DecodeInto(codec, badJSONKindMismatch, &runtimetesting.TestType1{}); err == nil {
   297  		t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
   298  	}
   299  	if err := runtime.DecodeInto(codec, []byte(``), &runtimetesting.TestType1{}); err != nil {
   300  		t.Errorf("Should allow empty decode: %v", err)
   301  	}
   302  	if _, _, err := codec.Decode([]byte(``), &schema.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err == nil {
   303  		t.Errorf("Did not give error for empty data with only kind default")
   304  	}
   305  	if _, _, err := codec.Decode([]byte(`{"myVersionKey":"v1"}`), &schema.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err != nil {
   306  		t.Errorf("Gave error for version and kind default")
   307  	}
   308  	if _, _, err := codec.Decode([]byte(`{"myKindKey":"ExternalInternalSame"}`), &schema.GroupVersionKind{Version: "v1"}, nil); err != nil {
   309  		t.Errorf("Gave error for version and kind default")
   310  	}
   311  	if _, _, err := codec.Decode([]byte(``), &schema.GroupVersionKind{Kind: "ExternalInternalSame", Version: "v1"}, nil); err != nil {
   312  		t.Errorf("Gave error for version and kind defaulted: %v", err)
   313  	}
   314  	if _, err := runtime.Decode(codec, []byte(``)); err == nil {
   315  		t.Errorf("Did not give error for empty data")
   316  	}
   317  }
   318  
   319  // Returns a new Scheme set up with the test objects needed by TestDirectCodec.
   320  func GetDirectCodecTestScheme() *runtime.Scheme {
   321  	internalGV := schema.GroupVersion{Version: runtime.APIVersionInternal}
   322  	externalGV := schema.GroupVersion{Version: "v1"}
   323  
   324  	s := runtime.NewScheme()
   325  	// Ordinarily, we wouldn't add TestType2, but because this is a test and
   326  	// both types are from the same package, we need to get it into the system
   327  	// so that converter will match it with ExternalType2.
   328  	s.AddKnownTypes(internalGV, &runtimetesting.TestType1{})
   329  	s.AddKnownTypes(externalGV, &runtimetesting.ExternalTestType1{})
   330  
   331  	s.AddUnversionedTypes(externalGV, &metav1.Status{})
   332  
   333  	utilruntime.Must(runtimetesting.RegisterConversions(s))
   334  	return s
   335  }
   336  
   337  func TestDirectCodec(t *testing.T) {
   338  	s := GetDirectCodecTestScheme()
   339  	cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}, CodecFactoryOptions{Pretty: true, Strict: true}))
   340  	info, _ := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeJSON)
   341  	serializer := info.Serializer
   342  	df := cf.WithoutConversion()
   343  	ignoredGV, err := schema.ParseGroupVersion("ignored group/ignored version")
   344  	if err != nil {
   345  		t.Fatal(err)
   346  	}
   347  	directEncoder := df.EncoderForVersion(serializer, ignoredGV)
   348  	directDecoder := df.DecoderToVersion(serializer, ignoredGV)
   349  	out, err := runtime.Encode(directEncoder, &runtimetesting.ExternalTestType1{})
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	if string(out) != `{"myVersionKey":"v1","myKindKey":"ExternalTestType1"}`+"\n" {
   354  		t.Fatal(string(out))
   355  	}
   356  	a, _, err := directDecoder.Decode(out, nil, nil)
   357  	if err != nil {
   358  		t.Fatalf("error on Decode: %v", err)
   359  	}
   360  	e := &runtimetesting.ExternalTestType1{
   361  		MyWeirdCustomEmbeddedVersionKindField: runtimetesting.MyWeirdCustomEmbeddedVersionKindField{
   362  			APIVersion: "v1",
   363  			ObjectKind: "ExternalTestType1",
   364  		},
   365  	}
   366  	if !semantic.DeepEqual(e, a) {
   367  		t.Fatalf("expect %v, got %v", e, a)
   368  	}
   369  }