github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/runtime/serializer/json/json_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 json_test 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 "testing" 24 25 metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1" 26 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured" 27 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime" 28 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema" 29 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/serializer/json" 30 runtimetesting "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/testing" 31 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/diff" 32 ) 33 34 type testDecodable struct { 35 metav1.TypeMeta `json:",inline"` 36 37 Other string 38 Value int `json:"value"` 39 Spec DecodableSpec `json:"spec"` 40 Interface interface{} `json:"interface"` 41 } 42 43 // DecodableSpec has 15 fields. 44 type DecodableSpec struct { 45 A int `json:"A"` 46 B int `json:"B"` 47 C int `json:"C"` 48 D int `json:"D"` 49 E int `json:"E"` 50 F int `json:"F"` 51 G int `json:"G"` 52 H int `json:"h"` 53 I int `json:"i"` 54 J int `json:"j"` 55 K int `json:"k"` 56 L int `json:"l"` 57 M int `json:"m"` 58 N int `json:"n"` 59 O int `json:"o"` 60 } 61 62 func (d *testDecodable) DeepCopyObject() runtime.Object { 63 if d == nil { 64 return nil 65 } 66 out := new(testDecodable) 67 d.DeepCopyInto(out) 68 return out 69 } 70 func (d *testDecodable) DeepCopyInto(out *testDecodable) { 71 *out = *d 72 out.Other = d.Other 73 out.Value = d.Value 74 out.Spec = d.Spec 75 out.Interface = d.Interface 76 return 77 } 78 79 type testDecodeCoercion struct { 80 metav1.TypeMeta `json:",inline"` 81 82 Bool bool `json:"bool"` 83 84 Int int `json:"int"` 85 Int32 int `json:"int32"` 86 Int64 int `json:"int64"` 87 88 Float32 float32 `json:"float32"` 89 Float64 float64 `json:"float64"` 90 91 String string `json:"string"` 92 93 Struct testDecodable `json:"struct"` 94 95 Array []string `json:"array"` 96 Map map[string]string `json:"map"` 97 } 98 99 func (d *testDecodeCoercion) DeepCopyObject() runtime.Object { 100 if d == nil { 101 return nil 102 } 103 out := new(testDecodeCoercion) 104 d.DeepCopyInto(out) 105 return out 106 } 107 func (d *testDecodeCoercion) DeepCopyInto(out *testDecodeCoercion) { 108 *out = *d 109 return 110 } 111 112 func TestDecode(t *testing.T) { 113 type testCase struct { 114 creater runtime.ObjectCreater 115 typer runtime.ObjectTyper 116 yaml bool 117 pretty bool 118 strict bool 119 120 data []byte 121 defaultGVK *schema.GroupVersionKind 122 into runtime.Object 123 124 errFn func(error) bool 125 expectedObject runtime.Object 126 expectedGVK *schema.GroupVersionKind 127 } 128 129 testCases := []testCase{ 130 // missing metadata without into, typed creater 131 { 132 data: []byte("{}"), 133 134 expectedGVK: &schema.GroupVersionKind{}, 135 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 136 }, 137 { 138 data: []byte("{}"), 139 140 expectedGVK: &schema.GroupVersionKind{}, 141 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 142 strict: true, 143 }, 144 145 { 146 data: []byte(`{"kind":"Foo"}`), 147 148 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"}, 149 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") }, 150 }, 151 { 152 data: []byte(`{"kind":"Foo"}`), 153 154 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"}, 155 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") }, 156 strict: true, 157 }, 158 159 { 160 data: []byte(`{"apiVersion":"foo/v1"}`), 161 162 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"}, 163 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 164 }, 165 { 166 data: []byte(`{"apiVersion":"foo/v1"}`), 167 168 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"}, 169 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 170 strict: true, 171 }, 172 173 { 174 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`), 175 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 176 creater: &mockCreater{obj: &testDecodable{}}, 177 178 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "/v1", Kind: "Foo"}}, 179 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"}, 180 }, 181 { 182 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`), 183 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 184 creater: &mockCreater{obj: &testDecodable{}}, 185 186 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "/v1", Kind: "Foo"}}, 187 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"}, 188 strict: true, 189 }, 190 191 // missing metadata with unstructured into 192 { 193 data: []byte("{}"), 194 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 195 into: &unstructured.Unstructured{}, 196 197 expectedGVK: &schema.GroupVersionKind{}, 198 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 199 }, 200 { 201 data: []byte("{}"), 202 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 203 into: &unstructured.Unstructured{}, 204 205 expectedGVK: &schema.GroupVersionKind{}, 206 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 207 strict: true, 208 }, 209 210 { 211 data: []byte(`{"kind":"Foo"}`), 212 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 213 into: &unstructured.Unstructured{}, 214 215 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"}, 216 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"kind": "Foo"}}, 217 // TODO(109023): expect this to error; unstructured decoding currently only requires kind to be set, not apiVersion 218 }, 219 { 220 data: []byte(`{"kind":"Foo"}`), 221 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 222 into: &unstructured.Unstructured{}, 223 224 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"}, 225 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"kind": "Foo"}}, 226 strict: true, 227 // TODO(109023): expect this to error; unstructured decoding currently only requires kind to be set, not apiVersion 228 }, 229 230 { 231 data: []byte(`{"apiVersion":"foo/v1"}`), 232 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 233 into: &unstructured.Unstructured{}, 234 235 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"}, 236 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 237 }, 238 { 239 data: []byte(`{"apiVersion":"foo/v1"}`), 240 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 241 into: &unstructured.Unstructured{}, 242 243 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"}, 244 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 245 strict: true, 246 }, 247 248 { 249 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`), 250 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 251 into: &unstructured.Unstructured{}, 252 253 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"}, 254 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}}, 255 }, 256 { 257 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`), 258 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 259 into: &unstructured.Unstructured{}, 260 261 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"}, 262 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}}, 263 strict: true, 264 }, 265 266 // missing metadata with unstructured into providing metadata 267 { 268 data: []byte("{}"), 269 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 270 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}}, 271 272 expectedGVK: &schema.GroupVersionKind{}, 273 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 274 }, 275 { 276 data: []byte("{}"), 277 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 278 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}}, 279 280 expectedGVK: &schema.GroupVersionKind{}, 281 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 282 strict: true, 283 }, 284 285 { 286 data: []byte(`{"kind":"Foo"}`), 287 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 288 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}}, 289 290 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"}, 291 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"kind": "Foo"}}, 292 // TODO(109023): expect this to error; unstructured decoding currently only requires kind to be set, not apiVersion 293 }, 294 { 295 data: []byte(`{"kind":"Foo"}`), 296 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 297 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}}, 298 299 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"}, 300 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"kind": "Foo"}}, 301 strict: true, 302 // TODO(109023): expect this to error; unstructured decoding currently only requires kind to be set, not apiVersion 303 }, 304 305 { 306 data: []byte(`{"apiVersion":"foo/v1"}`), 307 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 308 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}}, 309 310 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"}, 311 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 312 }, 313 { 314 data: []byte(`{"apiVersion":"foo/v1"}`), 315 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 316 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}}, 317 318 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"}, 319 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 320 strict: true, 321 }, 322 323 { 324 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`), 325 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 326 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}}, 327 328 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"}, 329 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}}, 330 }, 331 { 332 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`), 333 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 334 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}}, 335 336 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"}, 337 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}}, 338 strict: true, 339 }, 340 341 // missing metadata without into, unstructured creater 342 { 343 data: []byte("{}"), 344 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 345 creater: &mockCreater{obj: &unstructured.Unstructured{}}, 346 347 expectedGVK: &schema.GroupVersionKind{}, 348 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 349 }, 350 { 351 data: []byte("{}"), 352 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 353 creater: &mockCreater{obj: &unstructured.Unstructured{}}, 354 355 expectedGVK: &schema.GroupVersionKind{}, 356 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 357 strict: true, 358 }, 359 360 { 361 data: []byte(`{"kind":"Foo"}`), 362 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 363 creater: &mockCreater{obj: &unstructured.Unstructured{}}, 364 365 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"}, 366 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") }, 367 }, 368 { 369 data: []byte(`{"kind":"Foo"}`), 370 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 371 creater: &mockCreater{obj: &unstructured.Unstructured{}}, 372 373 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"}, 374 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") }, 375 strict: true, 376 }, 377 378 { 379 data: []byte(`{"apiVersion":"foo/v1"}`), 380 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 381 creater: &mockCreater{obj: &unstructured.Unstructured{}}, 382 383 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"}, 384 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 385 }, 386 { 387 data: []byte(`{"apiVersion":"foo/v1"}`), 388 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 389 creater: &mockCreater{obj: &unstructured.Unstructured{}}, 390 391 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"}, 392 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, 393 strict: true, 394 }, 395 396 { 397 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`), 398 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 399 creater: &mockCreater{obj: &unstructured.Unstructured{}}, 400 401 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"}, 402 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}}, 403 }, 404 { 405 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`), 406 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 407 creater: &mockCreater{obj: &unstructured.Unstructured{}}, 408 409 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"}, 410 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}}, 411 strict: true, 412 }, 413 414 // creator errors 415 { 416 data: []byte("{}"), 417 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 418 creater: &mockCreater{err: fmt.Errorf("fake error")}, 419 420 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 421 errFn: func(err error) bool { return err.Error() == "fake error" }, 422 }, 423 { 424 data: []byte("{}"), 425 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 426 creater: &mockCreater{err: fmt.Errorf("fake error")}, 427 428 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 429 errFn: func(err error) bool { return err.Error() == "fake error" }, 430 }, 431 // creator typed 432 { 433 data: []byte("{}"), 434 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 435 creater: &mockCreater{obj: &testDecodable{}}, 436 expectedObject: &testDecodable{}, 437 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 438 }, 439 { 440 data: []byte("{}"), 441 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 442 creater: &mockCreater{obj: &testDecodable{}}, 443 expectedObject: &testDecodable{}, 444 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 445 strict: true, 446 }, 447 448 // version without group is not defaulted 449 { 450 data: []byte(`{"apiVersion":"blah"}`), 451 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 452 creater: &mockCreater{obj: &testDecodable{}}, 453 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "blah"}}, 454 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"}, 455 }, 456 // group without version is defaulted 457 { 458 data: []byte(`{"apiVersion":"other/"}`), 459 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 460 creater: &mockCreater{obj: &testDecodable{}}, 461 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "other/"}}, 462 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 463 }, 464 // group version, kind is defaulted 465 { 466 data: []byte(`{"apiVersion":"other1/blah1"}`), 467 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 468 creater: &mockCreater{obj: &testDecodable{}}, 469 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "other1/blah1"}}, 470 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other1", Version: "blah1"}, 471 }, 472 // gvk all provided then not defaulted at all 473 { 474 data: []byte(`{"kind":"Test","apiVersion":"other/blah"}`), 475 defaultGVK: &schema.GroupVersionKind{Kind: "Test1", Group: "other1", Version: "blah1"}, 476 creater: &mockCreater{obj: &testDecodable{}}, 477 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"}}, 478 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 479 }, 480 //gvk defaulting if kind not provided in data and defaultGVK use into's kind 481 { 482 data: []byte(`{"apiVersion":"b1/c1"}`), 483 into: &testDecodable{TypeMeta: metav1.TypeMeta{Kind: "a3", APIVersion: "b1/c1"}}, 484 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "a3", Group: "b1", Version: "c1"}}, 485 defaultGVK: nil, 486 creater: &mockCreater{obj: &testDecodable{}}, 487 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{Kind: "a3", APIVersion: "b1/c1"}}, 488 expectedGVK: &schema.GroupVersionKind{Kind: "a3", Group: "b1", Version: "c1"}, 489 }, 490 491 // accept runtime.Unknown as into and bypass creator 492 { 493 data: []byte(`{}`), 494 into: &runtime.Unknown{}, 495 496 expectedGVK: &schema.GroupVersionKind{}, 497 expectedObject: &runtime.Unknown{ 498 Raw: []byte(`{}`), 499 ContentType: runtime.ContentTypeJSON, 500 }, 501 }, 502 { 503 data: []byte(`{"test":"object"}`), 504 into: &runtime.Unknown{}, 505 506 expectedGVK: &schema.GroupVersionKind{}, 507 expectedObject: &runtime.Unknown{ 508 Raw: []byte(`{"test":"object"}`), 509 ContentType: runtime.ContentTypeJSON, 510 }, 511 }, 512 { 513 data: []byte(`{"test":"object"}`), 514 into: &runtime.Unknown{}, 515 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 516 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 517 expectedObject: &runtime.Unknown{ 518 TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, 519 Raw: []byte(`{"test":"object"}`), 520 ContentType: runtime.ContentTypeJSON, 521 }, 522 }, 523 524 // unregistered objects can be decoded into directly 525 { 526 data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`), 527 into: &testDecodable{}, 528 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})}, 529 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 530 expectedObject: &testDecodable{ 531 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, 532 Other: "test", 533 Value: 1, 534 }, 535 }, 536 // registered types get defaulted by the into object kind 537 { 538 data: []byte(`{"value":1,"Other":"test"}`), 539 into: &testDecodable{}, 540 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 541 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 542 expectedObject: &testDecodable{ 543 Other: "test", 544 Value: 1, 545 }, 546 }, 547 // registered types get defaulted by the into object kind even without version, but return an error 548 { 549 data: []byte(`{"value":1,"Other":"test"}`), 550 into: &testDecodable{}, 551 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}}, 552 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}, 553 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") }, 554 expectedObject: &testDecodable{ 555 Other: "test", 556 Value: 1, 557 }, 558 }, 559 // Error on invalid number 560 { 561 data: []byte(`{"kind":"Test","apiVersion":"other/blah","interface":1e1000}`), 562 creater: &mockCreater{obj: &testDecodable{}}, 563 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 564 errFn: func(err error) bool { 565 return strings.Contains(err.Error(), `json: cannot unmarshal number 1e1000 into Go struct field testDecodable.interface of type float64`) 566 }, 567 }, 568 // Unmarshalling is case-sensitive 569 { 570 // "VaLue" should have been "value" 571 data: []byte(`{"kind":"Test","apiVersion":"other/blah","VaLue":1,"Other":"test"}`), 572 into: &testDecodable{}, 573 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})}, 574 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 575 expectedObject: &testDecodable{ 576 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, 577 Other: "test", 578 }, 579 }, 580 // Unmarshalling is case-sensitive for big struct. 581 { 582 // "b" should have been "B", "I" should have been "i" 583 data: []byte(`{"kind":"Test","apiVersion":"other/blah","spec": {"A": 1, "b": 2, "h": 3, "I": 4}}`), 584 into: &testDecodable{}, 585 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})}, 586 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 587 expectedObject: &testDecodable{ 588 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, 589 Spec: DecodableSpec{A: 1, H: 3}, 590 }, 591 }, 592 // Unknown fields should return an error from the strict JSON deserializer. 593 { 594 data: []byte(`{"unknown": 1}`), 595 into: &testDecodable{}, 596 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 597 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 598 errFn: func(err error) bool { 599 return strings.Contains(err.Error(), `unknown field "unknown"`) 600 }, 601 strict: true, 602 }, 603 // Unknown fields should return an error from the strict YAML deserializer. 604 { 605 data: []byte("unknown: 1\n"), 606 into: &testDecodable{}, 607 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 608 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 609 errFn: func(err error) bool { 610 return strings.Contains(err.Error(), `unknown field "unknown"`) 611 }, 612 yaml: true, 613 strict: true, 614 }, 615 // Duplicate fields should return an error from the strict JSON deserializer. 616 { 617 data: []byte(`{"value":1,"value":1}`), 618 into: &testDecodable{}, 619 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 620 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 621 errFn: func(err error) bool { 622 return strings.Contains(err.Error(), `duplicate field "value"`) 623 }, 624 strict: true, 625 }, 626 // Duplicate fields should return an error from the strict YAML deserializer. 627 { 628 data: []byte("value: 1\n" + 629 "value: 1\n"), 630 into: &testDecodable{}, 631 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 632 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 633 errFn: func(err error) bool { 634 return strings.Contains(err.Error(), `"value" already set in map`) 635 }, 636 yaml: true, 637 strict: true, 638 }, 639 // Duplicate fields should return an error from the strict JSON deserializer for unstructured. 640 { 641 data: []byte(`{"kind":"Custom","value":1,"value":1}`), 642 into: &unstructured.Unstructured{}, 643 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 644 expectedGVK: &schema.GroupVersionKind{Kind: "Custom"}, 645 errFn: func(err error) bool { 646 return strings.Contains(err.Error(), `duplicate field "value"`) 647 }, 648 strict: true, 649 }, 650 // Duplicate fields should return an error from the strict YAML deserializer for unstructured. 651 { 652 data: []byte("kind: Custom\n" + 653 "value: 1\n" + 654 "value: 1\n"), 655 into: &unstructured.Unstructured{}, 656 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 657 expectedGVK: &schema.GroupVersionKind{Kind: "Custom"}, 658 errFn: func(err error) bool { 659 return strings.Contains(err.Error(), `"value" already set in map`) 660 }, 661 yaml: true, 662 strict: true, 663 }, 664 // Strict JSON decode into unregistered objects directly. 665 { 666 data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`), 667 into: &testDecodable{}, 668 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})}, 669 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 670 expectedObject: &testDecodable{ 671 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, 672 Other: "test", 673 Value: 1, 674 }, 675 strict: true, 676 }, 677 // Strict YAML decode into unregistered objects directly. 678 { 679 data: []byte("kind: Test\n" + 680 "apiVersion: other/blah\n" + 681 "value: 1\n" + 682 "Other: test\n"), 683 into: &testDecodable{}, 684 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})}, 685 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 686 expectedObject: &testDecodable{ 687 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, 688 Other: "test", 689 Value: 1, 690 }, 691 yaml: true, 692 strict: true, 693 }, 694 // Valid strict JSON decode without GVK. 695 { 696 data: []byte(`{"value":1234}`), 697 into: &testDecodable{}, 698 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 699 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 700 expectedObject: &testDecodable{ 701 Value: 1234, 702 }, 703 strict: true, 704 }, 705 // Valid strict YAML decode without GVK. 706 { 707 data: []byte("value: 1234\n"), 708 into: &testDecodable{}, 709 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 710 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 711 expectedObject: &testDecodable{ 712 Value: 1234, 713 }, 714 yaml: true, 715 strict: true, 716 }, 717 // Invalid strict JSON, results in json parse error: 718 { 719 data: []byte("foo"), 720 into: &unstructured.Unstructured{}, 721 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 722 errFn: func(err error) bool { 723 return strings.Contains(err.Error(), `json parse error: invalid character 'o'`) 724 }, 725 strict: true, 726 }, 727 // empty JSON strict, results in missing kind error 728 { 729 data: []byte("{}"), 730 into: &unstructured.Unstructured{}, 731 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 732 expectedGVK: &schema.GroupVersionKind{}, 733 errFn: func(err error) bool { 734 return strings.Contains(err.Error(), `Object 'Kind' is missing`) 735 }, 736 strict: true, 737 }, 738 // coerce from null 739 { 740 data: []byte(`{"bool":null,"int":null,"int32":null,"int64":null,"float32":null,"float64":null,"string":null,"array":null,"map":null,"struct":null}`), 741 into: &testDecodeCoercion{}, 742 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 743 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 744 expectedObject: &testDecodeCoercion{}, 745 strict: true, 746 }, 747 { 748 data: []byte(`{"bool":null,"int":null,"int32":null,"int64":null,"float32":null,"float64":null,"string":null,"array":null,"map":null,"struct":null}`), 749 into: &testDecodeCoercion{}, 750 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 751 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 752 expectedObject: &testDecodeCoercion{}, 753 yaml: true, 754 strict: true, 755 }, 756 // coerce from string 757 { 758 data: []byte(`{"string":""}`), 759 into: &testDecodeCoercion{}, 760 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 761 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 762 expectedObject: &testDecodeCoercion{}, 763 strict: true, 764 }, 765 { 766 data: []byte(`{"string":""}`), 767 into: &testDecodeCoercion{}, 768 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 769 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 770 expectedObject: &testDecodeCoercion{}, 771 yaml: true, 772 strict: true, 773 }, 774 // coerce from array 775 { 776 data: []byte(`{"array":[]}`), 777 into: &testDecodeCoercion{}, 778 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 779 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 780 expectedObject: &testDecodeCoercion{Array: []string{}}, 781 strict: true, 782 }, 783 { 784 data: []byte(`{"array":[]}`), 785 into: &testDecodeCoercion{}, 786 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 787 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 788 expectedObject: &testDecodeCoercion{Array: []string{}}, 789 yaml: true, 790 strict: true, 791 }, 792 // coerce from map 793 { 794 data: []byte(`{"map":{},"struct":{}}`), 795 into: &testDecodeCoercion{}, 796 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 797 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 798 expectedObject: &testDecodeCoercion{Map: map[string]string{}}, 799 strict: true, 800 }, 801 { 802 data: []byte(`{"map":{},"struct":{}}`), 803 into: &testDecodeCoercion{}, 804 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 805 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 806 expectedObject: &testDecodeCoercion{Map: map[string]string{}}, 807 yaml: true, 808 strict: true, 809 }, 810 // coerce from int 811 { 812 data: []byte(`{"int":1,"int32":1,"int64":1,"float32":1,"float64":1}`), 813 into: &testDecodeCoercion{}, 814 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 815 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 816 expectedObject: &testDecodeCoercion{Int: 1, Int32: 1, Int64: 1, Float32: 1, Float64: 1}, 817 strict: true, 818 }, 819 { 820 data: []byte(`{"int":1,"int32":1,"int64":1,"float32":1,"float64":1}`), 821 into: &testDecodeCoercion{}, 822 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 823 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 824 expectedObject: &testDecodeCoercion{Int: 1, Int32: 1, Int64: 1, Float32: 1, Float64: 1}, 825 yaml: true, 826 strict: true, 827 }, 828 // coerce from float 829 { 830 data: []byte(`{"float32":1.0,"float64":1.0}`), 831 into: &testDecodeCoercion{}, 832 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 833 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 834 expectedObject: &testDecodeCoercion{Float32: 1, Float64: 1}, 835 strict: true, 836 }, 837 { 838 data: []byte(`{"int":1.0,"int32":1.0,"int64":1.0,"float32":1.0,"float64":1.0}`), // floating point gets dropped in yaml -> json step 839 into: &testDecodeCoercion{}, 840 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, 841 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, 842 expectedObject: &testDecodeCoercion{Int: 1, Int32: 1, Int64: 1, Float32: 1, Float64: 1}, 843 yaml: true, 844 strict: true, 845 }, 846 } 847 848 logTestCase := func(t *testing.T, tc testCase) { 849 t.Logf("data=%s\n\tinto=%T, yaml=%v, strict=%v", string(tc.data), tc.into, tc.yaml, tc.strict) 850 } 851 852 for i, test := range testCases { 853 var s runtime.Serializer 854 if test.yaml { 855 s = json.NewSerializerWithOptions(json.DefaultMetaFactory, test.creater, test.typer, json.SerializerOptions{Yaml: test.yaml, Pretty: false, Strict: test.strict}) 856 } else { 857 s = json.NewSerializerWithOptions(json.DefaultMetaFactory, test.creater, test.typer, json.SerializerOptions{Yaml: test.yaml, Pretty: test.pretty, Strict: test.strict}) 858 } 859 obj, gvk, err := s.Decode([]byte(test.data), test.defaultGVK, test.into) 860 861 if !reflect.DeepEqual(test.expectedGVK, gvk) { 862 logTestCase(t, test) 863 t.Errorf("%d: unexpected GVK: %v", i, gvk) 864 } 865 866 switch { 867 case err == nil && test.errFn != nil: 868 logTestCase(t, test) 869 t.Errorf("%d: failed: not getting the expected error", i) 870 continue 871 case err != nil && test.errFn == nil: 872 logTestCase(t, test) 873 t.Errorf("%d: failed: %v", i, err) 874 continue 875 case err != nil: 876 if !test.errFn(err) { 877 logTestCase(t, test) 878 t.Errorf("%d: failed: %v", i, err) 879 } 880 if !runtime.IsStrictDecodingError(err) && obj != nil { 881 logTestCase(t, test) 882 t.Errorf("%d: should have returned nil object", i) 883 } 884 continue 885 } 886 887 if test.into != nil && test.into != obj { 888 logTestCase(t, test) 889 t.Errorf("%d: expected into to be returned: %v", i, obj) 890 continue 891 } 892 893 if !reflect.DeepEqual(test.expectedObject, obj) { 894 logTestCase(t, test) 895 t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintSideBySide(test.expectedObject, obj)) 896 } 897 } 898 } 899 900 func TestCacheableObject(t *testing.T) { 901 gvk := schema.GroupVersionKind{Group: "group", Version: "version", Kind: "MockCacheableObject"} 902 creater := &mockCreater{obj: &runtimetesting.MockCacheableObject{}} 903 typer := &mockTyper{gvk: &gvk} 904 serializer := json.NewSerializer(json.DefaultMetaFactory, creater, typer, false) 905 906 runtimetesting.CacheableObjectTest(t, serializer) 907 } 908 909 type mockCreater struct { 910 apiVersion string 911 kind string 912 err error 913 obj runtime.Object 914 } 915 916 func (c *mockCreater) New(kind schema.GroupVersionKind) (runtime.Object, error) { 917 c.apiVersion, c.kind = kind.GroupVersion().String(), kind.Kind 918 return c.obj, c.err 919 } 920 921 type mockTyper struct { 922 gvk *schema.GroupVersionKind 923 err error 924 } 925 926 func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) { 927 if t.gvk == nil { 928 return nil, false, t.err 929 } 930 return []schema.GroupVersionKind{*t.gvk}, false, t.err 931 } 932 933 func (t *mockTyper) Recognizes(_ schema.GroupVersionKind) bool { 934 return false 935 }