Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 33 additions & 16 deletions pkg/asset/installconfig/aws/instancetypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,53 @@ import (
"context"
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
)

// Networking describes the network settings for an instance type.
type Networking struct {
// IPv6Supported indicates whether IPv6 is supported.
IPv6Supported bool
}

// InstanceType holds metadata for an instance type.
type InstanceType struct {
DefaultVCpus int64
MemInMiB int64
Arches []string
Networking Networking
}

// instanceTypes retrieves a list of instance types for the given region.
func instanceTypes(ctx context.Context, session *session.Session, region string) (map[string]InstanceType, error) {
func instanceTypes(ctx context.Context, client *ec2.Client) (map[string]InstanceType, error) {
types := map[string]InstanceType{}

client := ec2.New(session, aws.NewConfig().WithRegion(region))
if err := client.DescribeInstanceTypesPagesWithContext(ctx,
&ec2.DescribeInstanceTypesInput{},
func(page *ec2.DescribeInstanceTypesOutput, lastPage bool) bool {
for _, info := range page.InstanceTypes {
types[*info.InstanceType] = InstanceType{
DefaultVCpus: aws.Int64Value(info.VCpuInfo.DefaultVCpus),
MemInMiB: aws.Int64Value(info.MemoryInfo.SizeInMiB),
Arches: aws.StringValueSlice(info.ProcessorInfo.SupportedArchitectures),
paginator := ec2.NewDescribeInstanceTypesPaginator(client, &ec2.DescribeInstanceTypesInput{})
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
return nil, fmt.Errorf("failed to list instance types: %w", err)
}

for _, sdkTypeInfo := range page.InstanceTypes {
typeInfo := InstanceType{
DefaultVCpus: int64(aws.ToInt32(sdkTypeInfo.VCpuInfo.DefaultVCpus)),
MemInMiB: aws.ToInt64(sdkTypeInfo.MemoryInfo.SizeInMiB),
}

for _, arch := range sdkTypeInfo.ProcessorInfo.SupportedArchitectures {
typeInfo.Arches = append(typeInfo.Arches, string(arch))
}

if netInfo := sdkTypeInfo.NetworkInfo; netInfo != nil {
typeInfo.Networking = Networking{
IPv6Supported: aws.ToBool(netInfo.Ipv6Supported),
}
}
return !lastPage
}); err != nil {
return nil, fmt.Errorf("fetching instance types: %w", err)

types[string(sdkTypeInfo.InstanceType)] = typeInfo
}
}

return types, nil
Expand Down
4 changes: 2 additions & 2 deletions pkg/asset/installconfig/aws/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,12 +377,12 @@ func (m *Metadata) InstanceTypes(ctx context.Context) (map[string]InstanceType,
defer m.mutex.Unlock()

if len(m.instanceTypes) == 0 {
session, err := m.unlockedSession(ctx)
client, err := m.EC2Client(ctx)
if err != nil {
return nil, err
}

m.instanceTypes, err = instanceTypes(ctx, session, m.Region)
m.instanceTypes, err = instanceTypes(ctx, client)
if err != nil {
return nil, fmt.Errorf("error listing instance types: %w", err)
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/asset/installconfig/aws/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,14 @@ func validateMachinePool(ctx context.Context, meta *Metadata, fldPath *field.Pat
errMsg := fmt.Sprintf("instance type supported architectures %s do not match specified architecture %s", sets.List(instanceArches), arch)
allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), pool.InstanceType, errMsg))
}

// dual-stack: the instance type must support IPv6 networking
if platform.IPFamily.DualStackEnabled() {
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))
}
}
} else {
errMsg := fmt.Sprintf("instance type %s not found", pool.InstanceType)
allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), pool.InstanceType, errMsg))
Expand Down
58 changes: 58 additions & 0 deletions pkg/asset/installconfig/aws/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/openshift/installer/pkg/ipnet"
"github.com/openshift/installer/pkg/types"
"github.com/openshift/installer/pkg/types/aws"
"github.com/openshift/installer/pkg/types/network"
)

