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