Skip to content

Commit

Permalink
Dynamically filter out invalid instance types
Browse files Browse the repository at this point in the history
  • Loading branch information
mselim00 committed Dec 5, 2024
1 parent a626e37 commit f902cc2
Showing 1 changed file with 81 additions and 6 deletions.
87 changes: 81 additions & 6 deletions kubetest2/internal/deployers/eksapi/nodegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
_ "embed"
"errors"
"fmt"
"slices"
"strconv"
"strings"
"time"
Expand All @@ -18,6 +19,7 @@ import (
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go-v2/service/eks"
ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
"github.com/aws/smithy-go"
"k8s.io/klog/v2"

"github.com/aws/aws-k8s-tester/kubetest2/internal/deployers/eksapi/templates"
Expand Down Expand Up @@ -78,9 +80,9 @@ func (m *NodegroupManager) createNodegroup(infra *Infrastructure, cluster *Clust
return fmt.Errorf("failed to describe AMI when populating default instance types: %s: %v", opts.AMI, err)
} else {
amiArch := out.Images[0].Architecture
defaultInstanceTypes, ok := defaultInstanceTypesByEC2ArchitectureValues[amiArch]
if !ok {
return fmt.Errorf("no default instance types known for AMI architecture: %v", amiArch)
defaultInstanceTypes, err := m.getValidDefaultInstanceTypesByEC2Arch(amiArch)
if err != nil {
return err
}
opts.InstanceTypes = defaultInstanceTypes
klog.V(2).Infof("Using default instance types for AMI architecture: %v: %v", amiArch, opts.InstanceTypes)
Expand Down Expand Up @@ -116,9 +118,9 @@ func (m *NodegroupManager) createManagedNodegroup(infra *Infrastructure, cluster
} else {
// managed nodegroups uses a t3.medium by default at the time of writing
// this only supports 17 pods, which can cause some flakes in the k8s e2e suite
defaultInstanceTypes, ok := defaultInstanceTypesByEKSAMITypes[input.AmiType]
if !ok {
return fmt.Errorf("no default instance types known for AmiType: %v", input.AmiType)
defaultInstanceTypes, err := m.getValidDefaultInstanceTypesByEKSAMIType(input.AmiType)
if err != nil {
return err
}
input.InstanceTypes = defaultInstanceTypes
}
Expand Down Expand Up @@ -532,3 +534,76 @@ func (m *NodegroupManager) getSubnetWithCapacity(infra *Infrastructure, opts *de
klog.Infof("Using subnet: %s", subnetId)
return subnetId, capacityReservationId, nil
}

func (m *NodegroupManager) getValidDefaultInstanceTypesByEKSAMIType(amiType ekstypes.AMITypes) ([]string, error) {
defaults, ok := defaultInstanceTypesByEKSAMITypes[amiType]
if !ok {
return []string{}, fmt.Errorf("no default instance types known for AmiType: %v", amiType)
}

return m.getValidInstanceTypesFromList(defaults), nil
}

func (m *NodegroupManager) getValidDefaultInstanceTypesByEC2Arch(arch ec2types.ArchitectureValues) ([]string, error) {
defaults, ok := defaultInstanceTypesByEC2ArchitectureValues[arch]
if !ok {
return []string{}, fmt.Errorf("no default instance types known for AMI architecture: %v", arch)
}

return m.getValidInstanceTypesFromList(defaults), nil
}

func (m *NodegroupManager) getValidInstanceTypesFromList(desiredInstanceTypes []string) []string {
desiredEc2InstanceTypes := convertToEc2InstanceTypes(desiredInstanceTypes)
_, err := m.clients.EC2().DescribeInstanceTypes(context.TODO(), &ec2.DescribeInstanceTypesInput{
InstanceTypes: desiredEc2InstanceTypes,
})

invalidInstances := []string{}
if err != nil {
var apierr smithy.APIError
if errors.As(err, &apierr) {
errorCode := apierr.ErrorCode()
if errorCode == "InvalidInstanceType" {
invalidInstances = parseInvalidInstanceTypesStrings(apierr.ErrorMessage())
} else {
klog.Infof("Could not determine invalid instance types, received error: %s", apierr.ErrorCode())
}
}
}

return parseValidInstanceTypes(desiredInstanceTypes, invalidInstances)
}

func convertToEc2InstanceTypes(instanceTypes []string) []ec2types.InstanceType {
desiredEc2InstanceTypes := make([]ec2types.InstanceType, len(instanceTypes))
for i, instanceType := range instanceTypes {
desiredEc2InstanceTypes[i] = ec2types.InstanceType(instanceType)
}
return desiredEc2InstanceTypes
}

func parseInvalidInstanceTypesStrings(errString string) []string {
invalidInstancesStartIndex := strings.LastIndex(errString, "[") + 1 // trim leading '['
invalidInstancesEndIndex := len(errString) - 1 // trim trailing ']'
instanceSeparator := ", " // divider between instance types in output

invalidInstancesStr := errString[invalidInstancesStartIndex:invalidInstancesEndIndex]
invalidInstancesArr := strings.Split(invalidInstancesStr, instanceSeparator)

return invalidInstancesArr
}

func parseValidInstanceTypes(desiredInstances []string, invalidInstances []string) []string {
validInstances := []string{}

for _, instanceType := range desiredInstances {
if !slices.Contains(invalidInstances, instanceType) {
validInstances = append(validInstances, instanceType)
} else {
klog.Infof("Eliminating instance type %s as an option", instanceType)
}
}

return validInstances
}

0 comments on commit f902cc2

Please sign in to comment.