k8s.io/kubernetes@v1.29.3/pkg/apis/core/v1/conversion_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 v1_test 18 19 import ( 20 "encoding/json" 21 "math/rand" 22 "net/url" 23 "reflect" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 appsv1 "k8s.io/api/apps/v1" 29 v1 "k8s.io/api/core/v1" 30 "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" 31 apiequality "k8s.io/apimachinery/pkg/api/equality" 32 "k8s.io/apimachinery/pkg/api/resource" 33 metafuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/runtime" 36 "k8s.io/apimachinery/pkg/util/diff" 37 "k8s.io/kubernetes/pkg/api/legacyscheme" 38 apps "k8s.io/kubernetes/pkg/apis/apps" 39 "k8s.io/kubernetes/pkg/apis/core" 40 corefuzzer "k8s.io/kubernetes/pkg/apis/core/fuzzer" 41 corev1 "k8s.io/kubernetes/pkg/apis/core/v1" 42 utilpointer "k8s.io/utils/pointer" 43 44 // ensure types are installed 45 _ "k8s.io/kubernetes/pkg/apis/core/install" 46 // ensure types are installed corereplicationcontroller<->replicaset conversions 47 _ "k8s.io/kubernetes/pkg/apis/apps/install" 48 ) 49 50 func TestPodLogOptions(t *testing.T) { 51 sinceSeconds := int64(1) 52 sinceTime := metav1.NewTime(time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC).Local()) 53 tailLines := int64(2) 54 limitBytes := int64(3) 55 56 versionedLogOptions := &v1.PodLogOptions{ 57 Container: "mycontainer", 58 Follow: true, 59 Previous: true, 60 SinceSeconds: &sinceSeconds, 61 SinceTime: &sinceTime, 62 Timestamps: true, 63 TailLines: &tailLines, 64 LimitBytes: &limitBytes, 65 } 66 unversionedLogOptions := &core.PodLogOptions{ 67 Container: "mycontainer", 68 Follow: true, 69 Previous: true, 70 SinceSeconds: &sinceSeconds, 71 SinceTime: &sinceTime, 72 Timestamps: true, 73 TailLines: &tailLines, 74 LimitBytes: &limitBytes, 75 } 76 expectedParameters := url.Values{ 77 "container": {"mycontainer"}, 78 "follow": {"true"}, 79 "previous": {"true"}, 80 "sinceSeconds": {"1"}, 81 "sinceTime": {"2000-01-01T12:34:56Z"}, 82 "timestamps": {"true"}, 83 "tailLines": {"2"}, 84 "limitBytes": {"3"}, 85 } 86 87 codec := runtime.NewParameterCodec(legacyscheme.Scheme) 88 89 // unversioned -> query params 90 { 91 actualParameters, err := codec.EncodeParameters(unversionedLogOptions, v1.SchemeGroupVersion) 92 if err != nil { 93 t.Fatal(err) 94 } 95 if !reflect.DeepEqual(actualParameters, expectedParameters) { 96 t.Fatalf("Expected\n%#v\ngot\n%#v", expectedParameters, actualParameters) 97 } 98 } 99 100 // versioned -> query params 101 { 102 actualParameters, err := codec.EncodeParameters(versionedLogOptions, v1.SchemeGroupVersion) 103 if err != nil { 104 t.Fatal(err) 105 } 106 if !reflect.DeepEqual(actualParameters, expectedParameters) { 107 t.Fatalf("Expected\n%#v\ngot\n%#v", expectedParameters, actualParameters) 108 } 109 } 110 111 // query params -> versioned 112 { 113 convertedLogOptions := &v1.PodLogOptions{} 114 err := codec.DecodeParameters(expectedParameters, v1.SchemeGroupVersion, convertedLogOptions) 115 if err != nil { 116 t.Fatal(err) 117 } 118 if !reflect.DeepEqual(convertedLogOptions, versionedLogOptions) { 119 t.Fatalf("Unexpected deserialization:\n%s", diff.ObjectGoPrintSideBySide(versionedLogOptions, convertedLogOptions)) 120 } 121 } 122 123 // query params -> unversioned 124 { 125 convertedLogOptions := &core.PodLogOptions{} 126 err := codec.DecodeParameters(expectedParameters, v1.SchemeGroupVersion, convertedLogOptions) 127 if err != nil { 128 t.Fatal(err) 129 } 130 if !reflect.DeepEqual(convertedLogOptions, unversionedLogOptions) { 131 t.Fatalf("Unexpected deserialization:\n%s", diff.ObjectGoPrintSideBySide(unversionedLogOptions, convertedLogOptions)) 132 } 133 } 134 } 135 136 // TestPodSpecConversion tests that v1.ServiceAccount is an alias for 137 // ServiceAccountName. 138 func TestPodSpecConversion(t *testing.T) { 139 name, other := "foo", "bar" 140 141 // Test internal -> v1. Should have both alias (DeprecatedServiceAccount) 142 // and new field (ServiceAccountName). 143 i := &core.PodSpec{ 144 ServiceAccountName: name, 145 } 146 v := v1.PodSpec{} 147 if err := legacyscheme.Scheme.Convert(i, &v, nil); err != nil { 148 t.Fatalf("unexpected error: %v", err) 149 } 150 if v.ServiceAccountName != name { 151 t.Fatalf("want v1.ServiceAccountName %q, got %q", name, v.ServiceAccountName) 152 } 153 if v.DeprecatedServiceAccount != name { 154 t.Fatalf("want v1.DeprecatedServiceAccount %q, got %q", name, v.DeprecatedServiceAccount) 155 } 156 157 // Test v1 -> internal. Either DeprecatedServiceAccount, ServiceAccountName, 158 // or both should translate to ServiceAccountName. ServiceAccountName wins 159 // if both are set. 160 testCases := []*v1.PodSpec{ 161 // New 162 {ServiceAccountName: name}, 163 // Alias 164 {DeprecatedServiceAccount: name}, 165 // Both: same 166 {ServiceAccountName: name, DeprecatedServiceAccount: name}, 167 // Both: different 168 {ServiceAccountName: name, DeprecatedServiceAccount: other}, 169 } 170 for k, v := range testCases { 171 got := core.PodSpec{} 172 err := legacyscheme.Scheme.Convert(v, &got, nil) 173 if err != nil { 174 t.Fatalf("unexpected error for case %d: %v", k, err) 175 } 176 if got.ServiceAccountName != name { 177 t.Fatalf("want core.ServiceAccountName %q, got %q", name, got.ServiceAccountName) 178 } 179 } 180 } 181 182 func TestResourceListConversion(t *testing.T) { 183 bigMilliQuantity := resource.NewQuantity(resource.MaxMilliValue, resource.DecimalSI) 184 bigMilliQuantity.Add(resource.MustParse("12345m")) 185 186 tests := []struct { 187 input v1.ResourceList 188 expected core.ResourceList 189 }{ 190 { // No changes necessary. 191 input: v1.ResourceList{ 192 v1.ResourceMemory: resource.MustParse("30M"), 193 v1.ResourceCPU: resource.MustParse("100m"), 194 v1.ResourceStorage: resource.MustParse("1G"), 195 }, 196 expected: core.ResourceList{ 197 core.ResourceMemory: resource.MustParse("30M"), 198 core.ResourceCPU: resource.MustParse("100m"), 199 core.ResourceStorage: resource.MustParse("1G"), 200 }, 201 }, 202 { // Nano-scale values should be rounded up to milli-scale. 203 input: v1.ResourceList{ 204 v1.ResourceCPU: resource.MustParse("3.000023m"), 205 v1.ResourceMemory: resource.MustParse("500.000050m"), 206 }, 207 expected: core.ResourceList{ 208 core.ResourceCPU: resource.MustParse("4m"), 209 core.ResourceMemory: resource.MustParse("501m"), 210 }, 211 }, 212 { // Large values should still be accurate. 213 input: v1.ResourceList{ 214 v1.ResourceCPU: bigMilliQuantity.DeepCopy(), 215 v1.ResourceStorage: bigMilliQuantity.DeepCopy(), 216 }, 217 expected: core.ResourceList{ 218 core.ResourceCPU: bigMilliQuantity.DeepCopy(), 219 core.ResourceStorage: bigMilliQuantity.DeepCopy(), 220 }, 221 }, 222 } 223 224 for i, test := range tests { 225 output := core.ResourceList{} 226 227 // defaulting is a separate step from conversion that is applied when reading from the API or from etcd. 228 // perform that step explicitly. 229 corev1.SetDefaults_ResourceList(&test.input) 230 231 err := legacyscheme.Scheme.Convert(&test.input, &output, nil) 232 if err != nil { 233 t.Fatalf("unexpected error for case %d: %v", i, err) 234 } 235 if !apiequality.Semantic.DeepEqual(test.expected, output) { 236 t.Errorf("unexpected conversion for case %d: Expected\n%+v;\nGot\n%+v", i, test.expected, output) 237 } 238 } 239 } 240 241 func TestReplicationControllerConversion(t *testing.T) { 242 // If we start with a RC, we should always have round-trip fidelity. 243 inputs := []*v1.ReplicationController{ 244 { 245 ObjectMeta: metav1.ObjectMeta{ 246 Name: "name", 247 Namespace: "namespace", 248 }, 249 Spec: v1.ReplicationControllerSpec{ 250 Replicas: utilpointer.Int32(1), 251 MinReadySeconds: 32, 252 Selector: map[string]string{"foo": "bar", "bar": "foo"}, 253 Template: &v1.PodTemplateSpec{ 254 ObjectMeta: metav1.ObjectMeta{ 255 Labels: map[string]string{"foo": "bar", "bar": "foo"}, 256 }, 257 Spec: v1.PodSpec{ 258 Containers: []v1.Container{ 259 { 260 Name: "container", 261 Image: "image", 262 }, 263 }, 264 }, 265 }, 266 }, 267 Status: v1.ReplicationControllerStatus{ 268 Replicas: 1, 269 FullyLabeledReplicas: 2, 270 ReadyReplicas: 3, 271 AvailableReplicas: 4, 272 ObservedGeneration: 5, 273 Conditions: []v1.ReplicationControllerCondition{ 274 { 275 Type: v1.ReplicationControllerReplicaFailure, 276 Status: v1.ConditionTrue, 277 LastTransitionTime: metav1.NewTime(time.Unix(123456789, 0)), 278 Reason: "Reason", 279 Message: "Message", 280 }, 281 }, 282 }, 283 }, 284 } 285 286 // Add some fuzzed RCs. 287 apiObjectFuzzer := fuzzer.FuzzerFor(fuzzer.MergeFuzzerFuncs(metafuzzer.Funcs, corefuzzer.Funcs), rand.NewSource(152), legacyscheme.Codecs) 288 for i := 0; i < 100; i++ { 289 rc := &v1.ReplicationController{} 290 apiObjectFuzzer.Fuzz(rc) 291 // Sometimes the fuzzer decides to leave Spec.Template nil. 292 // We can't support that because Spec.Template is not a pointer in RS, 293 // so it will round-trip as non-nil but empty. 294 if rc.Spec.Template == nil { 295 rc.Spec.Template = &v1.PodTemplateSpec{} 296 } 297 // Sometimes the fuzzer decides to insert an empty label key. 298 // This doesn't round-trip properly because it's invalid. 299 if rc.Spec.Selector != nil { 300 delete(rc.Spec.Selector, "") 301 } 302 inputs = append(inputs, rc) 303 } 304 305 // Round-trip the input RCs before converting to RS. 306 for i := range inputs { 307 inputs[i] = roundTrip(t, inputs[i]).(*v1.ReplicationController) 308 } 309 310 for _, in := range inputs { 311 rs := &apps.ReplicaSet{} 312 // Use in.DeepCopy() to avoid sharing pointers with `in`. 313 if err := corev1.Convert_v1_ReplicationController_To_apps_ReplicaSet(in.DeepCopy(), rs, nil); err != nil { 314 t.Errorf("can't convert RC to RS: %v", err) 315 continue 316 } 317 // Round-trip RS before converting back to RC. 318 rs = roundTripRS(t, rs) 319 out := &v1.ReplicationController{} 320 if err := corev1.Convert_apps_ReplicaSet_To_v1_ReplicationController(rs, out, nil); err != nil { 321 t.Errorf("can't convert RS to RC: %v", err) 322 continue 323 } 324 if !apiequality.Semantic.DeepEqual(in, out) { 325 instr, _ := json.MarshalIndent(in, "", " ") 326 outstr, _ := json.MarshalIndent(out, "", " ") 327 t.Errorf("RC-RS conversion round-trip failed:\nin:\n%s\nout:\n%s", instr, outstr) 328 } 329 } 330 } 331 332 func roundTripRS(t *testing.T, rs *apps.ReplicaSet) *apps.ReplicaSet { 333 codec := legacyscheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion) 334 data, err := runtime.Encode(codec, rs) 335 if err != nil { 336 t.Errorf("%v\n %#v", err, rs) 337 return nil 338 } 339 obj2, err := runtime.Decode(codec, data) 340 if err != nil { 341 t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), rs) 342 return nil 343 } 344 return obj2.(*apps.ReplicaSet) 345 } 346 347 func Test_core_PodStatus_to_v1_PodStatus(t *testing.T) { 348 // core to v1 349 testInputs := []core.PodStatus{ 350 { 351 // one IP 352 PodIPs: []core.PodIP{ 353 { 354 IP: "1.1.1.1", 355 }, 356 }, 357 }, 358 { 359 // no ips 360 PodIPs: nil, 361 }, 362 { 363 // list of ips 364 PodIPs: []core.PodIP{ 365 { 366 IP: "1.1.1.1", 367 }, 368 { 369 IP: "2000::", 370 }, 371 }, 372 }, 373 } 374 for i, input := range testInputs { 375 v1PodStatus := v1.PodStatus{} 376 if err := corev1.Convert_core_PodStatus_To_v1_PodStatus(&input, &v1PodStatus, nil); nil != err { 377 t.Errorf("%v: Convert core.PodStatus to v1.PodStatus failed with error %v", i, err.Error()) 378 } 379 380 if len(input.PodIPs) == 0 { 381 // no more work needed 382 continue 383 } 384 // Primary IP was not set.. 385 if len(v1PodStatus.PodIP) == 0 { 386 t.Errorf("%v: Convert core.PodStatus to v1.PodStatus failed out.PodIP is empty, should be %v", i, v1PodStatus.PodIP) 387 } 388 389 // Primary should always == in.PodIPs[0].IP 390 if len(input.PodIPs) > 0 && v1PodStatus.PodIP != input.PodIPs[0].IP { 391 t.Errorf("%v: Convert core.PodStatus to v1.PodStatus failed out.PodIP != in.PodIP[0].IP expected %v found %v", i, input.PodIPs[0].IP, v1PodStatus.PodIP) 392 } 393 // match v1.PodIPs to core.PodIPs 394 for idx := range input.PodIPs { 395 if v1PodStatus.PodIPs[idx].IP != input.PodIPs[idx].IP { 396 t.Errorf("%v: Convert core.PodStatus to v1.PodStatus failed. Expected v1.PodStatus[%v]=%v but found %v", i, idx, input.PodIPs[idx].IP, v1PodStatus.PodIPs[idx].IP) 397 } 398 } 399 } 400 } 401 func Test_v1_PodStatus_to_core_PodStatus(t *testing.T) { 402 asymmetricInputs := []struct { 403 name string 404 in v1.PodStatus 405 out core.PodStatus 406 }{ 407 { 408 name: "mismatched podIP", 409 in: v1.PodStatus{ 410 PodIP: "1.1.2.1", // Older field takes precedence for compatibility with patch by older clients 411 PodIPs: []v1.PodIP{ 412 {IP: "1.1.1.1"}, 413 {IP: "2.2.2.2"}, 414 }, 415 }, 416 out: core.PodStatus{ 417 PodIPs: []core.PodIP{ 418 {IP: "1.1.2.1"}, 419 }, 420 }, 421 }, 422 { 423 name: "matching podIP", 424 in: v1.PodStatus{ 425 PodIP: "1.1.1.1", 426 PodIPs: []v1.PodIP{ 427 {IP: "1.1.1.1"}, 428 {IP: "2.2.2.2"}, 429 }, 430 }, 431 out: core.PodStatus{ 432 PodIPs: []core.PodIP{ 433 {IP: "1.1.1.1"}, 434 {IP: "2.2.2.2"}, 435 }, 436 }, 437 }, 438 { 439 name: "empty podIP", 440 in: v1.PodStatus{ 441 PodIP: "", 442 PodIPs: []v1.PodIP{ 443 {IP: "1.1.1.1"}, 444 {IP: "2.2.2.2"}, 445 }, 446 }, 447 out: core.PodStatus{ 448 PodIPs: []core.PodIP{ 449 {IP: "1.1.1.1"}, 450 {IP: "2.2.2.2"}, 451 }, 452 }, 453 }, 454 } 455 456 // success 457 v1TestInputs := []v1.PodStatus{ 458 // only Primary IP Provided 459 { 460 PodIP: "1.1.1.1", 461 }, 462 { 463 // both are not provided 464 PodIP: "", 465 PodIPs: nil, 466 }, 467 // only list of IPs 468 { 469 PodIPs: []v1.PodIP{ 470 {IP: "1.1.1.1"}, 471 {IP: "2.2.2.2"}, 472 }, 473 }, 474 // Both 475 { 476 PodIP: "1.1.1.1", 477 PodIPs: []v1.PodIP{ 478 {IP: "1.1.1.1"}, 479 {IP: "2.2.2.2"}, 480 }, 481 }, 482 // v4 and v6 483 { 484 PodIP: "1.1.1.1", 485 PodIPs: []v1.PodIP{ 486 {IP: "1.1.1.1"}, 487 {IP: "::1"}, 488 }, 489 }, 490 // v6 and v4 491 { 492 PodIP: "::1", 493 PodIPs: []v1.PodIP{ 494 {IP: "::1"}, 495 {IP: "1.1.1.1"}, 496 }, 497 }, 498 } 499 500 // run asymmetric cases 501 for _, tc := range asymmetricInputs { 502 testInput := tc.in 503 504 corePodStatus := core.PodStatus{} 505 // convert.. 506 if err := corev1.Convert_v1_PodStatus_To_core_PodStatus(&testInput, &corePodStatus, nil); err != nil { 507 t.Errorf("%s: Convert v1.PodStatus to core.PodStatus failed with error:%v for input %+v", tc.name, err.Error(), testInput) 508 } 509 if !reflect.DeepEqual(corePodStatus, tc.out) { 510 t.Errorf("%s: expected %#v, got %#v", tc.name, tc.out.PodIPs, corePodStatus.PodIPs) 511 } 512 } 513 514 // run ok cases 515 for i, testInput := range v1TestInputs { 516 corePodStatus := core.PodStatus{} 517 // convert.. 518 if err := corev1.Convert_v1_PodStatus_To_core_PodStatus(&testInput, &corePodStatus, nil); err != nil { 519 t.Errorf("%v: Convert v1.PodStatus to core.PodStatus failed with error:%v for input %+v", i, err.Error(), testInput) 520 } 521 522 if len(testInput.PodIP) == 0 && len(testInput.PodIPs) == 0 { 523 continue //no more work needed 524 } 525 526 // List should have at least 1 IP == v1.PodIP || v1.PodIPs[0] (whichever provided) 527 if len(testInput.PodIP) > 0 && corePodStatus.PodIPs[0].IP != testInput.PodIP { 528 t.Errorf("%v: Convert v1.PodStatus to core.PodStatus failed. expected corePodStatus.PodIPs[0].ip=%v found %v", i, corePodStatus.PodIPs[0].IP, corePodStatus.PodIPs[0].IP) 529 } 530 531 // walk the list 532 for idx := range testInput.PodIPs { 533 if corePodStatus.PodIPs[idx].IP != testInput.PodIPs[idx].IP { 534 t.Errorf("%v: Convert v1.PodStatus to core.PodStatus failed core.PodIPs[%v]=%v expected %v", i, idx, corePodStatus.PodIPs[idx].IP, testInput.PodIPs[idx].IP) 535 } 536 } 537 538 // if input has a list of IPs 539 // then out put should have the same length 540 if len(testInput.PodIPs) > 0 && len(testInput.PodIPs) != len(corePodStatus.PodIPs) { 541 t.Errorf("%v: Convert v1.PodStatus to core.PodStatus failed len(core.PodIPs) != len(v1.PodStatus.PodIPs) [%v]=[%v]", i, len(corePodStatus.PodIPs), len(testInput.PodIPs)) 542 } 543 } 544 } 545 546 func Test_core_NodeSpec_to_v1_NodeSpec(t *testing.T) { 547 // core to v1 548 testInputs := []core.NodeSpec{ 549 { 550 PodCIDRs: []string{"10.0.0.0/24", "10.0.1.0/24"}, 551 }, 552 { 553 PodCIDRs: nil, 554 }, 555 { 556 PodCIDRs: []string{"10.0.0.0/24"}, 557 }, 558 { 559 PodCIDRs: []string{"ace:cab:deca::/8"}, 560 }, 561 { 562 PodCIDRs: []string{"10.0.0.0/24", "ace:cab:deca::/8"}, 563 }, 564 { 565 PodCIDRs: []string{"ace:cab:deca::/8", "10.0.0.0/24"}, 566 }, 567 } 568 569 for i, testInput := range testInputs { 570 v1NodeSpec := v1.NodeSpec{} 571 // convert 572 if err := corev1.Convert_core_NodeSpec_To_v1_NodeSpec(&testInput, &v1NodeSpec, nil); nil != err { 573 t.Errorf("%v: Convert core.NodeSpec to v1.NodeSpec failed with error %v", i, err.Error()) 574 } 575 576 if len(testInput.PodCIDRs) == 0 { 577 continue // no more work needed 578 } 579 580 // validate results 581 if v1NodeSpec.PodCIDR != testInput.PodCIDRs[0] { 582 t.Errorf("%v: Convert core.NodeSpec to v1.NodeSpec failed. Expected v1.PodCIDR=%v but found %v", i, testInput.PodCIDRs[0], v1NodeSpec.PodCIDR) 583 } 584 585 // match v1.PodIPs to core.PodIPs 586 for idx := range testInput.PodCIDRs { 587 if v1NodeSpec.PodCIDRs[idx] != testInput.PodCIDRs[idx] { 588 t.Errorf("%v: Convert core.NodeSpec to v1.NodeSpec failed. Expected v1.NodeSpec[%v]=%v but found %v", i, idx, testInput.PodCIDRs[idx], v1NodeSpec.PodCIDRs[idx]) 589 } 590 } 591 } 592 } 593 594 func Test_v1_NodeSpec_to_core_NodeSpec(t *testing.T) { 595 asymmetricInputs := []struct { 596 name string 597 in v1.NodeSpec 598 out core.NodeSpec 599 }{ 600 { 601 name: "mismatched podCIDR", 602 in: v1.NodeSpec{ 603 PodCIDR: "10.0.0.0/24", 604 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 605 }, 606 out: core.NodeSpec{ 607 PodCIDRs: []string{"10.0.0.0/24"}, 608 }, 609 }, 610 { 611 name: "unset podCIDR", 612 in: v1.NodeSpec{ 613 PodCIDR: "", 614 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 615 }, 616 out: core.NodeSpec{ 617 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 618 }, 619 }, 620 { 621 name: "matching podCIDR", 622 in: v1.NodeSpec{ 623 PodCIDR: "10.0.1.0/24", 624 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 625 }, 626 out: core.NodeSpec{ 627 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 628 }, 629 }, 630 } 631 632 testInputs := []v1.NodeSpec{ 633 // cidr only - 4 634 { 635 PodCIDR: "10.0.1.0/24", 636 }, 637 // cidr only - 6 638 { 639 PodCIDR: "ace:cab:deca::/8", 640 }, 641 // Both are provided 642 { 643 PodCIDR: "10.0.1.0/24", 644 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 645 }, 646 // list only 647 { 648 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 649 }, 650 // Both are provided 4,6 651 { 652 PodCIDR: "10.0.1.0/24", 653 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 654 }, 655 // Both are provided 6,4 656 { 657 PodCIDR: "ace:cab:deca::/8", 658 PodCIDRs: []string{"ace:cab:deca::/8", "10.0.1.0/24"}, 659 }, 660 // list only 4,6 661 { 662 PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"}, 663 }, 664 // list only 6,4 665 { 666 PodCIDRs: []string{"ace:cab:deca::/8", "10.0.1.0/24"}, 667 }, 668 // no cidr and no cidrs 669 { 670 PodCIDR: "", 671 PodCIDRs: nil, 672 }, 673 } 674 675 // run asymmetric cases 676 for _, tc := range asymmetricInputs { 677 testInput := tc.in 678 679 coreNodeSpec := core.NodeSpec{} 680 // convert.. 681 if err := corev1.Convert_v1_NodeSpec_To_core_NodeSpec(&testInput, &coreNodeSpec, nil); err != nil { 682 t.Errorf("%s: Convert v1.NodeSpec to core.NodeSpec failed with error:%v for input %+v", tc.name, err.Error(), testInput) 683 } 684 if !reflect.DeepEqual(coreNodeSpec, tc.out) { 685 t.Errorf("%s: expected %#v, got %#v", tc.name, tc.out.PodCIDRs, coreNodeSpec.PodCIDRs) 686 } 687 } 688 689 for i, testInput := range testInputs { 690 coreNodeSpec := core.NodeSpec{} 691 if err := corev1.Convert_v1_NodeSpec_To_core_NodeSpec(&testInput, &coreNodeSpec, nil); err != nil { 692 t.Errorf("%v:Convert v1.NodeSpec to core.NodeSpec failed with error:%v", i, err.Error()) 693 } 694 if len(testInput.PodCIDRs) == 0 && len(testInput.PodCIDR) == 0 { 695 continue // no more work needed 696 } 697 if len(testInput.PodCIDR) > 0 && coreNodeSpec.PodCIDRs[0] != testInput.PodCIDR { 698 t.Errorf("%v:Convert v1.NodeSpec to core.NodeSpec failed. expected coreNodeSpec.PodCIDRs[0]=%v found %v", i, testInput.PodCIDR, coreNodeSpec.PodCIDRs[0]) 699 } 700 // match ip list 701 for idx := range testInput.PodCIDRs { 702 if coreNodeSpec.PodCIDRs[idx] != testInput.PodCIDRs[idx] { 703 t.Errorf("%v:Convert v1.NodeSpec to core.NodeSpec failed core.PodCIDRs[%v]=%v expected %v", i, idx, coreNodeSpec.PodCIDRs[idx], testInput.PodCIDRs[idx]) 704 } 705 } 706 } 707 } 708 709 func TestConvert_v1_Pod_To_core_Pod(t *testing.T) { 710 type args struct { 711 in *v1.Pod 712 out *core.Pod 713 } 714 tests := []struct { 715 name string 716 args args 717 wantErr bool 718 wantOut *core.Pod 719 }{ 720 { 721 args: args{ 722 in: &v1.Pod{ 723 Spec: v1.PodSpec{ 724 TerminationGracePeriodSeconds: utilpointer.Int64(-1), 725 }, 726 }, 727 out: &core.Pod{}, 728 }, 729 wantOut: &core.Pod{ 730 Spec: core.PodSpec{ 731 TerminationGracePeriodSeconds: utilpointer.Int64(1), 732 SecurityContext: &core.PodSecurityContext{}, 733 }, 734 }, 735 }, 736 } 737 for _, tt := range tests { 738 t.Run(tt.name, func(t *testing.T) { 739 if err := corev1.Convert_v1_Pod_To_core_Pod(tt.args.in, tt.args.out, nil); (err != nil) != tt.wantErr { 740 t.Errorf("Convert_v1_Pod_To_core_Pod() error = %v, wantErr %v", err, tt.wantErr) 741 } 742 if diff := cmp.Diff(tt.args.out, tt.wantOut); diff != "" { 743 t.Errorf("Convert_v1_Pod_To_core_Pod() mismatch (-want +got):\n%s", diff) 744 } 745 }) 746 } 747 } 748 749 func TestConvert_core_Pod_To_v1_Pod(t *testing.T) { 750 type args struct { 751 in *core.Pod 752 out *v1.Pod 753 } 754 tests := []struct { 755 name string 756 args args 757 wantErr bool 758 wantOut *v1.Pod 759 }{ 760 { 761 args: args{ 762 in: &core.Pod{ 763 Spec: core.PodSpec{ 764 TerminationGracePeriodSeconds: utilpointer.Int64(-1), 765 }, 766 }, 767 out: &v1.Pod{}, 768 }, 769 wantOut: &v1.Pod{ 770 Spec: v1.PodSpec{ 771 TerminationGracePeriodSeconds: utilpointer.Int64(1), 772 }, 773 }, 774 }, 775 } 776 for _, tt := range tests { 777 t.Run(tt.name, func(t *testing.T) { 778 if err := corev1.Convert_core_Pod_To_v1_Pod(tt.args.in, tt.args.out, nil); (err != nil) != tt.wantErr { 779 t.Errorf("Convert_core_Pod_To_v1_Pod() error = %v, wantErr %v", err, tt.wantErr) 780 } 781 if diff := cmp.Diff(tt.args.out, tt.wantOut); diff != "" { 782 t.Errorf("Convert_core_Pod_To_v1_Pod() mismatch (-want +got):\n%s", diff) 783 } 784 }) 785 } 786 }