github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/runtime/serializer/versioning/versioning_test.go (about) 1 /* 2 Copyright 2015 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 versioning 18 19 import ( 20 "fmt" 21 "io" 22 "io/ioutil" 23 "reflect" 24 "testing" 25 26 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime" 27 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema" 28 runtimetesting "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/testing" 29 "github.com/spotmaxtech/k8s-apimachinery-v0260/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 // no real deepcopy because these tests check for pointer equality 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 // no real deepcopy because these tests check for pointer equality 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 // defaultGVK.Group is allowed to force a conversion to the destination group 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 // uses direct conversion for into when objects differ 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 // decode into the same version as the serialized object 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 // Avoid optimizing by compiler. 401 if id[0] != '{' { 402 b.Errorf("unexpected identifier: %s", id) 403 } 404 } 405 }