var (
Expand Down Expand Up @@ -155,6 +156,37 @@ func TestValidate(t *testing.T) {
},
expectErr: `^compute\[1\].architecture: Invalid value: "arm64": all compute machine pools must be of the same architecture$`,
},
{
name: "valid dual-stack with IPv6 supporting instance types",
installConfig: icBuild.build(icBuild.withInstanceType("m5.xlarge", "m5.xlarge", "m5.large"), icBuild.withIPFamily(network.DualStackIPv4Primary)),
availRegions: validAvailRegions(),
availZones: validAvailZones(),
instanceTypes: validInstanceTypes(),
},
{
name: "invalid dual-stack control plane instance type does not support IPv6",
installConfig: icBuild.build(icBuild.withInstanceType("m5.xlarge", "m1.xlarge", "m5.large"), icBuild.withIPFamily(network.DualStackIPv4Primary)),
availRegions: validAvailRegions(),
availZones: validAvailZones(),
instanceTypes: validInstanceTypes(),
expectErr: `controlPlane\.platform\.aws\.type: Invalid value: "m1\.xlarge": instance type m1\.xlarge does not support IPv6 networking`,
},
{
name: "invalid dual-stack compute instance type does not support IPv6",
installConfig: icBuild.build(icBuild.withInstanceType("m5.xlarge", "m5.xlarge", "m1.xlarge"), icBuild.withIPFamily(network.DualStackIPv4Primary)),
availRegions: validAvailRegions(),
availZones: validAvailZones(),
instanceTypes: validInstanceTypes(),
expectErr: `compute\[0\]\.platform\.aws\.type: Invalid value: "m1\.xlarge": instance type m1\.xlarge does not support IPv6 networking`,
},
{
name: "invalid dual-stack default machine platform instance types do not support IPv6",
installConfig: icBuild.build(icBuild.withInstanceType("m1.xlarge", "", ""), icBuild.withIPFamily(network.DualStackIPv6Primary)),
availRegions: validAvailRegions(),
availZones: validAvailZones(),
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 edge pool, missing zones",
installConfig: icBuild.build(
Expand Down Expand Up @@ -1761,21 +1793,41 @@ func validInstanceTypes() map[string]InstanceType {
DefaultVCpus: 1,
MemInMiB: 2048,
Arches: []string{ec2.ArchitectureTypeX8664},
Networking: Networking{
IPv6Supported: true,
},
},
"m5.large": {
DefaultVCpus: 2,
MemInMiB: 8192,
Arches: []string{ec2.ArchitectureTypeX8664},
Networking: Networking{
IPv6Supported: true,
},
},
"m5.xlarge": {
DefaultVCpus: 4,
MemInMiB: 16384,
Arches: []string{ec2.ArchitectureTypeX8664},
Networking: Networking{
IPv6Supported: true,
},
},
"m6g.xlarge": {
DefaultVCpus: 4,
MemInMiB: 16384,
Arches: []string{ec2.ArchitectureTypeArm64},
Networking: Networking{
IPv6Supported: true,
},
},
"m1.xlarge": {
DefaultVCpus: 4,
MemInMiB: 15360,
Arches: []string{ec2.ArchitectureTypeX8664},
Networking: Networking{
IPv6Supported: false,
},
},
}
}
Expand Down Expand Up @@ -2025,3 +2077,9 @@ func (icBuild icBuildForAWS) withPublicIPv4Pool(publicIPv4Pool string) icOption
ic.Platform.AWS.PublicIpv4Pool = publicIPv4Pool
}
}

func (icBuild icBuildForAWS) withIPFamily(ipFamily network.IPFamily) icOption {
return func(ic *types.InstallConfig) {
ic.Platform.AWS.IPFamily = ipFamily
}
}