1
16
17 package versioning
18
19 import (
20 "fmt"
21 "io"
22 "io/ioutil"
23 "reflect"
24 "testing"
25
26 "k8s.io/apimachinery/pkg/runtime"
27 "k8s.io/apimachinery/pkg/runtime/schema"
28 runtimetesting "k8s.io/apimachinery/pkg/runtime/testing"
29 "k8s.io/apimachinery/pkg/util/diff"
30 )
31
32 type testDecodable struct {
33 Other string
34 Value int `json:"value"`
35 gvk schema.GroupVersionKind
36 }
37
38 func (d *testDecodable) GetObjectKind() schema.ObjectKind { return d }
39 func (d *testDecodable) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk }
40 func (d *testDecodable) GroupVersionKind() schema.GroupVersionKind { return d.gvk }
41 func (d *testDecodable) DeepCopyObject() runtime.Object {
42
43 return d
44 }
45
46 type testNestedDecodable struct {
47 Other string
48 Value int `json:"value"`
49
50 gvk schema.GroupVersionKind
51 nestedCalled bool
52 nestedErr error
53 }
54
55 func (d *testNestedDecodable) GetObjectKind() schema.ObjectKind { return d }
56 func (d *testNestedDecodable) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk }
57 func (d *testNestedDecodable) GroupVersionKind() schema.GroupVersionKind { return d.gvk }
58 func (d *testNestedDecodable) DeepCopyObject() runtime.Object {
59
60 return d
61 }
62
63 func (d *testNestedDecodable) EncodeNestedObjects(e runtime.Encoder) error {
64 d.nestedCalled = true
65 return d.nestedErr
66 }
67
68 func (d *testNestedDecodable) DecodeNestedObjects(_ runtime.Decoder) error {
69 d.nestedCalled = true
70 return d.nestedErr
71 }
72
73 func TestNestedDecode(t *testing.T) {
74 n := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode")}
75 decoder := &mockSerializer{obj: n}
76 codec := NewCodec(nil, decoder, nil, nil, nil, nil, nil, nil, "TestNestedDecode")
77 if _, _, err := codec.Decode([]byte(`{}`), nil, n); err != n.nestedErr {
78 t.Errorf("unexpected error: %v", err)
79 }
80 if !n.nestedCalled {
81 t.Errorf("did not invoke nested decoder")
82 }
83 }
84
85 func TestNestedDecodeStrictDecodingError(t *testing.T) {
86 strictErr := runtime.NewStrictDecodingError([]error{fmt.Errorf("duplicate field")})
87 n := &testNestedDecodable{nestedErr: strictErr}
88 decoder := &mockSerializer{obj: n}
89 codec := NewCodec(nil, decoder, nil, nil, nil, nil, nil, nil, "TestNestedDecode")
90 o, _, err := codec.Decode([]byte(`{}`), nil, n)
91 if strictErr, ok := runtime.AsStrictDecodingError(err); !ok || err != strictErr {
92 t.Errorf("unexpected error: %v", err)
93 }
94 if o != n {
95 t.Errorf("did not successfully decode with strict decoding error: %v", o)
96 }
97 if !n.nestedCalled {
98 t.Errorf("did not invoke nested decoder")
99 }
100 }
101
102 func TestNestedEncode(t *testing.T) {
103 n := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode")}
104 n2 := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode 2")}
105 encoder := &mockSerializer{obj: n}
106 codec := NewCodec(
107 encoder, nil,
108 &checkConvertor{obj: n2, groupVersion: schema.GroupVersion{Group: "other"}},
109 nil,
110 &mockTyper{gvks: []schema.GroupVersionKind{{Kind: "test"}}},
111 nil,
112 schema.GroupVersion{Group: "other"}, nil,
113 "TestNestedEncode",
114 )
115 if err := codec.Encode(n, ioutil.Discard); err != n2.nestedErr {
116 t.Errorf("unexpected error: %v", err)
117 }
118 if n.nestedCalled || !n2.nestedCalled {
119 t.Errorf("did not invoke correct nested decoder")
120 }
121 }
122
123 func TestNestedEncodeError(t *testing.T) {
124 n := &testNestedDecodable{nestedErr: fmt.Errorf("unable to encode")}
125 gvk1 := schema.GroupVersionKind{Kind: "test", Group: "other", Version: "v1"}
126 gvk2 := schema.GroupVersionKind{Kind: "test", Group: "other", Version: "v2"}
127 n.SetGroupVersionKind(gvk1)
128 encoder := &mockSerializer{obj: n}
129 codec := NewCodec(
130 encoder, nil,
131 &mockConvertor{},
132 nil,
133 &mockTyper{gvks: []schema.GroupVersionKind{gvk1, gvk2}},
134 nil,
135 schema.GroupVersion{Group: "other", Version: "v2"}, nil,
136 "TestNestedEncodeError",
137 )
138 if err := codec.Encode(n, ioutil.Discard); err != n.nestedErr {
139 t.Errorf("unexpected error: %v", err)
140 }
141 if n.GroupVersionKind() != gvk1 {
142 t.Errorf("unexpected gvk of input object: %v", n.GroupVersionKind())
143 }
144 }
145
146 func TestDecode(t *testing.T) {
147 gvk1 := &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}
148 decodable1 := &testDecodable{}
149 decodable2 := &testDecodable{}
150 decodable3 := &testDecodable{}
151
152 testCases := []struct {
153 serializer runtime.Serializer
154 convertor runtime.ObjectConvertor
155 creater runtime.ObjectCreater
156 typer runtime.ObjectTyper
157 defaulter runtime.ObjectDefaulter
158 yaml bool
159 pretty bool
160
161 encodes, decodes runtime.GroupVersioner
162
163 defaultGVK *schema.GroupVersionKind
164 into runtime.Object
165
166 errFn func(error) bool
167 expectedObject runtime.Object
168 sameObject runtime.Object
169 expectedGVK *schema.GroupVersionKind
170 }{
171 {
172 serializer: &mockSerializer{actual: gvk1},
173 convertor: &checkConvertor{groupVersion: schema.GroupVersion{Group: "other", Version: runtime.APIVersionInternal}},
174 expectedGVK: gvk1,
175 decodes: schema.GroupVersion{Group: "other", Version: runtime.APIVersionInternal},
176 },
177 {
178 serializer: &mockSerializer{actual: gvk1, obj: decodable1},
179 convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: schema.GroupVersion{Group: "other", Version: runtime.APIVersionInternal}},
180 expectedGVK: gvk1,
181 sameObject: decodable2,
182 decodes: schema.GroupVersion{Group: "other", Version: runtime.APIVersionInternal},
183 },
184
185 {
186 serializer: &mockSerializer{actual: gvk1, obj: decodable1},
187 defaultGVK: &schema.GroupVersionKind{Group: "force"},
188 convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: schema.GroupVersion{Group: "force", Version: runtime.APIVersionInternal}},
189 expectedGVK: gvk1,
190 sameObject: decodable2,
191 decodes: schema.GroupVersion{Group: "force", Version: runtime.APIVersionInternal},
192 },
193
194 {
195 into: decodable3,
196 serializer: &mockSerializer{actual: gvk1, obj: decodable1},
197 convertor: &checkConvertor{in: decodable1, obj: decodable3, directConvert: true},
198 expectedGVK: gvk1,
199 sameObject: decodable3,
200 },
201
202 {
203 decodes: schema.GroupVersions{gvk1.GroupVersion()},
204
205 serializer: &mockSerializer{actual: gvk1, obj: decodable1},
206 convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: schema.GroupVersions{{Group: "other", Version: "blah"}}},
207 expectedGVK: gvk1,
208 expectedObject: decodable1,
209 },
210 }
211
212 for i, test := range testCases {
213 t.Logf("%d", i)
214 s := NewCodec(test.serializer, test.serializer, test.convertor, test.creater, test.typer, test.defaulter, test.encodes, test.decodes, fmt.Sprintf("mock-%d", i))
215 obj, gvk, err := s.Decode([]byte(`{}`), test.defaultGVK, test.into)
216
217 if !reflect.DeepEqual(test.expectedGVK, gvk) {
218 t.Errorf("%d: unexpected GVK: %v", i, gvk)
219 }
220
221 switch {
222 case err == nil && test.errFn != nil:
223 t.Errorf("%d: failed: %v", i, err)
224 continue
225 case err != nil && test.errFn == nil:
226 t.Errorf("%d: failed: %v", i, err)
227 continue
228 case err != nil:
229 if !test.errFn(err) {
230 t.Errorf("%d: failed: %v", i, err)
231 }
232 if obj != nil {
233 t.Errorf("%d: should have returned nil object", i)
234 }
235 continue
236 }
237
238 if test.into != nil && test.into != obj {
239 t.Errorf("%d: expected into to be returned: %v", i, obj)
240 continue
241 }
242
243 switch {
244 case test.expectedObject != nil:
245 if !reflect.DeepEqual(test.expectedObject, obj) {
246 t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintSideBySide(test.expectedObject, obj))
247 }
248 case test.sameObject != nil:
249 if test.sameObject != obj {
250 t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintSideBySide(test.sameObject, obj))
251 }
252 case obj != nil:
253 t.Errorf("%d: unexpected object: %#v", i, obj)
254 }
255 }
256 }
257
258 type checkConvertor struct {
259 err error
260 in, obj runtime.Object
261 groupVersion runtime.GroupVersioner
262 directConvert bool
263 }
264
265 func (c *checkConvertor) Convert(in, out, context interface{}) error {
266 if !c.directConvert {
267 return fmt.Errorf("unexpected call to Convert")
268 }
269 if c.in != nil && c.in != in {
270 return fmt.Errorf("unexpected in: %s", in)
271 }
272 if c.obj != nil && c.obj != out {
273 return fmt.Errorf("unexpected out: %s", out)
274 }
275 return c.err
276 }
277 func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (out runtime.Object, err error) {
278 if c.directConvert {
279 return nil, fmt.Errorf("unexpected call to ConvertToVersion")
280 }
281 if c.in != nil && c.in != in {
282 return nil, fmt.Errorf("unexpected in: %s", in)
283 }
284 if !reflect.DeepEqual(c.groupVersion, outVersion) {
285 return nil, fmt.Errorf("unexpected outversion: %s (%s)", outVersion, c.groupVersion)
286 }
287 return c.obj, c.err
288 }
289 func (c *checkConvertor) ConvertFieldLabel(gvk schema.GroupVersionKind, label, value string) (string, string, error) {
290 return "", "", fmt.Errorf("unexpected call to ConvertFieldLabel")
291 }
292
293 type mockConvertor struct {
294 }
295
296 func (c *mockConvertor) Convert(in, out, context interface{}) error {
297 return fmt.Errorf("unexpect call to Convert")
298 }
299
300 func (c *mockConvertor) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (out runtime.Object, err error) {
301 objectKind := in.GetObjectKind()
302 inGVK := objectKind.GroupVersionKind()
303 if out, ok := outVersion.KindForGroupVersionKinds([]schema.GroupVersionKind{inGVK}); ok {
304 objectKind.SetGroupVersionKind(out)
305 } else {
306 return nil, fmt.Errorf("unexpected conversion")
307 }
308 return in, nil
309 }
310
311 func (c *mockConvertor) ConvertFieldLabel(gvk schema.GroupVersionKind, label, value string) (string, string, error) {
312 return "", "", fmt.Errorf("unexpected call to ConvertFieldLabel")
313 }
314
315 type mockSerializer struct {
316 err error
317 obj runtime.Object
318 encodingObjGVK schema.GroupVersionKind
319
320 defaults, actual *schema.GroupVersionKind
321 into runtime.Object
322 }
323
324 func (s *mockSerializer) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
325 s.defaults = defaults
326 s.into = into
327 return s.obj, s.actual, s.err
328 }
329
330 func (s *mockSerializer) Encode(obj runtime.Object, w io.Writer) error {
331 s.obj = obj
332 s.encodingObjGVK = obj.GetObjectKind().GroupVersionKind()
333 return s.err
334 }
335
336 func (s *mockSerializer) Identifier() runtime.Identifier {
337 return runtime.Identifier("mock")
338 }
339
340 type mockTyper struct {
341 gvks []schema.GroupVersionKind
342 unversioned bool
343 err error
344 }
345
346 func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
347 return t.gvks, t.unversioned, t.err
348 }
349
350 func (t *mockTyper) Recognizes(_ schema.GroupVersionKind) bool {
351 return true
352 }
353
354 func TestDirectCodecEncode(t *testing.T) {
355 serializer := mockSerializer{}
356 typer := mockTyper{
357 gvks: []schema.GroupVersionKind{
358 {
359 Group: "wrong_group",
360 Kind: "some_kind",
361 },
362 {
363 Group: "expected_group",
364 Kind: "some_kind",
365 },
366 },
367 }
368
369 c := runtime.WithVersionEncoder{
370 Version: schema.GroupVersion{Group: "expected_group"},
371 Encoder: &serializer,
372 ObjectTyper: &typer,
373 }
374 c.Encode(&testDecodable{}, ioutil.Discard)
375 if e, a := "expected_group", serializer.encodingObjGVK.Group; e != a {
376 t.Errorf("expected group to be %v, got %v", e, a)
377 }
378 }
379
380 func TestCacheableObject(t *testing.T) {
381 gvk1 := schema.GroupVersionKind{Group: "group", Version: "version1", Kind: "MockCacheableObject"}
382 gvk2 := schema.GroupVersionKind{Group: "group", Version: "version2", Kind: "MockCacheableObject"}
383
384 encoder := NewCodec(
385 &mockSerializer{}, &mockSerializer{},
386 &mockConvertor{}, nil,
387 &mockTyper{gvks: []schema.GroupVersionKind{gvk1, gvk2}}, nil,
388 gvk1.GroupVersion(), gvk2.GroupVersion(),
389 "TestCacheableObject")
390
391 runtimetesting.CacheableObjectTest(t, encoder)
392 }
393
394 func BenchmarkIdentifier(b *testing.B) {
395 encoder := &mockSerializer{}
396 gv := schema.GroupVersion{Group: "group", Version: "version"}
397
398 for i := 0; i < b.N; i++ {
399 id := identifier(gv, encoder)
400
401 if id[0] != '{' {
402 b.Errorf("unexpected identifier: %s", id)
403 }
404 }
405 }
406
View as plain text