55 "fmt"
66 "net/http"
77 "os"
8+ "slices"
89 "sort"
910 "strings"
1011 "testing"
@@ -390,6 +391,72 @@ func TestValidate(t *testing.T) {
390391 instanceTypes : validInstanceTypes (),
391392 expectErr : `controlPlane\.platform\.aws\.type: Invalid value: "m1\.xlarge": instance type m1\.xlarge does not support IPv6 networking.*compute\[0\]\.platform\.aws\.type: Invalid value: "m1\.xlarge": instance type m1\.xlarge does not support IPv6 networking` ,
392393 },
394+ {
395+ name : "invalid dual-stack control plane instance type is not Nitro-based" ,
396+ installConfig : icBuild .build (icBuild .withInstanceType ("m5.xlarge" , "t2.small" , "m5.large" ), icBuild .withIPFamily (network .DualStackIPv4Primary )),
397+ availRegions : validAvailRegions (),
398+ availZones : validAvailZones (),
399+ instanceTypes : validInstanceTypes (),
400+ expectErr : `controlPlane\.platform\.aws\.type: Invalid value: "t2\.small": instance type t2\.small is not Nitro-based` ,
401+ },
402+ {
403+ name : "invalid dual-stack compute instance type is not Nitro-based" ,
404+ installConfig : icBuild .build (icBuild .withInstanceType ("m5.xlarge" , "m5.xlarge" , "t2.small" ), icBuild .withIPFamily (network .DualStackIPv4Primary )),
405+ availRegions : validAvailRegions (),
406+ availZones : validAvailZones (),
407+ instanceTypes : validInstanceTypes (),
408+ expectErr : `compute\[0\]\.platform\.aws\.type: Invalid value: "t2\.small": instance type t2\.small is not Nitro-based` ,
409+ },
410+ {
411+ name : "invalid dual-stack default machine platform instance type is not Nitro-based" ,
412+ installConfig : icBuild .build (icBuild .withInstanceType ("t2.small" , "" , "" ), icBuild .withIPFamily (network .DualStackIPv6Primary )),
413+ availRegions : validAvailRegions (),
414+ availZones : validAvailZones (),
415+ instanceTypes : validInstanceTypes (),
416+ expectErr : `controlPlane\.platform\.aws\.type: Invalid value: "t2\.small": instance type t2\.small is not Nitro-based.*compute\[0\]\.platform\.aws\.type: Invalid value: "t2\.small": instance type t2\.small is not Nitro-based` ,
417+ },
418+ {
419+ name : "valid dual-stack byo subnets with IPv6 CIDRs" ,
420+ installConfig : icBuild .build (
421+ icBuild .withBaseBYO (),
422+ icBuild .withIPFamily (network .DualStackIPv4Primary ),
423+ icBuild .withDualStackMachineNetworks (network .DualStackIPv4Primary ),
424+ ),
425+ availRegions : validAvailRegions (),
426+ availZones : validAvailZones (),
427+ instanceTypes : validInstanceTypes (),
428+ subnets : SubnetGroups {
429+ Private : validDualstackSubnets ("private" ),
430+ Public : validDualstackSubnets ("public" ),
431+ VpcID : validVPCID ,
432+ },
433+ },
434+ {
435+ name : "invalid dual-stack byo subnets, some subnets missing IPv6 CIDR" ,
436+ installConfig : icBuild .build (
437+ icBuild .withBaseBYO (),
438+ icBuild .withVPCSubnetIDs ([]string {"invalid-private-cidr-subnet" , "invalid-public-cidr-subnet" }, false ),
439+ icBuild .withIPFamily (network .DualStackIPv4Primary ),
440+ icBuild .withDualStackMachineNetworks (network .DualStackIPv4Primary ),
441+ ),
442+ availRegions : validAvailRegions (),
443+ availZones : append (validAvailZones (), "zone-for-invalid-cidr-subnet" ),
444+ instanceTypes : validInstanceTypes (),
445+ subnets : SubnetGroups {
446+ Private : mergeSubnets (validDualstackSubnets ("private" ), Subnets {"invalid-private-cidr-subnet" : Subnet {
447+ ID : "invalid-private-cidr-subnet" ,
448+ Zone : & Zone {Name : "zone-for-invalid-cidr-subnet" },
449+ CIDR : "10.0.7.0/24" ,
450+ }}),
451+ Public : mergeSubnets (validDualstackSubnets ("public" ), Subnets {"invalid-public-cidr-subnet" : Subnet {
452+ ID : "invalid-public-cidr-subnet" ,
453+ Zone : & Zone {Name : "zone-for-invalid-cidr-subnet" },
454+ CIDR : "10.0.8.0/24" ,
455+ }}),
456+ VpcID : validVPCID ,
457+ },
458+ expectErr : `^\Q[platform.aws.vpc.subnets[6]: Required value: subnet does not have an associated IPv6 CIDR block, platform.aws.vpc.subnets[7]: Required value: subnet does not have an associated IPv6 CIDR block]\E$` ,
459+ },
393460 {
394461 name : "invalid edge pool, missing zones" ,
395462 installConfig : icBuild .build (
@@ -431,6 +498,63 @@ func TestValidate(t *testing.T) {
431498 availRegions : validAvailRegions (),
432499 expectErr : `^\[compute\[0\]\.platform\.aws: Required value: edge compute pools are only supported on the AWS platform, compute\[0\].platform.aws: Required value: zone is required when using edge machine pools\]$` ,
433500 },
501+ {
502+ name : "valid edge pool with dual-stack and local zone byo subnets" ,
503+ installConfig : icBuild .build (
504+ icBuild .withBaseBYO (),
505+ icBuild .withIPFamily (network .DualStackIPv4Primary ),
506+ icBuild .withDualStackMachineNetworks (network .DualStackIPv4Primary ),
507+ icBuild .withVPCEdgeSubnetIDs (validDualstackSubnets ("edge-local" ).IDs (), false ),
508+ ),
509+ availRegions : validAvailRegions (),
510+ availZones : validAvailZones (),
511+ instanceTypes : validInstanceTypes (),
512+ subnets : SubnetGroups {
513+ Private : validDualstackSubnets ("private" ),
514+ Public : validDualstackSubnets ("public" ),
515+ Edge : validDualstackSubnets ("edge-local" ),
516+ VpcID : validVPCID ,
517+ },
518+ },
519+ {
520+ name : "invalid edge pool with dual-stack and wavelength zone byo subnets" ,
521+ installConfig : icBuild .build (
522+ icBuild .withBaseBYO (),
523+ icBuild .withIPFamily (network .DualStackIPv4Primary ),
524+ icBuild .withDualStackMachineNetworks (network .DualStackIPv4Primary ),
525+ icBuild .withVPCEdgeSubnetIDs (validDualstackSubnets ("edge-wavelength" ).IDs (), false ),
526+ ),
527+ availRegions : validAvailRegions (),
528+ availZones : validAvailZones (),
529+ instanceTypes : validInstanceTypes (),
530+ subnets : SubnetGroups {
531+ Private : validDualstackSubnets ("private" ),
532+ Public : validDualstackSubnets ("public" ),
533+ Edge : validDualstackSubnets ("edge-wavelength" ),
534+ VpcID : validVPCID ,
535+ },
536+ expectErr : `^\Qplatform.aws.vpc.subnets: Invalid value: [{"id":"subnet-valid-private-a"},{"id":"subnet-valid-private-b"},{"id":"subnet-valid-private-c"},{"id":"subnet-valid-public-a"},{"id":"subnet-valid-public-b"},{"id":"subnet-valid-public-c"},{"id":"subnet-valid-edge-wavelength-a"}]: ipFamily DualStackIPv4Primary is not supported for subnets in wavelength zones\E` ,
537+ },
538+ {
539+ name : "invalid edge pool with dual-stack without byo subnets" ,
540+ installConfig : icBuild .build (
541+ icBuild .withIPFamily (network .DualStackIPv6Primary ),
542+ icBuild .withComputeMachinePool ([]types.MachinePool {{
543+ Name : types .MachinePoolEdgeRoleName ,
544+ Replicas : ptr.To [int64 ](1 ),
545+ Platform : types.MachinePoolPlatform {
546+ AWS : & aws.MachinePool {
547+ InstanceType : "m5.xlarge" ,
548+ Zones : []string {"nyc-1a" },
549+ },
550+ },
551+ }}, true ),
552+ ),
553+ availRegions : validAvailRegions (),
554+ availZones : validAvailZones (),
555+ instanceTypes : validInstanceTypes (),
556+ expectErr : `^\Qcompute[0].platform.aws: Forbidden: ipFamily DualStackIPv6Primary is only supported with user-provided subnets for edge machine pools\E` ,
557+ },
434558 {
435559 name : "valid service endpoints, custom region and no endpoints provided" ,
436560 installConfig : icBuild .build (
@@ -2019,6 +2143,81 @@ func validSubnets(subnetType string) Subnets {
20192143 return nil
20202144}
20212145
2146+ // validDualstackSubnets returns subnets with both IPv4 and IPv6 CIDRs for dual-stack testing.
2147+ func validDualstackSubnets (subnetType string ) Subnets {
2148+ switch subnetType {
2149+ case "public" :
2150+ return Subnets {
2151+ "subnet-valid-public-a" : {
2152+ ID : "subnet-valid-public-a" ,
2153+ Zone : & Zone {Name : "a" },
2154+ CIDR : "10.0.4.0/24" ,
2155+ IPv6CIDR : "2600:1f13:fe4:3::/64" ,
2156+ Public : true ,
2157+ },
2158+ "subnet-valid-public-b" : {
2159+ ID : "subnet-valid-public-b" ,
2160+ Zone : & Zone {Name : "b" },
2161+ CIDR : "10.0.5.0/24" ,
2162+ IPv6CIDR : "2600:1f13:fe4:4::/64" ,
2163+ Public : true ,
2164+ },
2165+ "subnet-valid-public-c" : {
2166+ ID : "subnet-valid-public-c" ,
2167+ Zone : & Zone {Name : "c" },
2168+ CIDR : "10.0.6.0/24" ,
2169+ IPv6CIDR : "2600:1f13:fe4:5::/64" ,
2170+ Public : true ,
2171+ },
2172+ }
2173+ case "private" :
2174+ return Subnets {
2175+ "subnet-valid-private-a" : {
2176+ ID : "subnet-valid-private-a" ,
2177+ Zone : & Zone {Name : "a" },
2178+ CIDR : "10.0.1.0/24" ,
2179+ IPv6CIDR : "2600:1f13:fe4:0::/64" ,
2180+ Public : false ,
2181+ },
2182+ "subnet-valid-private-b" : {
2183+ ID : "subnet-valid-private-b" ,
2184+ Zone : & Zone {Name : "b" },
2185+ CIDR : "10.0.2.0/24" ,
2186+ IPv6CIDR : "2600:1f13:fe4:1::/64" ,
2187+ Public : false ,
2188+ },
2189+ "subnet-valid-private-c" : {
2190+ ID : "subnet-valid-private-c" ,
2191+ Zone : & Zone {Name : "c" },
2192+ CIDR : "10.0.3.0/24" ,
2193+ IPv6CIDR : "2600:1f13:fe4:2::/64" ,
2194+ Public : false ,
2195+ },
2196+ }
2197+ case "edge-local" :
2198+ return Subnets {
2199+ "subnet-valid-edge-local-a" : {
2200+ ID : "subnet-valid-edge-local-a" ,
2201+ Zone : & Zone {Name : "nyc-1a" , Type : aws .LocalZoneType },
2202+ CIDR : "10.0.128.0/24" ,
2203+ IPv6CIDR : "2600:1f13:fe4:10::/64" ,
2204+ Public : true ,
2205+ },
2206+ }
2207+ case "edge-wavelength" :
2208+ return Subnets {
2209+ "subnet-valid-edge-wavelength-a" : {
2210+ ID : "subnet-valid-edge-wavelength-a" ,
2211+ Zone : & Zone {Name : "wlz-1" , Type : aws .WavelengthZoneType },
2212+ CIDR : "10.0.129.0/24" ,
2213+ IPv6CIDR : "" ,
2214+ Public : true ,
2215+ },
2216+ }
2217+ }
2218+ return nil
2219+ }
2220+
20222221// byoSubnetsWithRoles returns a valid collection of subnets
20232222// with assigned roles.
20242223func byoSubnetsWithRoles () []aws.Subnet {
@@ -2202,6 +2401,7 @@ func validInstanceTypes() map[string]InstanceType {
22022401 DefaultVCpus : 1 ,
22032402 MemInMiB : 2048 ,
22042403 Arches : []string {string (ec2types .ArchitectureTypeX8664 )},
2404+ Hypervisor : string (ec2types .InstanceTypeHypervisorXen ),
22052405 Networking : Networking {
22062406 IPv6Supported : true ,
22072407 },
@@ -2210,6 +2410,7 @@ func validInstanceTypes() map[string]InstanceType {
22102410 DefaultVCpus : 2 ,
22112411 MemInMiB : 8192 ,
22122412 Arches : []string {string (ec2types .ArchitectureTypeX8664 )},
2413+ Hypervisor : string (ec2types .InstanceTypeHypervisorNitro ),
22132414 Networking : Networking {
22142415 IPv6Supported : true ,
22152416 },
@@ -2218,6 +2419,7 @@ func validInstanceTypes() map[string]InstanceType {
22182419 DefaultVCpus : 4 ,
22192420 MemInMiB : 16384 ,
22202421 Arches : []string {string (ec2types .ArchitectureTypeX8664 )},
2422+ Hypervisor : string (ec2types .InstanceTypeHypervisorNitro ),
22212423 Networking : Networking {
22222424 IPv6Supported : true ,
22232425 },
@@ -2226,6 +2428,7 @@ func validInstanceTypes() map[string]InstanceType {
22262428 DefaultVCpus : 4 ,
22272429 MemInMiB : 16384 ,
22282430 Arches : []string {string (ec2types .ArchitectureTypeArm64 )},
2431+ Hypervisor : string (ec2types .InstanceTypeHypervisorNitro ),
22292432 Networking : Networking {
22302433 IPv6Supported : true ,
22312434 },
@@ -2234,6 +2437,7 @@ func validInstanceTypes() map[string]InstanceType {
22342437 DefaultVCpus : 4 ,
22352438 MemInMiB : 15360 ,
22362439 Arches : []string {string (ec2types .ArchitectureTypeX8664 )},
2440+ Hypervisor : string (ec2types .InstanceTypeHypervisorXen ),
22372441 Networking : Networking {
22382442 IPv6Supported : false ,
22392443 },
@@ -2242,6 +2446,7 @@ func validInstanceTypes() map[string]InstanceType {
22422446 DefaultVCpus : 4 ,
22432447 MemInMiB : 16384 ,
22442448 Arches : []string {string (ec2types .ArchitectureTypeX8664 )},
2449+ Hypervisor : string (ec2types .InstanceTypeHypervisorNitro ),
22452450 Features : []string {"amd-sev-snp" },
22462451 },
22472452 }
@@ -2551,3 +2756,16 @@ func (icBuild icBuildForAWS) withComputeCPUOptions(cpuOptions *aws.CPUOptions, i
25512756 ic .Compute [index ].Platform .AWS .CPUOptions = cpuOptions
25522757 }
25532758}
2759+
2760+ func (icBuild icBuildForAWS ) withDualStackMachineNetworks (ipFamily network.IPFamily ) icOption {
2761+ return func (ic * types.InstallConfig ) {
2762+ ic .Networking .MachineNetwork = []types.MachineNetworkEntry {
2763+ {CIDR : * ipnet .MustParseCIDR (validCIDR )}, // IPv4: 10.0.0.0/16
2764+ {CIDR : * ipnet .MustParseCIDR ("2600:1f13:fe4::/56" )}, // IPv6
2765+ }
2766+
2767+ if ipFamily == network .DualStackIPv6Primary {
2768+ slices .Reverse (ic .Networking .MachineNetwork )
2769+ }
2770+ }
2771+ }
0 commit comments