github.com/openshift/installer@v1.4.17/pkg/asset/manifests/aws/zones_test.go (about) 1 package aws 2 3 import ( 4 "sort" 5 "testing" 6 7 "github.com/google/go-cmp/cmp" 8 "github.com/stretchr/testify/assert" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 "k8s.io/apimachinery/pkg/util/sets" 11 capa "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" 12 13 "github.com/openshift/installer/pkg/asset/installconfig" 14 "github.com/openshift/installer/pkg/asset/installconfig/aws" 15 "github.com/openshift/installer/pkg/asset/manifests/capiutils" 16 "github.com/openshift/installer/pkg/ipnet" 17 "github.com/openshift/installer/pkg/types" 18 awstypes "github.com/openshift/installer/pkg/types/aws" 19 ) 20 21 var stubDefaultCIDR = "10.0.0.0/16" 22 23 func stubClusterID() *installconfig.ClusterID { 24 return &installconfig.ClusterID{ 25 InfraID: "infra-id", 26 } 27 } 28 29 func stubInstallConfig() *installconfig.InstallConfig { 30 return &installconfig.InstallConfig{} 31 } 32 33 func stubInstallConfigType() *types.InstallConfig { 34 return &types.InstallConfig{ 35 TypeMeta: metav1.TypeMeta{ 36 APIVersion: types.InstallConfigVersion, 37 }, 38 ObjectMeta: metav1.ObjectMeta{ 39 Name: "test-cluster", 40 }, 41 Publish: types.ExternalPublishingStrategy, 42 Networking: &types.Networking{ 43 MachineNetwork: []types.MachineNetworkEntry{ 44 { 45 CIDR: *ipnet.MustParseCIDR(stubDefaultCIDR), 46 }, 47 }, 48 }, 49 } 50 } 51 52 func stubInstallConfigPoolCompute() []types.MachinePool { 53 return []types.MachinePool{ 54 { 55 Name: "worker", 56 Platform: types.MachinePoolPlatform{ 57 AWS: &awstypes.MachinePool{ 58 Zones: []string{"b", "c"}, 59 }, 60 }, 61 }, 62 } 63 } 64 65 func stubInstallConfigPoolComputeWithEdge() []types.MachinePool { 66 p := stubInstallConfigPoolCompute() 67 p = append(p, types.MachinePool{ 68 Name: "edge", 69 Platform: types.MachinePoolPlatform{ 70 AWS: &awstypes.MachinePool{ 71 Zones: []string{"edge-b", "edge-c"}, 72 }, 73 }, 74 }) 75 return p 76 } 77 78 func stubInstallConfigPoolControl() *types.MachinePool { 79 return &types.MachinePool{ 80 Name: "master", 81 Platform: types.MachinePoolPlatform{ 82 AWS: &awstypes.MachinePool{ 83 Zones: []string{"a", "b"}, 84 }, 85 }, 86 } 87 } 88 89 func tSortCapaSubnetsByID(in capa.Subnets) capa.Subnets { 90 subnetIDs := []string{} 91 subnetsMap := make(map[string]capa.SubnetSpec, len(in)) 92 for _, subnet := range in { 93 subnetsMap[subnet.ID] = subnet 94 subnetIDs = append(subnetIDs, subnet.ID) 95 } 96 sort.Strings(subnetIDs) 97 out := capa.Subnets{} 98 for _, sid := range subnetIDs { 99 out = append(out, subnetsMap[sid]) 100 } 101 return out 102 } 103 104 func Test_extractZonesFromInstallConfig(t *testing.T) { 105 type args struct { 106 in *zonesInput 107 } 108 tests := []struct { 109 name string 110 args args 111 want *ZonesCAPI 112 wantErrMsg string 113 }{ 114 { 115 name: "no zones in config, use default from region", 116 args: args{ 117 in: &zonesInput{ 118 InstallConfig: func() *installconfig.InstallConfig { 119 ic := stubInstallConfig() 120 ic.Config = stubInstallConfigType() 121 ic.Config.AWS = &awstypes.Platform{ 122 DefaultMachinePlatform: &awstypes.MachinePool{ 123 Zones: []string{}, 124 }, 125 } 126 return ic 127 }(), 128 ZonesInRegion: []string{"x", "y"}, 129 }, 130 }, 131 want: &ZonesCAPI{ 132 ControlPlaneZones: sets.New("x", "y"), 133 ComputeZones: sets.New("x", "y"), 134 EdgeZones: sets.Set[string]{}, 135 }, 136 }, 137 { 138 name: "no zones in config pools, use default from platform config", 139 args: args{ 140 in: &zonesInput{ 141 InstallConfig: func() *installconfig.InstallConfig { 142 ic := stubInstallConfig() 143 ic.Config = stubInstallConfigType() 144 ic.Config.AWS = &awstypes.Platform{ 145 DefaultMachinePlatform: &awstypes.MachinePool{ 146 Zones: []string{"a", "b"}, 147 }, 148 } 149 return ic 150 }(), 151 ZonesInRegion: []string{"x", "y"}, 152 }, 153 }, 154 want: &ZonesCAPI{ 155 ControlPlaneZones: sets.New("a", "b"), 156 ComputeZones: sets.New("a", "b"), 157 EdgeZones: sets.Set[string]{}, 158 }, 159 }, 160 { 161 name: "custom zones control plane pool", 162 args: args{ 163 in: &zonesInput{ 164 InstallConfig: func() *installconfig.InstallConfig { 165 ic := stubInstallConfig() 166 ic.Config = &types.InstallConfig{ 167 ControlPlane: stubInstallConfigPoolControl(), 168 Compute: nil, 169 } 170 return ic 171 }(), 172 ZonesInRegion: []string{"x", "y"}, 173 }, 174 }, 175 want: &ZonesCAPI{ 176 ControlPlaneZones: sets.New("a", "b"), 177 ComputeZones: sets.New("x", "y"), 178 EdgeZones: sets.Set[string]{}, 179 }, 180 }, 181 { 182 name: "custom zones compute pool", 183 args: args{ 184 in: &zonesInput{ 185 InstallConfig: func() *installconfig.InstallConfig { 186 ic := stubInstallConfig() 187 ic.Config = &types.InstallConfig{ 188 ControlPlane: nil, 189 Compute: stubInstallConfigPoolCompute(), 190 } 191 return ic 192 }(), 193 ZonesInRegion: []string{"x", "y"}, 194 }, 195 }, 196 want: &ZonesCAPI{ 197 ControlPlaneZones: sets.New("x", "y"), 198 ComputeZones: sets.New("b", "c"), 199 EdgeZones: sets.Set[string]{}, 200 }, 201 }, 202 { 203 name: "custom zones control plane and compute pools", 204 args: args{ 205 in: &zonesInput{ 206 InstallConfig: func() *installconfig.InstallConfig { 207 ic := stubInstallConfig() 208 ic.Config = &types.InstallConfig{ 209 ControlPlane: stubInstallConfigPoolControl(), 210 Compute: stubInstallConfigPoolCompute(), 211 } 212 return ic 213 }(), 214 ZonesInRegion: []string{"x", "y"}, 215 }, 216 }, 217 want: &ZonesCAPI{ 218 ControlPlaneZones: sets.New("a", "b"), 219 ComputeZones: sets.New("b", "c"), 220 EdgeZones: sets.Set[string]{}, 221 }, 222 }, 223 { 224 name: "custom zones control plane, compute and edge pools", 225 args: args{ 226 in: &zonesInput{ 227 InstallConfig: func() *installconfig.InstallConfig { 228 ic := stubInstallConfig() 229 ic.Config = &types.InstallConfig{ 230 ControlPlane: stubInstallConfigPoolControl(), 231 Compute: stubInstallConfigPoolComputeWithEdge(), 232 } 233 return ic 234 }(), 235 ZonesInRegion: []string{"x", "y"}, 236 }, 237 }, 238 want: &ZonesCAPI{ 239 ControlPlaneZones: sets.New("a", "b"), 240 ComputeZones: sets.New("b", "c"), 241 EdgeZones: sets.New("edge-b", "edge-c"), 242 }, 243 }, 244 // errors 245 { 246 name: "unexpected empty zones on config and metadata", 247 args: args{ 248 in: &zonesInput{ 249 InstallConfig: func() *installconfig.InstallConfig { 250 ic := stubInstallConfig() 251 ic.Config = stubInstallConfigType() 252 ic.Config.AWS = &awstypes.Platform{ 253 DefaultMachinePlatform: &awstypes.MachinePool{ 254 Zones: []string{}, 255 }, 256 } 257 return ic 258 }(), 259 ZonesInRegion: []string{}, 260 }, 261 }, 262 want: &ZonesCAPI{ 263 ControlPlaneZones: sets.Set[string]{}, 264 ComputeZones: sets.Set[string]{}, 265 EdgeZones: sets.Set[string]{}, 266 }, 267 wantErrMsg: `failed to set zones from config, got: []`, 268 }, 269 { 270 name: "unexpected empty zones from edge compute pool", 271 args: args{ 272 in: &zonesInput{ 273 InstallConfig: func() *installconfig.InstallConfig { 274 ic := stubInstallConfig() 275 ic.Config = &types.InstallConfig{ 276 ControlPlane: stubInstallConfigPoolControl(), 277 Compute: func() []types.MachinePool { 278 pools := stubInstallConfigPoolCompute() 279 // create empty zones' edge pool to force failures 280 pools = append(pools, types.MachinePool{ 281 Name: "edge", 282 Platform: types.MachinePoolPlatform{ 283 AWS: &awstypes.MachinePool{ 284 Zones: []string{}, 285 }, 286 }, 287 }) 288 return pools 289 }(), 290 } 291 return ic 292 }(), 293 ZonesInRegion: []string{"x", "y"}, 294 }, 295 }, 296 wantErrMsg: `expect one or more zones in the edge compute pool, got: []`, 297 }, 298 } 299 for _, tt := range tests { 300 t.Run(tt.name, func(t *testing.T) { 301 got, err := extractZonesFromInstallConfig(tt.args.in) 302 if err != nil { 303 if len(tt.wantErrMsg) > 0 { 304 if got := err.Error(); !cmp.Equal(got, tt.wantErrMsg) { 305 t.Errorf("extractZonesFromInstallConfig() unexpected error message: %v", cmp.Diff(got, tt.wantErrMsg)) 306 } 307 return 308 } 309 t.Errorf("extractZonesFromInstallConfig() unexpected error: %v", err) 310 return 311 } 312 if !cmp.Equal(got, tt.want) { 313 t.Errorf("extractZonesFromInstallConfig() Got unexpected results:\n%v", cmp.Diff(got, tt.want)) 314 } 315 }) 316 } 317 } 318 319 func Test_setSubnetsManagedVPC(t *testing.T) { 320 type args struct { 321 in *zonesInput 322 } 323 tests := []struct { 324 name string 325 args args 326 wantErr bool 327 want capa.NetworkSpec 328 }{ 329 { 330 name: "default availability zones in the region", 331 args: args{ 332 in: &zonesInput{ 333 ClusterID: stubClusterID(), 334 InstallConfig: func() *installconfig.InstallConfig { 335 ic := stubInstallConfig() 336 ic.Config = &types.InstallConfig{ 337 Publish: types.ExternalPublishingStrategy, 338 Networking: &types.Networking{ 339 MachineNetwork: []types.MachineNetworkEntry{ 340 { 341 CIDR: *ipnet.MustParseCIDR(stubDefaultCIDR), 342 }, 343 }, 344 }, 345 } 346 return ic 347 }(), 348 Cluster: &capa.AWSCluster{ 349 ObjectMeta: metav1.ObjectMeta{ 350 Name: "infraId", 351 Namespace: capiutils.Namespace, 352 }, 353 Spec: capa.AWSClusterSpec{}, 354 }, 355 ZonesInRegion: []string{"a", "b", "c"}, 356 }, 357 }, 358 want: capa.NetworkSpec{ 359 VPC: capa.VPCSpec{CidrBlock: stubDefaultCIDR}, 360 Subnets: []capa.SubnetSpec{ 361 { 362 ID: "infra-id-subnet-private-a", 363 AvailabilityZone: "a", 364 IsPublic: false, 365 CidrBlock: "10.0.0.0/19", 366 }, { 367 ID: "infra-id-subnet-private-b", 368 AvailabilityZone: "b", 369 IsPublic: false, 370 CidrBlock: "10.0.32.0/19", 371 }, { 372 ID: "infra-id-subnet-private-c", 373 AvailabilityZone: "c", 374 IsPublic: false, 375 CidrBlock: "10.0.64.0/19", 376 }, { 377 ID: "infra-id-subnet-public-a", 378 AvailabilityZone: "a", 379 IsPublic: true, 380 CidrBlock: "10.0.96.0/21", 381 }, { 382 ID: "infra-id-subnet-public-b", 383 AvailabilityZone: "b", 384 IsPublic: true, 385 CidrBlock: "10.0.104.0/21", 386 }, { 387 ID: "infra-id-subnet-public-c", 388 AvailabilityZone: "c", 389 IsPublic: true, 390 CidrBlock: "10.0.112.0/21", 391 }, 392 }, 393 }, 394 }, 395 { 396 name: "default availability zones in the region and edge", 397 args: args{ 398 in: &zonesInput{ 399 ClusterID: stubClusterID(), 400 InstallConfig: func() *installconfig.InstallConfig { 401 ic := stubInstallConfig() 402 ic.Config = &types.InstallConfig{ 403 Publish: types.ExternalPublishingStrategy, 404 Networking: &types.Networking{ 405 MachineNetwork: []types.MachineNetworkEntry{ 406 { 407 CIDR: *ipnet.MustParseCIDR(stubDefaultCIDR), 408 }, 409 }, 410 }, 411 Compute: []types.MachinePool{ 412 { 413 Name: "edge", 414 Platform: types.MachinePoolPlatform{ 415 AWS: &awstypes.MachinePool{ 416 Zones: []string{"edge-a"}, 417 }, 418 }, 419 }, 420 }, 421 } 422 return ic 423 }(), 424 Cluster: &capa.AWSCluster{ 425 ObjectMeta: metav1.ObjectMeta{ 426 Name: "infraId", 427 Namespace: capiutils.Namespace, 428 }, 429 Spec: capa.AWSClusterSpec{}, 430 }, 431 ZonesInRegion: []string{"a", "b", "c"}, 432 }, 433 }, 434 want: capa.NetworkSpec{ 435 VPC: capa.VPCSpec{CidrBlock: stubDefaultCIDR}, 436 Subnets: []capa.SubnetSpec{ 437 { 438 ID: "infra-id-subnet-private-a", 439 AvailabilityZone: "a", 440 IsPublic: false, 441 CidrBlock: "10.0.0.0/19", 442 }, { 443 ID: "infra-id-subnet-private-b", 444 AvailabilityZone: "b", 445 IsPublic: false, 446 CidrBlock: "10.0.32.0/19", 447 }, { 448 ID: "infra-id-subnet-private-c", 449 AvailabilityZone: "c", 450 IsPublic: false, 451 CidrBlock: "10.0.64.0/19", 452 }, { 453 ID: "infra-id-subnet-private-edge-a", 454 AvailabilityZone: "edge-a", 455 IsPublic: false, 456 CidrBlock: "10.0.128.0/21", 457 }, { 458 ID: "infra-id-subnet-public-a", 459 AvailabilityZone: "a", 460 IsPublic: true, 461 CidrBlock: "10.0.96.0/21", 462 }, { 463 ID: "infra-id-subnet-public-b", 464 AvailabilityZone: "b", 465 IsPublic: true, 466 CidrBlock: "10.0.104.0/21", 467 }, { 468 ID: "infra-id-subnet-public-c", 469 AvailabilityZone: "c", 470 IsPublic: true, 471 CidrBlock: "10.0.112.0/21", 472 }, { 473 ID: "infra-id-subnet-public-edge-a", 474 AvailabilityZone: "edge-a", 475 IsPublic: true, 476 CidrBlock: "10.0.136.0/21", 477 }, 478 }, 479 }, 480 }, 481 // TODO: error scenarios to review the coverage 482 // { 483 // name: "err: failed to get availability zones: expect one or more zones in the edge compute pool", 484 // }, 485 // { 486 // name: "err: failed to get availability zones: failed to set zones from config", 487 // }, 488 // { 489 // name: "err: unable to generate CIDR blocks for all private subnets", 490 // }, 491 // { 492 // name: "err: unable to generate CIDR blocks for all public subnets", 493 // }, 494 // { 495 // name: "err: unable to define CIDR blocks to all zones for private subnets", 496 // }, 497 // { 498 // name: "err: unable to define CIDR blocks to all zones for public subnets", 499 // }, 500 // { 501 // name: "err: unable to generate CIDR blocks for all edge subnets", 502 // }, 503 // { 504 // name: "err: unable to define CIDR blocks to all edge zones for private subnets", 505 // }, 506 // { 507 // name: "err: unable to define CIDR blocks to all edge zones for public subnets", 508 // }, 509 } 510 for _, tt := range tests { 511 t.Run(tt.name, func(t *testing.T) { 512 err := setSubnetsManagedVPC(tt.args.in) 513 if (err != nil) != tt.wantErr { 514 t.Errorf("setSubnetsManagedVPC() #1 error: %+v,\nwantErr %+v\n", err, tt.wantErr) 515 } 516 if tt.args.in == nil && (err == nil) { 517 return 518 } 519 if tt.args.in.Cluster == nil && (err == nil) { 520 return 521 } 522 523 if len(tt.args.in.Cluster.Spec.NetworkSpec.Subnets) == 0 { 524 if !tt.wantErr { 525 t.Errorf("setSubnetsManagedVPC() #2 error: %v, wantErr: %v", err, tt.wantErr) 526 } 527 return 528 } 529 tt.args.in.Cluster.Spec.NetworkSpec.Subnets = tSortCapaSubnetsByID(tt.args.in.Cluster.Spec.NetworkSpec.Subnets) 530 if got := tt.args.in.Cluster.Spec.NetworkSpec; !cmp.Equal(got, tt.want) { 531 t.Errorf("setSubnetsManagedVPC() NetworkSpec.Subnets diff: %v", cmp.Diff(got, tt.want)) 532 } 533 }) 534 } 535 } 536 537 func Test_setSubnetsBYOVPC(t *testing.T) { 538 type args struct { 539 in *zonesInput 540 } 541 tests := []struct { 542 name string 543 args args 544 want capa.NetworkSpec 545 wantErr bool 546 }{ 547 { 548 name: "default byo vpc", 549 args: args{ 550 in: &zonesInput{ 551 Cluster: &capa.AWSCluster{}, 552 Subnets: &subnetsInput{ 553 vpc: "vpc-id", 554 privateSubnets: aws.Subnets{ 555 "subnetId-a-private": aws.Subnet{ 556 ID: "subnetId-a-private", 557 CIDR: "10.0.1.0/24", 558 Zone: &aws.Zone{ 559 Name: "a", 560 }, 561 Public: false, 562 }, 563 "subnetId-b-private": aws.Subnet{ 564 ID: "subnetId-b-private", 565 CIDR: "10.0.2.0/24", 566 Zone: &aws.Zone{ 567 Name: "b", 568 }, 569 Public: false, 570 }, 571 "subnetId-c-private": aws.Subnet{ 572 ID: "subnetId-c-private", 573 CIDR: "10.0.3.0/24", 574 Zone: &aws.Zone{ 575 Name: "c", 576 }, 577 Public: false, 578 }, 579 }, 580 publicSubnets: aws.Subnets{ 581 "subnetId-a-public": aws.Subnet{ 582 ID: "subnetId-a-public", 583 CIDR: "10.0.4.0/24", 584 Zone: &aws.Zone{ 585 Name: "a", 586 }, 587 Public: true, 588 }, 589 "subnetId-b-public": aws.Subnet{ 590 ID: "subnetId-b-public", 591 CIDR: "10.0.5.0/24", 592 Zone: &aws.Zone{ 593 Name: "b", 594 }, 595 Public: true, 596 }, 597 "subnetId-c-public": aws.Subnet{ 598 ID: "subnetId-c-public", 599 CIDR: "10.0.6.0/24", 600 Zone: &aws.Zone{ 601 Name: "c", 602 }, 603 Public: true, 604 }, 605 }, 606 }, 607 }, 608 }, 609 want: capa.NetworkSpec{ 610 VPC: capa.VPCSpec{ID: "vpc-id"}, 611 Subnets: []capa.SubnetSpec{ 612 { 613 ID: "subnetId-a-private", 614 AvailabilityZone: "a", 615 IsPublic: false, 616 CidrBlock: "10.0.1.0/24", 617 }, { 618 ID: "subnetId-a-public", 619 AvailabilityZone: "a", 620 IsPublic: true, 621 CidrBlock: "10.0.4.0/24", 622 }, { 623 ID: "subnetId-b-private", 624 AvailabilityZone: "b", 625 IsPublic: false, 626 CidrBlock: "10.0.2.0/24", 627 }, { 628 ID: "subnetId-b-public", 629 AvailabilityZone: "b", 630 IsPublic: true, 631 CidrBlock: "10.0.5.0/24", 632 }, { 633 ID: "subnetId-c-private", 634 AvailabilityZone: "c", 635 IsPublic: false, 636 CidrBlock: "10.0.3.0/24", 637 }, { 638 ID: "subnetId-c-public", 639 AvailabilityZone: "c", 640 IsPublic: true, 641 CidrBlock: "10.0.6.0/24", 642 }, 643 }, 644 }, 645 }, 646 { 647 name: "byo vpc only private subnets", 648 args: args{ 649 in: &zonesInput{ 650 Cluster: &capa.AWSCluster{}, 651 Subnets: &subnetsInput{ 652 vpc: "vpc-id", 653 privateSubnets: aws.Subnets{ 654 "subnetId-a-private": aws.Subnet{ 655 ID: "subnetId-a-private", 656 CIDR: "10.0.1.0/24", 657 Zone: &aws.Zone{ 658 Name: "a", 659 }, 660 Public: false, 661 }, 662 "subnetId-b-private": aws.Subnet{ 663 ID: "subnetId-b-private", 664 CIDR: "10.0.2.0/24", 665 Zone: &aws.Zone{ 666 Name: "b", 667 }, 668 Public: false, 669 }, 670 "subnetId-c-private": aws.Subnet{ 671 ID: "subnetId-c-private", 672 CIDR: "10.0.3.0/24", 673 Zone: &aws.Zone{ 674 Name: "c", 675 }, 676 Public: false, 677 }, 678 }, 679 }, 680 }, 681 }, 682 want: capa.NetworkSpec{ 683 VPC: capa.VPCSpec{ 684 ID: "vpc-id", 685 }, 686 Subnets: capa.Subnets{ 687 { 688 ID: "subnetId-a-private", 689 AvailabilityZone: "a", 690 IsPublic: false, 691 CidrBlock: "10.0.1.0/24", 692 }, 693 { 694 ID: "subnetId-b-private", 695 AvailabilityZone: "b", 696 IsPublic: false, 697 CidrBlock: "10.0.2.0/24", 698 }, 699 { 700 ID: "subnetId-c-private", 701 AvailabilityZone: "c", 702 IsPublic: false, 703 CidrBlock: "10.0.3.0/24", 704 }, 705 }, 706 }, 707 }, 708 { 709 name: "byo vpc with edge", 710 args: args{ 711 in: &zonesInput{ 712 Cluster: &capa.AWSCluster{}, 713 Subnets: &subnetsInput{ 714 vpc: "vpc-id", 715 privateSubnets: aws.Subnets{ 716 "subnetId-a-private": aws.Subnet{ 717 ID: "subnetId-a-private", 718 CIDR: "10.0.1.0/24", 719 Zone: &aws.Zone{ 720 Name: "a", 721 }, 722 Public: false, 723 }, 724 "subnetId-b-private": aws.Subnet{ 725 ID: "subnetId-b-private", 726 CIDR: "10.0.2.0/24", 727 Zone: &aws.Zone{ 728 Name: "b", 729 }, 730 Public: false, 731 }, 732 "subnetId-c-private": aws.Subnet{ 733 ID: "subnetId-c-private", 734 CIDR: "10.0.3.0/24", 735 Zone: &aws.Zone{ 736 Name: "c", 737 }, 738 Public: false, 739 }, 740 }, 741 publicSubnets: aws.Subnets{ 742 "subnetId-a-public": aws.Subnet{ 743 ID: "subnetId-a-public", 744 CIDR: "10.0.4.0/24", 745 Zone: &aws.Zone{ 746 Name: "a", 747 }, 748 Public: true, 749 }, 750 "subnetId-b-public": aws.Subnet{ 751 ID: "subnetId-b-public", 752 CIDR: "10.0.5.0/24", 753 Zone: &aws.Zone{ 754 Name: "b", 755 }, 756 Public: true, 757 }, 758 "subnetId-c-public": aws.Subnet{ 759 ID: "subnetId-c-public", 760 CIDR: "10.0.6.0/24", 761 Zone: &aws.Zone{ 762 Name: "c", 763 }, 764 Public: true, 765 }, 766 }, 767 edgeSubnets: aws.Subnets{ 768 "subnetId-edge-a-private": aws.Subnet{ 769 ID: "subnetId-edge-a-private", 770 CIDR: "10.0.7.0/24", 771 Zone: &aws.Zone{ 772 Name: "edge-a", 773 }, 774 Public: false, 775 }, 776 "subnetId-edge-a-public": aws.Subnet{ 777 ID: "subnetId-edge-a-public", 778 CIDR: "10.0.8.0/24", 779 Zone: &aws.Zone{ 780 Name: "edge-a", 781 }, 782 Public: true, 783 }, 784 }, 785 }, 786 }, 787 }, 788 want: capa.NetworkSpec{ 789 VPC: capa.VPCSpec{ID: "vpc-id"}, 790 Subnets: []capa.SubnetSpec{ 791 { 792 ID: "subnetId-a-private", 793 AvailabilityZone: "a", 794 IsPublic: false, 795 CidrBlock: "10.0.1.0/24", 796 }, { 797 ID: "subnetId-a-public", 798 AvailabilityZone: "a", 799 IsPublic: true, 800 CidrBlock: "10.0.4.0/24", 801 }, { 802 ID: "subnetId-b-private", 803 AvailabilityZone: "b", 804 IsPublic: false, 805 CidrBlock: "10.0.2.0/24", 806 }, { 807 ID: "subnetId-b-public", 808 AvailabilityZone: "b", 809 IsPublic: true, 810 CidrBlock: "10.0.5.0/24", 811 }, { 812 ID: "subnetId-c-private", 813 AvailabilityZone: "c", 814 IsPublic: false, 815 CidrBlock: "10.0.3.0/24", 816 }, { 817 ID: "subnetId-c-public", 818 AvailabilityZone: "c", 819 IsPublic: true, 820 CidrBlock: "10.0.6.0/24", 821 }, { 822 ID: "subnetId-edge-a-private", 823 AvailabilityZone: "edge-a", 824 IsPublic: false, 825 CidrBlock: "10.0.7.0/24", 826 }, { 827 ID: "subnetId-edge-a-public", 828 AvailabilityZone: "edge-a", 829 IsPublic: true, 830 CidrBlock: "10.0.8.0/24", 831 }, 832 }, 833 }, 834 }, 835 } 836 for _, tt := range tests { 837 t.Run(tt.name, func(t *testing.T) { 838 err := setSubnetsBYOVPC(tt.args.in) 839 if (err != nil) != tt.wantErr { 840 t.Errorf("setSubnetsBYOVPC() #1 error: %v, wantErr: %v", err, tt.wantErr) 841 return 842 } 843 if len(tt.args.in.Cluster.Spec.NetworkSpec.Subnets) == 0 { 844 if !tt.wantErr { 845 t.Errorf("setSubnetsBYOVPC() #2 error: %v, wantErr: %v", err, tt.wantErr) 846 } 847 return 848 } 849 tt.args.in.Cluster.Spec.NetworkSpec.Subnets = tSortCapaSubnetsByID(tt.args.in.Cluster.Spec.NetworkSpec.Subnets) 850 if got := tt.args.in.Cluster.Spec.NetworkSpec; !cmp.Equal(got, tt.want) { 851 t.Errorf("setSubnetsBYOVPC() NetworkSpec.Subnets diff: %v", cmp.Diff(got, tt.want)) 852 } 853 }) 854 } 855 } 856 857 func Test_ZonesCAPI_SetAvailabilityZones(t *testing.T) { 858 type fields struct { 859 ControlPlaneZones sets.Set[string] 860 ComputeZones sets.Set[string] 861 } 862 type args struct { 863 pool string 864 zones []string 865 } 866 tests := []struct { 867 name string 868 fields fields 869 args args 870 want *ZonesCAPI 871 }{ 872 { 873 name: "empty", 874 fields: fields{ 875 ControlPlaneZones: sets.Set[string]{}, 876 ComputeZones: sets.Set[string]{}, 877 }, 878 args: args{ 879 pool: types.MachinePoolControlPlaneRoleName, 880 zones: []string{}, 881 }, 882 want: &ZonesCAPI{ 883 ControlPlaneZones: sets.Set[string]{}, 884 ComputeZones: sets.Set[string]{}, 885 }, 886 }, 887 { 888 name: "set zones for control plane pool", 889 fields: fields{ 890 ControlPlaneZones: sets.Set[string]{}, 891 ComputeZones: sets.Set[string]{}, 892 }, 893 args: args{ 894 pool: types.MachinePoolControlPlaneRoleName, 895 zones: []string{"a", "b"}, 896 }, 897 want: &ZonesCAPI{ 898 ControlPlaneZones: sets.New("a", "b"), 899 ComputeZones: sets.Set[string]{}, 900 }, 901 }, 902 { 903 name: "set zones for compute pool", 904 fields: fields{ 905 ControlPlaneZones: sets.Set[string]{}, 906 ComputeZones: sets.Set[string]{}, 907 }, 908 args: args{ 909 pool: types.MachinePoolComputeRoleName, 910 zones: []string{"b", "c"}, 911 }, 912 want: &ZonesCAPI{ 913 ControlPlaneZones: sets.Set[string]{}, 914 ComputeZones: sets.New("b", "c"), 915 }, 916 }, 917 } 918 for _, tt := range tests { 919 t.Run(tt.name, func(t *testing.T) { 920 zo := &ZonesCAPI{ 921 ControlPlaneZones: tt.fields.ControlPlaneZones, 922 ComputeZones: tt.fields.ComputeZones, 923 } 924 zo.SetAvailabilityZones(tt.args.pool, tt.args.zones) 925 if tt.want != nil { 926 assert.EqualValuesf(t, tt.want, zo, "%v failed", tt.name) 927 } 928 }) 929 } 930 } 931 932 func Test_ZonesCAPI_SetDefaultConfigZones(t *testing.T) { 933 type fields struct { 934 AvailabilityZones sets.Set[string] 935 ControlPlaneZones sets.Set[string] 936 ComputeZones sets.Set[string] 937 } 938 type args struct { 939 pool string 940 defConfig []string 941 defRegion []string 942 } 943 tests := []struct { 944 name string 945 fields fields 946 args args 947 want *ZonesCAPI 948 }{ 949 { 950 name: "empty", 951 fields: fields{ 952 ControlPlaneZones: sets.Set[string]{}, 953 ComputeZones: sets.Set[string]{}, 954 }, 955 args: args{ 956 pool: types.MachinePoolControlPlaneRoleName, 957 defConfig: []string{}, 958 defRegion: []string{}, 959 }, 960 want: &ZonesCAPI{ 961 ControlPlaneZones: sets.Set[string]{}, 962 ComputeZones: sets.Set[string]{}, 963 }, 964 }, 965 { 966 name: "platform defaults when control plane pool exists", 967 fields: fields{ 968 ControlPlaneZones: sets.New("a"), 969 ComputeZones: sets.Set[string]{}, 970 }, 971 args: args{ 972 pool: types.MachinePoolControlPlaneRoleName, 973 defConfig: []string{"d"}, 974 defRegion: []string{"f"}, 975 }, 976 want: &ZonesCAPI{ 977 ControlPlaneZones: sets.New("a"), 978 ComputeZones: sets.Set[string]{}, 979 }, 980 }, 981 { 982 name: "platform defaults when control plane pool not exists", 983 fields: fields{ 984 ControlPlaneZones: sets.Set[string]{}, 985 ComputeZones: sets.Set[string]{}, 986 }, 987 args: args{ 988 pool: types.MachinePoolControlPlaneRoleName, 989 defConfig: []string{"d"}, 990 defRegion: []string{"f"}, 991 }, 992 want: &ZonesCAPI{ 993 ControlPlaneZones: sets.New("d"), 994 ComputeZones: sets.Set[string]{}, 995 }, 996 }, 997 { 998 name: "region defaults when control plane pool exists", 999 fields: fields{ 1000 ControlPlaneZones: sets.New("a"), 1001 ComputeZones: sets.Set[string]{}, 1002 }, 1003 args: args{ 1004 pool: types.MachinePoolControlPlaneRoleName, 1005 defConfig: []string{}, 1006 defRegion: []string{"f"}, 1007 }, 1008 want: &ZonesCAPI{ 1009 ControlPlaneZones: sets.New("a"), 1010 ComputeZones: sets.Set[string]{}, 1011 }, 1012 }, 1013 { 1014 name: "region defaults when control plane pool not exists", 1015 fields: fields{ 1016 ControlPlaneZones: sets.Set[string]{}, 1017 ComputeZones: sets.Set[string]{}, 1018 }, 1019 args: args{ 1020 pool: types.MachinePoolControlPlaneRoleName, 1021 defConfig: []string{}, 1022 defRegion: []string{"f"}, 1023 }, 1024 want: &ZonesCAPI{ 1025 ControlPlaneZones: sets.New("f"), 1026 ComputeZones: sets.Set[string]{}, 1027 }, 1028 }, 1029 { 1030 name: "platform defaults when compute pool exists", 1031 fields: fields{ 1032 ControlPlaneZones: sets.Set[string]{}, 1033 ComputeZones: sets.New("b"), 1034 }, 1035 args: args{ 1036 pool: types.MachinePoolComputeRoleName, 1037 defConfig: []string{"d"}, 1038 defRegion: []string{"f"}, 1039 }, 1040 want: &ZonesCAPI{ 1041 ControlPlaneZones: sets.Set[string]{}, 1042 ComputeZones: sets.New("b"), 1043 }, 1044 }, 1045 { 1046 name: "platform defaults when compute pool not exists", 1047 fields: fields{ 1048 AvailabilityZones: sets.Set[string]{}, 1049 ControlPlaneZones: sets.Set[string]{}, 1050 ComputeZones: sets.Set[string]{}, 1051 }, 1052 args: args{ 1053 pool: types.MachinePoolComputeRoleName, 1054 defConfig: []string{"d"}, 1055 defRegion: []string{"f"}, 1056 }, 1057 want: &ZonesCAPI{ 1058 ControlPlaneZones: sets.Set[string]{}, 1059 ComputeZones: sets.New("d"), 1060 }, 1061 }, 1062 { 1063 name: "region defaults when compute pool exists", 1064 fields: fields{ 1065 ControlPlaneZones: sets.Set[string]{}, 1066 ComputeZones: sets.New("b"), 1067 }, 1068 args: args{ 1069 pool: types.MachinePoolComputeRoleName, 1070 defConfig: []string{}, 1071 defRegion: []string{"f"}, 1072 }, 1073 want: &ZonesCAPI{ 1074 ControlPlaneZones: sets.Set[string]{}, 1075 ComputeZones: sets.New("b"), 1076 }, 1077 }, 1078 { 1079 name: "region defaults when compute pool not exists", 1080 fields: fields{ 1081 ControlPlaneZones: sets.Set[string]{}, 1082 ComputeZones: sets.Set[string]{}, 1083 }, 1084 args: args{ 1085 pool: types.MachinePoolComputeRoleName, 1086 defConfig: []string{}, 1087 defRegion: []string{"f"}, 1088 }, 1089 want: &ZonesCAPI{ 1090 ControlPlaneZones: sets.Set[string]{}, 1091 ComputeZones: sets.New("f"), 1092 }, 1093 }, 1094 } 1095 for _, tt := range tests { 1096 t.Run(tt.name, func(t *testing.T) { 1097 zo := &ZonesCAPI{ 1098 ControlPlaneZones: tt.fields.ControlPlaneZones, 1099 ComputeZones: tt.fields.ComputeZones, 1100 } 1101 zo.SetDefaultConfigZones(tt.args.pool, tt.args.defConfig, tt.args.defRegion) 1102 if tt.want != nil { 1103 assert.EqualValuesf(t, tt.want, zo, "%v failed", tt.name) 1104 } 1105 }) 1106 } 1107 } 1108 1109 func Test_ZonesCAPI_GetAvailabilityZones(t *testing.T) { 1110 tests := []struct { 1111 name string 1112 zones *ZonesCAPI 1113 want []string 1114 }{ 1115 { 1116 name: "empty", 1117 zones: &ZonesCAPI{}, 1118 want: []string{}, 1119 }, 1120 { 1121 name: "empty az", 1122 zones: &ZonesCAPI{ 1123 EdgeZones: sets.New("edge-x", "edge-y"), 1124 }, 1125 want: []string{}, 1126 }, 1127 { 1128 name: "sorted", 1129 zones: &ZonesCAPI{ 1130 ControlPlaneZones: sets.New("a", "b"), 1131 ComputeZones: sets.New("b", "c"), 1132 EdgeZones: sets.New("edge-x", "edge-y"), 1133 }, 1134 want: []string{"a", "b", "c"}, 1135 }, 1136 { 1137 name: "unsorted", 1138 zones: &ZonesCAPI{ 1139 ControlPlaneZones: sets.New("x", "a"), 1140 ComputeZones: sets.New("b", "a"), 1141 EdgeZones: sets.New("edge-x", "edge-y"), 1142 }, 1143 want: []string{"a", "b", "x"}, 1144 }, 1145 { 1146 name: "control planes only", 1147 zones: &ZonesCAPI{ 1148 ControlPlaneZones: sets.New("x", "a"), 1149 ComputeZones: sets.Set[string]{}, 1150 EdgeZones: sets.New("edge-x", "edge-y"), 1151 }, 1152 want: []string{"a", "x"}, 1153 }, 1154 { 1155 name: "compute only", 1156 zones: &ZonesCAPI{ 1157 ControlPlaneZones: sets.Set[string]{}, 1158 ComputeZones: sets.New("x", "a"), 1159 EdgeZones: sets.New("edge-x", "edge-y"), 1160 }, 1161 want: []string{"a", "x"}, 1162 }, 1163 } 1164 for _, tt := range tests { 1165 t.Run(tt.name, func(t *testing.T) { 1166 zo := tt.zones 1167 if got := zo.GetAvailabilityZones(); !cmp.Equal(got, tt.want) { 1168 t.Errorf("ZonesCAPI.GetAvailabilityZones() unexpected results:\n%v", cmp.Diff(got, tt.want)) 1169 } 1170 }) 1171 } 1172 } 1173 1174 func Test_ZonesCAPI_EdgeZones(t *testing.T) { 1175 tests := []struct { 1176 name string 1177 zones *ZonesCAPI 1178 want []string 1179 }{ 1180 { 1181 name: "empty", 1182 zones: &ZonesCAPI{}, 1183 want: []string{}, 1184 }, 1185 { 1186 name: "empty edge", 1187 zones: &ZonesCAPI{ 1188 ControlPlaneZones: sets.New("x", "y"), 1189 }, 1190 want: []string{}, 1191 }, 1192 { 1193 name: "empty only", 1194 zones: &ZonesCAPI{ 1195 EdgeZones: sets.New("edge-x"), 1196 }, 1197 want: []string{"edge-x"}, 1198 }, 1199 { 1200 name: "sorted", 1201 zones: &ZonesCAPI{ 1202 ControlPlaneZones: sets.New("a", "b"), 1203 ComputeZones: sets.New("b", "c"), 1204 EdgeZones: sets.New("edge-x", "edge-y"), 1205 }, 1206 want: []string{"edge-x", "edge-y"}, 1207 }, 1208 { 1209 name: "unsorted", 1210 zones: &ZonesCAPI{ 1211 ControlPlaneZones: sets.New("x", "a"), 1212 ComputeZones: sets.New("b", "a"), 1213 EdgeZones: sets.New("edge-y", "edge-a"), 1214 }, 1215 want: []string{"edge-a", "edge-y"}, 1216 }, 1217 { 1218 name: "control planes only", 1219 zones: &ZonesCAPI{ 1220 ControlPlaneZones: sets.New("x", "a"), 1221 ComputeZones: sets.Set[string]{}, 1222 EdgeZones: sets.New("edge-a", "edge-y"), 1223 }, 1224 want: []string{"edge-a", "edge-y"}, 1225 }, 1226 { 1227 name: "compute only", 1228 zones: &ZonesCAPI{ 1229 ControlPlaneZones: sets.Set[string]{}, 1230 ComputeZones: sets.New("x", "a"), 1231 EdgeZones: sets.New("edge-a", "edge-y"), 1232 }, 1233 want: []string{"edge-a", "edge-y"}, 1234 }, 1235 } 1236 for _, tt := range tests { 1237 t.Run(tt.name, func(t *testing.T) { 1238 zo := tt.zones 1239 if got := zo.GetEdgeZones(); !cmp.Equal(got, tt.want) { 1240 t.Errorf("ZonesCAPI.EdgeZones() unexpected results:\n %v", cmp.Diff(got, tt.want)) 1241 } 1242 }) 1243 } 1244 }