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 }