github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/apis/meta/v1/unstructured/unstructured_conversion_test.go (about) 1 /* 2 Copyright 2019 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 unstructured_test 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/stretchr/testify/assert" 27 28 metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1" 29 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured" 30 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/testapigroup" 31 testapigroupv1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/testapigroup/v1" 32 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime" 33 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema" 34 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/test" 35 ) 36 37 func TestObjectToUnstructuredConversion(t *testing.T) { 38 scheme, _ := test.TestScheme() 39 testCases := []struct { 40 name string 41 objectToConvert runtime.Object 42 expectedErr error 43 expectedConvertedUnstructured *unstructured.Unstructured 44 }{ 45 { 46 name: "convert nil object to unstructured should fail", 47 objectToConvert: nil, 48 expectedErr: fmt.Errorf("unable to convert object type <nil> to Unstructured, must be a runtime.Object"), 49 expectedConvertedUnstructured: &unstructured.Unstructured{}, 50 }, 51 { 52 name: "convert versioned empty object to unstructured should work", 53 objectToConvert: &testapigroupv1.Carp{}, 54 expectedConvertedUnstructured: &unstructured.Unstructured{ 55 Object: map[string]interface{}{ 56 "apiVersion": "v1", 57 "kind": "Carp", 58 "metadata": map[string]interface{}{ 59 "creationTimestamp": nil, 60 }, 61 "spec": map[string]interface{}{}, 62 "status": map[string]interface{}{}, 63 }, 64 }, 65 }, 66 { 67 name: "convert valid versioned object to unstructured should work", 68 objectToConvert: &testapigroupv1.Carp{ 69 ObjectMeta: metav1.ObjectMeta{ 70 Name: "noxu", 71 }, 72 Spec: testapigroupv1.CarpSpec{ 73 Hostname: "example.com", 74 }, 75 }, 76 expectedConvertedUnstructured: &unstructured.Unstructured{ 77 Object: map[string]interface{}{ 78 "apiVersion": "v1", 79 "kind": "Carp", 80 "metadata": map[string]interface{}{ 81 "creationTimestamp": nil, 82 "name": "noxu", 83 }, 84 "spec": map[string]interface{}{ 85 "hostname": "example.com", 86 }, 87 "status": map[string]interface{}{}, 88 }, 89 }, 90 }, 91 { 92 name: "convert hub-versioned object to unstructured should fail", 93 objectToConvert: &testapigroup.Carp{}, 94 expectedErr: fmt.Errorf("unable to convert the internal object type *testapigroup.Carp to Unstructured without providing a preferred version to convert to"), 95 }, 96 } 97 for _, testCase := range testCases { 98 t.Run(testCase.name, func(t *testing.T) { 99 outUnstructured := &unstructured.Unstructured{} 100 err := scheme.Convert(testCase.objectToConvert, outUnstructured, nil) 101 if err != nil { 102 assert.Equal(t, testCase.expectedErr, err) 103 return 104 } 105 assert.Equal(t, testCase.expectedConvertedUnstructured, outUnstructured) 106 }) 107 } 108 } 109 110 func TestUnstructuredToObjectConversion(t *testing.T) { 111 scheme, _ := test.TestScheme() 112 testCases := []struct { 113 name string 114 unstructuredToConvert *unstructured.Unstructured 115 convertingObject runtime.Object 116 expectPanic bool 117 expectedErrFunc func(err error) bool 118 expectedConvertedObject runtime.Object 119 }{ 120 { 121 name: "convert empty unstructured w/o gvk to versioned object should fail", 122 unstructuredToConvert: &unstructured.Unstructured{ 123 Object: map[string]interface{}{}, 124 }, 125 convertingObject: &testapigroupv1.Carp{}, 126 expectedErrFunc: func(err error) bool { 127 return reflect.DeepEqual(err, runtime.NewMissingKindErr("unstructured object has no kind")) 128 }, 129 }, 130 { 131 name: "convert empty versioned unstructured to versioned object should work", 132 unstructuredToConvert: &unstructured.Unstructured{ 133 Object: map[string]interface{}{ 134 "apiVersion": "v1", 135 "kind": "Carp", 136 }, 137 }, 138 convertingObject: &testapigroupv1.Carp{}, 139 expectedConvertedObject: &testapigroupv1.Carp{}, 140 }, 141 { 142 name: "convert empty unstructured w/o gvk to versioned object should fail", 143 unstructuredToConvert: &unstructured.Unstructured{ 144 Object: map[string]interface{}{}, 145 }, 146 convertingObject: &testapigroupv1.Carp{}, 147 expectedErrFunc: func(err error) bool { 148 return reflect.DeepEqual(err, runtime.NewMissingKindErr("unstructured object has no kind")) 149 }, 150 }, 151 { 152 name: "convert valid versioned unstructured to versioned object should work", 153 unstructuredToConvert: &unstructured.Unstructured{ 154 Object: map[string]interface{}{ 155 "apiVersion": "v1", 156 "kind": "Carp", 157 "metadata": map[string]interface{}{ 158 "creationTimestamp": nil, 159 "name": "noxu", 160 }, 161 "spec": map[string]interface{}{ 162 "hostname": "example.com", 163 }, 164 "status": map[string]interface{}{}, 165 }, 166 }, 167 convertingObject: &testapigroupv1.Carp{}, 168 expectedConvertedObject: &testapigroupv1.Carp{ 169 ObjectMeta: metav1.ObjectMeta{ 170 Name: "noxu", 171 }, 172 Spec: testapigroupv1.CarpSpec{ 173 Hostname: "example.com", 174 }, 175 }, 176 }, 177 { 178 name: "convert valid versioned unstructured to hub-versioned object should work", 179 unstructuredToConvert: &unstructured.Unstructured{ 180 Object: map[string]interface{}{ 181 "apiVersion": "v1", 182 "kind": "Carp", 183 "metadata": map[string]interface{}{ 184 "creationTimestamp": nil, 185 "name": "noxu", 186 }, 187 "spec": map[string]interface{}{ 188 "hostname": "example.com", 189 }, 190 "status": map[string]interface{}{}, 191 }, 192 }, 193 convertingObject: &testapigroup.Carp{}, 194 expectedConvertedObject: &testapigroup.Carp{ 195 ObjectMeta: metav1.ObjectMeta{ 196 Name: "noxu", 197 }, 198 Spec: testapigroup.CarpSpec{ 199 Hostname: "example.com", 200 }, 201 }, 202 }, 203 { 204 name: "convert unexisting-versioned unstructured to hub-versioned object should fail", 205 unstructuredToConvert: &unstructured.Unstructured{ 206 Object: map[string]interface{}{ 207 "apiVersion": "v9", 208 "kind": "Carp", 209 "metadata": map[string]interface{}{ 210 "creationTimestamp": nil, 211 "name": "noxu", 212 }, 213 "spec": map[string]interface{}{ 214 "hostname": "example.com", 215 }, 216 "status": map[string]interface{}{}, 217 }, 218 }, 219 convertingObject: &testapigroup.Carp{}, 220 expectedErrFunc: func(err error) bool { 221 return reflect.DeepEqual(err, runtime.NewNotRegisteredGVKErrForTarget( 222 scheme.Name(), 223 schema.GroupVersionKind{Group: "", Version: "v9", Kind: "Carp"}, 224 nil, 225 )) 226 }, 227 }, 228 { 229 name: "convert valid versioned unstructured to object w/ a mismatching kind should fail", 230 unstructuredToConvert: &unstructured.Unstructured{ 231 Object: map[string]interface{}{ 232 "apiVersion": "v1", 233 "kind": "Carp", 234 "metadata": map[string]interface{}{ 235 "creationTimestamp": nil, 236 "name": "noxu", 237 }, 238 "spec": map[string]interface{}{ 239 "hostname": "example.com", 240 }, 241 "status": map[string]interface{}{}, 242 }, 243 }, 244 convertingObject: &metav1.CreateOptions{}, 245 expectedErrFunc: func(err error) bool { 246 return strings.HasPrefix(err.Error(), "converting (v1.Carp) to (v1.CreateOptions):") 247 }, 248 }, 249 } 250 for _, testCase := range testCases { 251 t.Run(testCase.name, func(t *testing.T) { 252 defer func() { 253 v := recover() 254 assert.Equal(t, testCase.expectPanic, v != nil, "unexpected panic") 255 }() 256 outObject := testCase.convertingObject.DeepCopyObject() 257 // Convert by specifying destination object 258 err := scheme.Convert(testCase.unstructuredToConvert, outObject, nil) 259 if err != nil { 260 if testCase.expectedErrFunc != nil { 261 if !testCase.expectedErrFunc(err) { 262 t.Errorf("error mismatched: %v", err) 263 } 264 } 265 return 266 } 267 assert.Equal(t, testCase.expectedConvertedObject, outObject) 268 }) 269 } 270 } 271 272 func TestUnstructuredToGVConversion(t *testing.T) { 273 scheme, _ := test.TestScheme() 274 // HACK: registering fake internal/v1beta1 api 275 scheme.AddKnownTypes(schema.GroupVersion{Group: "foo", Version: "v1beta1"}, &testapigroup.Carp{}) 276 scheme.AddKnownTypes(schema.GroupVersion{Group: "foo", Version: "__internal"}, &testapigroup.Carp{}) 277 278 testCases := []struct { 279 name string 280 unstructuredToConvert *unstructured.Unstructured 281 targetGV schema.GroupVersion 282 expectPanic bool 283 expectedErrFunc func(err error) bool 284 expectedConvertedObject runtime.Object 285 }{ 286 { 287 name: "convert versioned unstructured to valid external version should work", 288 unstructuredToConvert: &unstructured.Unstructured{ 289 Object: map[string]interface{}{ 290 "apiVersion": "v1", 291 "kind": "Carp", 292 }, 293 }, 294 targetGV: schema.GroupVersion{Group: "", Version: "v1"}, 295 expectedConvertedObject: &testapigroupv1.Carp{ 296 TypeMeta: metav1.TypeMeta{ 297 APIVersion: "v1", 298 Kind: "Carp", 299 }, 300 }, 301 }, 302 { 303 name: "convert hub-versioned unstructured to hub version should work", 304 unstructuredToConvert: &unstructured.Unstructured{ 305 Object: map[string]interface{}{ 306 "apiVersion": "__internal", 307 "kind": "Carp", 308 }, 309 }, 310 targetGV: schema.GroupVersion{Group: "", Version: "__internal"}, 311 expectedConvertedObject: &testapigroup.Carp{}, 312 }, 313 { 314 name: "convert empty unstructured w/o gvk to versioned should fail", 315 unstructuredToConvert: &unstructured.Unstructured{ 316 Object: map[string]interface{}{}, 317 }, 318 targetGV: schema.GroupVersion{Group: "", Version: "v1"}, 319 expectedErrFunc: func(err error) bool { 320 return reflect.DeepEqual(err, runtime.NewMissingKindErr("unstructured object has no kind")) 321 }, 322 expectedConvertedObject: nil, 323 }, 324 { 325 name: "convert versioned unstructured to mismatching external version should fail", 326 unstructuredToConvert: &unstructured.Unstructured{ 327 Object: map[string]interface{}{ 328 "apiVersion": "v1", 329 "kind": "Carp", 330 }, 331 }, 332 targetGV: schema.GroupVersion{Group: "foo", Version: "v1beta1"}, 333 expectedErrFunc: func(err error) bool { 334 return reflect.DeepEqual(err, runtime.NewNotRegisteredErrForTarget( 335 scheme.Name(), reflect.TypeOf(testapigroupv1.Carp{}), schema.GroupVersion{Group: "foo", Version: "v1beta1"})) 336 }, 337 expectedConvertedObject: nil, 338 }, 339 { 340 name: "convert versioned unstructured to mismatching internal version should fail", 341 unstructuredToConvert: &unstructured.Unstructured{ 342 Object: map[string]interface{}{ 343 "apiVersion": "v1", 344 "kind": "Carp", 345 }, 346 }, 347 targetGV: schema.GroupVersion{Group: "foo", Version: "__internal"}, 348 expectedErrFunc: func(err error) bool { 349 return reflect.DeepEqual(err, runtime.NewNotRegisteredErrForTarget( 350 scheme.Name(), reflect.TypeOf(testapigroupv1.Carp{}), schema.GroupVersion{Group: "foo", Version: "__internal"})) 351 }, 352 expectedConvertedObject: nil, 353 }, 354 { 355 name: "convert valid versioned unstructured to its own version should work", 356 unstructuredToConvert: &unstructured.Unstructured{ 357 Object: map[string]interface{}{ 358 "apiVersion": "v1", 359 "kind": "Carp", 360 "metadata": map[string]interface{}{ 361 "creationTimestamp": nil, 362 "name": "noxu", 363 }, 364 "spec": map[string]interface{}{ 365 "hostname": "example.com", 366 }, 367 "status": map[string]interface{}{}, 368 }, 369 }, 370 targetGV: schema.GroupVersion{Group: "", Version: "v1"}, 371 expectedConvertedObject: &testapigroupv1.Carp{ 372 TypeMeta: metav1.TypeMeta{ 373 APIVersion: "v1", 374 Kind: "Carp", 375 }, 376 ObjectMeta: metav1.ObjectMeta{ 377 Name: "noxu", 378 }, 379 Spec: testapigroupv1.CarpSpec{ 380 Hostname: "example.com", 381 }, 382 }, 383 }, 384 { 385 name: "convert valid versioned unstructured to hub-version should work ignoring type meta", 386 unstructuredToConvert: &unstructured.Unstructured{ 387 Object: map[string]interface{}{ 388 "apiVersion": "v1", 389 "kind": "Carp", 390 "metadata": map[string]interface{}{ 391 "creationTimestamp": nil, 392 "name": "noxu", 393 }, 394 "spec": map[string]interface{}{ 395 "hostname": "example.com", 396 }, 397 "status": map[string]interface{}{}, 398 }, 399 }, 400 targetGV: schema.GroupVersion{Group: "", Version: "__internal"}, 401 expectedConvertedObject: &testapigroup.Carp{ 402 ObjectMeta: metav1.ObjectMeta{ 403 Name: "noxu", 404 }, 405 Spec: testapigroup.CarpSpec{ 406 Hostname: "example.com", 407 }, 408 }, 409 }, 410 { 411 name: "convert valid versioned unstructured to unexisting version should fail", 412 unstructuredToConvert: &unstructured.Unstructured{ 413 Object: map[string]interface{}{ 414 "apiVersion": "v1", 415 "kind": "Carp", 416 "metadata": map[string]interface{}{ 417 "creationTimestamp": nil, 418 "name": "noxu", 419 }, 420 "spec": map[string]interface{}{ 421 "hostname": "example.com", 422 }, 423 "status": map[string]interface{}{}, 424 }, 425 }, 426 targetGV: schema.GroupVersion{Group: "", Version: "v9"}, 427 expectedErrFunc: func(err error) bool { 428 return reflect.DeepEqual(err, runtime.NewNotRegisteredGVKErrForTarget( 429 scheme.Name(), 430 schema.GroupVersionKind{Group: "", Version: "v9", Kind: "Carp"}, 431 nil, 432 )) 433 }, 434 expectedConvertedObject: nil, 435 }, 436 } 437 for _, testCase := range testCases { 438 t.Run(testCase.name, func(t *testing.T) { 439 defer func() { 440 v := recover() 441 assert.Equal(t, testCase.expectPanic, v != nil, "unexpected panic") 442 }() 443 // Convert by specifying destination object 444 outObject, err := scheme.ConvertToVersion(testCase.unstructuredToConvert, testCase.targetGV) 445 if testCase.expectedErrFunc != nil { 446 if !testCase.expectedErrFunc(err) { 447 t.Errorf("error mismatched: %v", err) 448 } 449 } 450 assert.Equal(t, testCase.expectedConvertedObject, outObject) 451 }) 452 } 453 } 454 455 func TestUnstructuredToUnstructuredConversion(t *testing.T) { 456 // eventually, we don't want any inter-unstructured conversion happen, but for now, the conversion 457 // just copy/pastes 458 scheme, _ := test.TestScheme() 459 inUnstructured := &unstructured.Unstructured{ 460 Object: map[string]interface{}{ 461 "apiVersion": "v1", 462 "kind": "Carp", 463 }, 464 } 465 outUnstructured := &unstructured.Unstructured{} 466 err := scheme.Convert(inUnstructured, outUnstructured, nil) 467 assert.NoError(t, err) 468 assert.Equal(t, inUnstructured, outUnstructured) 469 } 470 471 func benchmarkCarp() *testapigroupv1.Carp { 472 t := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC) 473 return &testapigroupv1.Carp{ 474 ObjectMeta: metav1.ObjectMeta{ 475 Name: "name", 476 Namespace: "namespace", 477 }, 478 Spec: testapigroupv1.CarpSpec{ 479 RestartPolicy: "restart", 480 NodeSelector: map[string]string{ 481 "label1": "value1", 482 "label2": "value2", 483 }, 484 ServiceAccountName: "service-account", 485 HostNetwork: false, 486 HostPID: true, 487 Subdomain: "hostname.subdomain.namespace.svc.domain", 488 }, 489 Status: testapigroupv1.CarpStatus{ 490 Phase: "phase", 491 Conditions: []testapigroupv1.CarpCondition{ 492 { 493 Type: "condition1", 494 Status: "true", 495 LastProbeTime: t, 496 LastTransitionTime: t, 497 Reason: "reason", 498 Message: "message", 499 }, 500 }, 501 Message: "message", 502 Reason: "reason", 503 HostIP: "1.2.3.4", 504 }, 505 } 506 } 507 508 func BenchmarkToUnstructured(b *testing.B) { 509 carp := benchmarkCarp() 510 converter := runtime.DefaultUnstructuredConverter 511 b.ResetTimer() 512 513 for i := 0; i < b.N; i++ { 514 result, err := converter.ToUnstructured(carp) 515 if err != nil { 516 b.Fatalf("Unexpected conversion error: %v", err) 517 } 518 if len(result) != 3 { 519 b.Errorf("Unexpected conversion result: %#v", result) 520 } 521 } 522 } 523 524 func BenchmarkFromUnstructured(b *testing.B) { 525 carp := benchmarkCarp() 526 converter := runtime.DefaultUnstructuredConverter 527 unstr, err := converter.ToUnstructured(carp) 528 if err != nil { 529 b.Fatalf("Unexpected conversion error: %v", err) 530 } 531 b.ResetTimer() 532 533 for i := 0; i < b.N; i++ { 534 result := testapigroupv1.Carp{} 535 if err := converter.FromUnstructured(unstr, &result); err != nil { 536 b.Fatalf("Unexpected conversion error: %v", err) 537 } 538 if result.Status.Phase != "phase" { 539 b.Errorf("Unexpected conversion result: %#v", result) 540 } 541 } 542 }