From 1db94aa7e40a99e50760eb35279979b121424e66 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 10 Mar 2026 15:49:14 -0700 Subject: [PATCH 1/5] validations: ensure intro-based instance type for dualstack Instance types must be Nitro-based in order to enable the IPv6 HTTP Endpoint for IMDS [0] and be able to reach IPv6 nameserver [1]. References [0] https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html [1] https://docs.aws.amazon.com/whitepapers/latest/ipv6-on-aws/designing-dns-for-ipv6.html#dns-resolution-within-a-vpc --- pkg/asset/installconfig/aws/instancetypes.go | 2 ++ pkg/asset/installconfig/aws/validation.go | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/asset/installconfig/aws/instancetypes.go b/pkg/asset/installconfig/aws/instancetypes.go index 7a2a6b11ec6..ded14210634 100644 --- a/pkg/asset/installconfig/aws/instancetypes.go +++ b/pkg/asset/installconfig/aws/instancetypes.go @@ -19,6 +19,7 @@ type InstanceType struct { DefaultVCpus int64 MemInMiB int64 Arches []string + Hypervisor string Networking Networking Features []string } @@ -38,6 +39,7 @@ func instanceTypes(ctx context.Context, client *ec2.Client) (map[string]Instance typeInfo := InstanceType{ DefaultVCpus: int64(aws.ToInt32(sdkTypeInfo.VCpuInfo.DefaultVCpus)), MemInMiB: aws.ToInt64(sdkTypeInfo.MemoryInfo.SizeInMiB), + Hypervisor: string(sdkTypeInfo.Hypervisor), } for _, arch := range sdkTypeInfo.ProcessorInfo.SupportedArchitectures { diff --git a/pkg/asset/installconfig/aws/validation.go b/pkg/asset/installconfig/aws/validation.go index 0cffc8d4efe..bdb4ed97b0a 100644 --- a/pkg/asset/installconfig/aws/validation.go +++ b/pkg/asset/installconfig/aws/validation.go @@ -449,12 +449,18 @@ func validateMachinePool(ctx context.Context, meta *Metadata, fldPath *field.Pat allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), pool.InstanceType, errMsg)) } - // dual-stack: the instance type must support IPv6 networking if platform.IPFamily.DualStackEnabled() { + // The instance type must support IPv6 networking if !typeMeta.Networking.IPv6Supported { errMsg := fmt.Sprintf("instance type %s does not support IPv6 networking", pool.InstanceType) allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), pool.InstanceType, errMsg)) } + + // The instance type must be Nitro-based to enable IPv6 IMDS endpoint + if typeMeta.Hypervisor != string(ec2types.InstanceTypeHypervisorNitro) { + errMsg := fmt.Sprintf("instance type %s is not Nitro-based", pool.InstanceType) + allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), pool.InstanceType, errMsg)) + } } } else { errMsg := fmt.Sprintf("instance type %s not found", pool.InstanceType) From 5edd01b9e0158bee5cad976ccf2dd0cd01ca37a5 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 10 Mar 2026 15:50:37 -0700 Subject: [PATCH 2/5] validations: ensure byo subnet has IPv4 and IPv6 CIDRs in dualstack In dualstack networking, we expect the BYO subnets to have an associated IPv4 and IPv6 CIDR to assign both IP family addresses to nodes. --- pkg/asset/installconfig/aws/subnet.go | 27 ++++++++++---- pkg/asset/installconfig/aws/validation.go | 43 ++++++++++++++++------- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/pkg/asset/installconfig/aws/subnet.go b/pkg/asset/installconfig/aws/subnet.go index bc08321dd9e..ae9cb4998f6 100644 --- a/pkg/asset/installconfig/aws/subnet.go +++ b/pkg/asset/installconfig/aws/subnet.go @@ -41,6 +41,9 @@ type Subnet struct { // CIDR is the subnet's CIDR block. CIDR string + // IPv6CIDR is the subnet's associated IPv6 CIDR block. + IPv6CIDR string + // Public is the flag to define the subnet public. Public bool @@ -118,15 +121,25 @@ func subnets(ctx context.Context, client *ec2.Client, subnetIDs []string, vpcID return fmt.Errorf("all subnets must belong to the same VPC: %s is from %s, but %s is from %s", *subnet.SubnetId, *subnet.VpcId, vpcFromSubnet, subnetGroups.VpcID) } + // We currently can only handle subnets with one associated IPv6 CIDR + var ipv6CIDR string + for _, snAssoc := range subnet.Ipv6CidrBlockAssociationSet { + if snAssoc.Ipv6CidrBlock != nil && + snAssoc.Ipv6CidrBlockState != nil && snAssoc.Ipv6CidrBlockState.State == ec2types.SubnetCidrBlockStateCodeAssociated { + ipv6CIDR = aws.ToString(snAssoc.Ipv6CidrBlock) + } + } + // At this point, we should be safe to dereference these fields. metas[*subnet.SubnetId] = Subnet{ - ID: *subnet.SubnetId, - ARN: *subnet.SubnetArn, - Zone: &Zone{Name: *subnet.AvailabilityZone}, - CIDR: ptr.Deref(subnet.CidrBlock, ""), - Public: false, - Tags: FromAWSTags(subnet.Tags), - VPCID: *subnet.VpcId, + ID: *subnet.SubnetId, + ARN: *subnet.SubnetArn, + Zone: &Zone{Name: *subnet.AvailabilityZone}, + CIDR: aws.ToString(subnet.CidrBlock), + IPv6CIDR: ipv6CIDR, + Public: false, + Tags: FromAWSTags(subnet.Tags), + VPCID: *subnet.VpcId, } zoneNames = append(zoneNames, *subnet.AvailabilityZone) } diff --git a/pkg/asset/installconfig/aws/validation.go b/pkg/asset/installconfig/aws/validation.go index bdb4ed97b0a..750c28d7be4 100644 --- a/pkg/asset/installconfig/aws/validation.go +++ b/pkg/asset/installconfig/aws/validation.go @@ -299,7 +299,6 @@ func (sdg *subnetDataGroups) From(ctx context.Context, meta *Metadata, providedS // validateSubnets ensures BYO subnets are valid. func validateSubnets(ctx context.Context, meta *Metadata, fldPath *field.Path, config *types.InstallConfig) field.ErrorList { allErrs := field.ErrorList{} - networking := config.Networking providedSubnets := config.AWS.VPC.Subnets publish := config.Publish @@ -330,8 +329,8 @@ func validateSubnets(ctx context.Context, meta *Metadata, fldPath *field.Path, c } allErrs = append(allErrs, validateSharedSubnets(ctx, meta, fldPath)...) - allErrs = append(allErrs, validateSubnetCIDR(fldPath, subnetDataGroups.Private, networking.MachineNetwork)...) - allErrs = append(allErrs, validateSubnetCIDR(fldPath, subnetDataGroups.Public, networking.MachineNetwork)...) + allErrs = append(allErrs, validateSubnetCIDRs(fldPath, subnetDataGroups.Private, config)...) + allErrs = append(allErrs, validateSubnetCIDRs(fldPath, subnetDataGroups.Public, config)...) if len(subnetsWithRole) > 0 { allErrs = append(allErrs, validateSubnetRoles(fldPath, subnetsWithRole, subnetDataGroups, config)...) @@ -533,27 +532,47 @@ func validateSecurityGroupIDs(ctx context.Context, meta *Metadata, fldPath *fiel return allErrs } -func validateSubnetCIDR(fldPath *field.Path, subnetDataGroup map[string]subnetData, networks []types.MachineNetworkEntry) field.ErrorList { +func validateSubnetCIDRs(fldPath *field.Path, subnetDataGroup map[string]subnetData, ic *types.InstallConfig) field.ErrorList { allErrs := field.ErrorList{} + for id, subnetData := range subnetDataGroup { fp := fldPath.Index(subnetData.Idx) - cidr, _, err := net.ParseCIDR(subnetData.CIDR) - if err != nil { - allErrs = append(allErrs, field.Invalid(fp, id, err.Error())) - continue + + // Validate subnetIPv4 CIDR + if len(subnetData.CIDR) == 0 { + allErrs = append(allErrs, field.Required(fp, "subnet does not have an associated IPv4 CIDR block")) + } else { + allErrs = append(allErrs, validateMachineNetworksContainSubnetCIDR(fp, ic.MachineNetwork, id, subnetData.CIDR)...) + } + + // If dualstack is enabled, the subnet must also have an IPv6 CIDR + if ic.AWS.IPFamily.DualStackEnabled() { + if len(subnetData.IPv6CIDR) == 0 { + allErrs = append(allErrs, field.Required(fp, "subnet does not have an associated IPv6 CIDR block")) + } else { + allErrs = append(allErrs, validateMachineNetworksContainSubnetCIDR(fp, ic.MachineNetwork, id, subnetData.IPv6CIDR)...) + } } - allErrs = append(allErrs, validateMachineNetworksContainIP(fp, networks, id, cidr)...) } + return allErrs } -func validateMachineNetworksContainIP(fldPath *field.Path, networks []types.MachineNetworkEntry, subnetName string, ip net.IP) field.ErrorList { +func validateMachineNetworksContainSubnetCIDR(fldPath *field.Path, networks []types.MachineNetworkEntry, subnetName string, cidr string) field.ErrorList { + allErrs := field.ErrorList{} + + cidrIP, _, err := net.ParseCIDR(cidr) + if err != nil { + return append(allErrs, field.Invalid(fldPath, subnetName, err.Error())) + } + for _, network := range networks { - if network.CIDR.Contains(ip) { + if network.CIDR.Contains(cidrIP) { return nil } } - return field.ErrorList{field.Invalid(fldPath, subnetName, fmt.Sprintf("subnet's CIDR range start %s is outside of the specified machine networks", ip))} + + return append(allErrs, field.Invalid(fldPath, subnetName, fmt.Sprintf("subnet's CIDR range start %s is outside of the specified machine networks", cidrIP))) } func validateDuplicateSubnetZones(fldPath *field.Path, subnetDataGroup map[string]subnetData, typ string) field.ErrorList { From 92162c9cb00bf8dd105e9c0b39d5d1fba26c09cb Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 10 Mar 2026 15:51:06 -0700 Subject: [PATCH 3/5] validations: dualstack is only supported in byo subnets in local zones In dualstack networking, edge compute pool is only valid if: - using existing subnets; and - using local zones Wavelength zones do not support IPv6. --- pkg/asset/installconfig/aws/validation.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pkg/asset/installconfig/aws/validation.go b/pkg/asset/installconfig/aws/validation.go index 750c28d7be4..a66354d2d9b 100644 --- a/pkg/asset/installconfig/aws/validation.go +++ b/pkg/asset/installconfig/aws/validation.go @@ -365,17 +365,32 @@ func validateMachinePool(ctx context.Context, meta *Metadata, fldPath *field.Pat // Edge Compute Pool / AWS Local Zones: // - is valid when installing in existing VPC; or // - is valid in new VPC when Local Zone name is defined + // - in dualstack networking: is valid when using local zones and installing in existing VPC if poolName == types.MachinePoolEdgeRoleName { if len(platform.VPC.Subnets) > 0 { + subnetFp := field.NewPath("platform", "aws", "vpc", "subnets") + edgeSubnets, err := meta.EdgeSubnets(ctx) if err != nil { errMsg := fmt.Sprintf("%s pool. %v", poolName, err.Error()) - return append(allErrs, field.Invalid(field.NewPath("platform", "aws", "vpc", "subnets"), platform.VPC.Subnets, errMsg)) + return append(allErrs, field.Invalid(subnetFp, platform.VPC.Subnets, errMsg)) } if len(edgeSubnets) == 0 { return append(allErrs, field.Required(fldPath, "the provided subnets must include valid subnets for the specified edge zones")) } + + if platform.IPFamily.DualStackEnabled() { + for _, sn := range edgeSubnets { + if sn.Zone.Type == awstypes.WavelengthZoneType { + allErrs = append(allErrs, field.Invalid(subnetFp, platform.VPC.Subnets, fmt.Sprintf("ipFamily %s is not supported for subnets in wavelength zones", platform.IPFamily))) + } + } + } } else { + if platform.IPFamily.DualStackEnabled() { + return append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("ipFamily %s is only supported with user-provided subnets for edge machine pools", string(platform.IPFamily)))) + } + if pool.Zones == nil || len(pool.Zones) == 0 { return append(allErrs, field.Required(fldPath, "zone is required when using edge machine pools")) } From b8d4e3b705a875f523e1bc06d3fef119c685e95b Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 10 Mar 2026 16:08:44 -0700 Subject: [PATCH 4/5] lint: define constants to satisfy go lint The subnet types "public", "private" and "edge" are extracted into constants to pass golint. --- pkg/asset/installconfig/aws/validation.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pkg/asset/installconfig/aws/validation.go b/pkg/asset/installconfig/aws/validation.go index a66354d2d9b..dcbdfc3710e 100644 --- a/pkg/asset/installconfig/aws/validation.go +++ b/pkg/asset/installconfig/aws/validation.go @@ -41,6 +41,12 @@ var computeReq = resourceRequirements{ minimumMemory: 8192, } +const ( + subnetTypePrivate = "private" + subnetTypePublic = "public" + subnetTypeEdge = "edge" +) + // Validate executes platform-specific validation. func Validate(ctx context.Context, meta *Metadata, config *types.InstallConfig) error { allErrs := field.ErrorList{} @@ -336,9 +342,9 @@ func validateSubnets(ctx context.Context, meta *Metadata, fldPath *field.Path, c allErrs = append(allErrs, validateSubnetRoles(fldPath, subnetsWithRole, subnetDataGroups, config)...) } else { allErrs = append(allErrs, validateUntaggedSubnets(ctx, fldPath, meta, subnetDataGroups)...) - allErrs = append(allErrs, validateDuplicateSubnetZones(fldPath, subnetDataGroups.Private, "private")...) - allErrs = append(allErrs, validateDuplicateSubnetZones(fldPath, subnetDataGroups.Public, "public")...) - allErrs = append(allErrs, validateDuplicateSubnetZones(fldPath, subnetDataGroups.Edge, "edge")...) + allErrs = append(allErrs, validateDuplicateSubnetZones(fldPath, subnetDataGroups.Private, subnetTypePrivate)...) + allErrs = append(allErrs, validateDuplicateSubnetZones(fldPath, subnetDataGroups.Public, subnetTypePublic)...) + allErrs = append(allErrs, validateDuplicateSubnetZones(fldPath, subnetDataGroups.Edge, subnetTypeEdge)...) } privateZones := sets.New[string]() @@ -699,9 +705,9 @@ func validateSubnetRoles(fldPath *field.Path, subnetsWithRole map[awstypes.Subne } if ingressSubnet.Public != config.PublicIngress() { - subnetType := "private" + subnetType := subnetTypePrivate if ingressSubnet.Public { - subnetType = "public" + subnetType = subnetTypePublic } allErrs = append(allErrs, field.Invalid(fldPath.Index(ingressSubnet.Idx), ingressSubnet.ID, fmt.Sprintf("subnet %s has role %s and is %s, which is not allowed when publish is set to %s", ingressSubnet.ID, awstypes.IngressControllerLBSubnetRole, subnetType, config.Publish))) From 262ce8d97bd2dee5fb0ad1467361e5a775e5e2c1 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 10 Mar 2026 16:10:12 -0700 Subject: [PATCH 5/5] tests: update unit tests --- .../installconfig/aws/validation_test.go | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/pkg/asset/installconfig/aws/validation_test.go b/pkg/asset/installconfig/aws/validation_test.go index f368199baa2..69dbce7a0b0 100644 --- a/pkg/asset/installconfig/aws/validation_test.go +++ b/pkg/asset/installconfig/aws/validation_test.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "os" + "slices" "sort" "strings" "testing" @@ -389,6 +390,72 @@ func TestValidate(t *testing.T) { instanceTypes: validInstanceTypes(), 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`, }, + { + name: "invalid dual-stack control plane instance type is not Nitro-based", + installConfig: icBuild.build(icBuild.withInstanceType("m5.xlarge", "t2.small", "m5.large"), icBuild.withIPFamily(network.DualStackIPv4Primary)), + availRegions: validAvailRegions(), + availZones: validAvailZones(), + instanceTypes: validInstanceTypes(), + expectErr: `controlPlane\.platform\.aws\.type: Invalid value: "t2\.small": instance type t2\.small is not Nitro-based`, + }, + { + name: "invalid dual-stack compute instance type is not Nitro-based", + installConfig: icBuild.build(icBuild.withInstanceType("m5.xlarge", "m5.xlarge", "t2.small"), icBuild.withIPFamily(network.DualStackIPv4Primary)), + availRegions: validAvailRegions(), + availZones: validAvailZones(), + instanceTypes: validInstanceTypes(), + expectErr: `compute\[0\]\.platform\.aws\.type: Invalid value: "t2\.small": instance type t2\.small is not Nitro-based`, + }, + { + name: "invalid dual-stack default machine platform instance type is not Nitro-based", + installConfig: icBuild.build(icBuild.withInstanceType("t2.small", "", ""), icBuild.withIPFamily(network.DualStackIPv6Primary)), + availRegions: validAvailRegions(), + availZones: validAvailZones(), + instanceTypes: validInstanceTypes(), + 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`, + }, + { + name: "valid dual-stack byo subnets with IPv6 CIDRs", + installConfig: icBuild.build( + icBuild.withBaseBYO(), + icBuild.withIPFamily(network.DualStackIPv4Primary), + icBuild.withDualStackMachineNetworks(network.DualStackIPv4Primary), + ), + availRegions: validAvailRegions(), + availZones: validAvailZones(), + instanceTypes: validInstanceTypes(), + subnets: SubnetGroups{ + Private: validDualstackSubnets("private"), + Public: validDualstackSubnets("public"), + VpcID: validVPCID, + }, + }, + { + name: "invalid dual-stack byo subnets, some subnets missing IPv6 CIDR", + installConfig: icBuild.build( + icBuild.withBaseBYO(), + icBuild.withVPCSubnetIDs([]string{"invalid-private-cidr-subnet", "invalid-public-cidr-subnet"}, false), + icBuild.withIPFamily(network.DualStackIPv4Primary), + icBuild.withDualStackMachineNetworks(network.DualStackIPv4Primary), + ), + availRegions: validAvailRegions(), + availZones: append(validAvailZones(), "zone-for-invalid-cidr-subnet"), + instanceTypes: validInstanceTypes(), + subnets: SubnetGroups{ + Private: mergeSubnets(validDualstackSubnets("private"), Subnets{"invalid-private-cidr-subnet": Subnet{ + ID: "invalid-private-cidr-subnet", + Zone: &Zone{Name: "zone-for-invalid-cidr-subnet"}, + CIDR: "10.0.7.0/24", + }}), + Public: mergeSubnets(validDualstackSubnets("public"), Subnets{"invalid-public-cidr-subnet": Subnet{ + ID: "invalid-public-cidr-subnet", + Zone: &Zone{Name: "zone-for-invalid-cidr-subnet"}, + CIDR: "10.0.8.0/24", + }}), + VpcID: validVPCID, + }, + 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$`, + }, { name: "invalid edge pool, missing zones", installConfig: icBuild.build( @@ -430,6 +497,63 @@ func TestValidate(t *testing.T) { availRegions: validAvailRegions(), 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\]$`, }, + { + name: "valid edge pool with dual-stack and local zone byo subnets", + installConfig: icBuild.build( + icBuild.withBaseBYO(), + icBuild.withIPFamily(network.DualStackIPv4Primary), + icBuild.withDualStackMachineNetworks(network.DualStackIPv4Primary), + icBuild.withVPCEdgeSubnetIDs(validDualstackSubnets("edge-local").IDs(), false), + ), + availRegions: validAvailRegions(), + availZones: validAvailZones(), + instanceTypes: validInstanceTypes(), + subnets: SubnetGroups{ + Private: validDualstackSubnets("private"), + Public: validDualstackSubnets("public"), + Edge: validDualstackSubnets("edge-local"), + VpcID: validVPCID, + }, + }, + { + name: "invalid edge pool with dual-stack and wavelength zone byo subnets", + installConfig: icBuild.build( + icBuild.withBaseBYO(), + icBuild.withIPFamily(network.DualStackIPv4Primary), + icBuild.withDualStackMachineNetworks(network.DualStackIPv4Primary), + icBuild.withVPCEdgeSubnetIDs(validDualstackSubnets("edge-wavelength").IDs(), false), + ), + availRegions: validAvailRegions(), + availZones: validAvailZones(), + instanceTypes: validInstanceTypes(), + subnets: SubnetGroups{ + Private: validDualstackSubnets("private"), + Public: validDualstackSubnets("public"), + Edge: validDualstackSubnets("edge-wavelength"), + VpcID: validVPCID, + }, + 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`, + }, + { + name: "invalid edge pool with dual-stack without byo subnets", + installConfig: icBuild.build( + icBuild.withIPFamily(network.DualStackIPv6Primary), + icBuild.withComputeMachinePool([]types.MachinePool{{ + Name: types.MachinePoolEdgeRoleName, + Replicas: ptr.To[int64](1), + Platform: types.MachinePoolPlatform{ + AWS: &aws.MachinePool{ + InstanceType: "m5.xlarge", + Zones: []string{"nyc-1a"}, + }, + }, + }}, true), + ), + availRegions: validAvailRegions(), + availZones: validAvailZones(), + instanceTypes: validInstanceTypes(), + expectErr: `^\Qcompute[0].platform.aws: Forbidden: ipFamily DualStackIPv6Primary is only supported with user-provided subnets for edge machine pools\E`, + }, { name: "valid service endpoints, custom region and no endpoints provided", installConfig: icBuild.build( @@ -1896,6 +2020,81 @@ func validSubnets(subnetType string) Subnets { return nil } +// validDualstackSubnets returns subnets with both IPv4 and IPv6 CIDRs for dual-stack testing. +func validDualstackSubnets(subnetType string) Subnets { + switch subnetType { + case "public": + return Subnets{ + "subnet-valid-public-a": { + ID: "subnet-valid-public-a", + Zone: &Zone{Name: "a"}, + CIDR: "10.0.4.0/24", + IPv6CIDR: "2600:1f13:fe4:3::/64", + Public: true, + }, + "subnet-valid-public-b": { + ID: "subnet-valid-public-b", + Zone: &Zone{Name: "b"}, + CIDR: "10.0.5.0/24", + IPv6CIDR: "2600:1f13:fe4:4::/64", + Public: true, + }, + "subnet-valid-public-c": { + ID: "subnet-valid-public-c", + Zone: &Zone{Name: "c"}, + CIDR: "10.0.6.0/24", + IPv6CIDR: "2600:1f13:fe4:5::/64", + Public: true, + }, + } + case "private": + return Subnets{ + "subnet-valid-private-a": { + ID: "subnet-valid-private-a", + Zone: &Zone{Name: "a"}, + CIDR: "10.0.1.0/24", + IPv6CIDR: "2600:1f13:fe4:0::/64", + Public: false, + }, + "subnet-valid-private-b": { + ID: "subnet-valid-private-b", + Zone: &Zone{Name: "b"}, + CIDR: "10.0.2.0/24", + IPv6CIDR: "2600:1f13:fe4:1::/64", + Public: false, + }, + "subnet-valid-private-c": { + ID: "subnet-valid-private-c", + Zone: &Zone{Name: "c"}, + CIDR: "10.0.3.0/24", + IPv6CIDR: "2600:1f13:fe4:2::/64", + Public: false, + }, + } + case "edge-local": + return Subnets{ + "subnet-valid-edge-local-a": { + ID: "subnet-valid-edge-local-a", + Zone: &Zone{Name: "nyc-1a", Type: aws.LocalZoneType}, + CIDR: "10.0.128.0/24", + IPv6CIDR: "2600:1f13:fe4:10::/64", + Public: true, + }, + } + case "edge-wavelength": + return Subnets{ + "subnet-valid-edge-wavelength-a": { + ID: "subnet-valid-edge-wavelength-a", + Zone: &Zone{Name: "wlz-1", Type: aws.WavelengthZoneType}, + CIDR: "10.0.129.0/24", + IPv6CIDR: "", + Public: true, + }, + } + } + return nil +} + // byoSubnetsWithRoles returns a valid collection of subnets // with assigned roles. func byoSubnetsWithRoles() []aws.Subnet { @@ -2079,6 +2278,7 @@ func validInstanceTypes() map[string]InstanceType { DefaultVCpus: 1, MemInMiB: 2048, Arches: []string{string(ec2types.ArchitectureTypeX8664)}, + Hypervisor: string(ec2types.InstanceTypeHypervisorXen), Networking: Networking{ IPv6Supported: true, }, @@ -2087,6 +2287,7 @@ func validInstanceTypes() map[string]InstanceType { DefaultVCpus: 2, MemInMiB: 8192, Arches: []string{string(ec2types.ArchitectureTypeX8664)}, + Hypervisor: string(ec2types.InstanceTypeHypervisorNitro), Networking: Networking{ IPv6Supported: true, }, @@ -2095,6 +2296,7 @@ func validInstanceTypes() map[string]InstanceType { DefaultVCpus: 4, MemInMiB: 16384, Arches: []string{string(ec2types.ArchitectureTypeX8664)}, + Hypervisor: string(ec2types.InstanceTypeHypervisorNitro), Networking: Networking{ IPv6Supported: true, }, @@ -2103,6 +2305,7 @@ func validInstanceTypes() map[string]InstanceType { DefaultVCpus: 4, MemInMiB: 16384, Arches: []string{string(ec2types.ArchitectureTypeArm64)}, + Hypervisor: string(ec2types.InstanceTypeHypervisorNitro), Networking: Networking{ IPv6Supported: true, }, @@ -2111,6 +2314,7 @@ func validInstanceTypes() map[string]InstanceType { DefaultVCpus: 4, MemInMiB: 15360, Arches: []string{string(ec2types.ArchitectureTypeX8664)}, + Hypervisor: string(ec2types.InstanceTypeHypervisorXen), Networking: Networking{ IPv6Supported: false, }, @@ -2119,6 +2323,7 @@ func validInstanceTypes() map[string]InstanceType { DefaultVCpus: 4, MemInMiB: 16384, Arches: []string{string(ec2types.ArchitectureTypeX8664)}, + Hypervisor: string(ec2types.InstanceTypeHypervisorNitro), Features: []string{"amd-sev-snp"}, }, } @@ -2400,3 +2605,16 @@ func (icBuild icBuildForAWS) withComputeCPUOptions(cpuOptions *aws.CPUOptions, i ic.Compute[index].Platform.AWS.CPUOptions = cpuOptions } } + +func (icBuild icBuildForAWS) withDualStackMachineNetworks(ipFamily network.IPFamily) icOption { + return func(ic *types.InstallConfig) { + ic.Networking.MachineNetwork = []types.MachineNetworkEntry{ + {CIDR: *ipnet.MustParseCIDR(validCIDR)}, // IPv4: 10.0.0.0/16 + {CIDR: *ipnet.MustParseCIDR("2600:1f13:fe4::/56")}, // IPv6 + } + + if ipFamily == network.DualStackIPv6Primary { + slices.Reverse(ic.Networking.MachineNetwork) + } + } +}