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
35 changes: 34 additions & 1 deletion pkg/types/aws/validation/machinepool.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var (
types.ArchitectureARM64: true,
}

// validArchitectureValues lists the supported arches for AWS
// validArchitectureValues lists the supported arches for AWS.
validArchitectureValues = func() []string {
v := make([]string, 0, len(validArchitectures))
for m := range validArchitectures {
Expand All @@ -39,6 +39,14 @@ var (
// We set a user limit of 10 and reserve 6 for use by OpenShift.
const maxUserSecurityGroupsCount = 10

// maxThroughputIopsRatio is the maximum allowed ratio of throughput (MiBps) to IOPS for gp3 volumes.
// AWS constraint: throughput (MiBps) / iops <= 0.25 (maximum 0.25 MiBps per iops).
const maxThroughputIopsRatio = 0.25

// gp3DefaultIOPS is the default IOPS value for gp3 volumes when not explicitly set.
// According to AWS documentation, gp3 volumes have a baseline of 3,000 IOPS.
const gp3DefaultIOPS int32 = 3000

// ValidateMachinePool checks that the specified machine pool is valid.
func ValidateMachinePool(platform *aws.Platform, p *aws.MachinePool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
Expand Down Expand Up @@ -129,6 +137,31 @@ func validateThroughput(p *aws.MachinePool, fldPath *field.Path) field.ErrorList
case "gp3":
if throughput < 125 || throughput > 2000 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("throughput"), throughput, "throughput must be between 125 MiB/s and 2000 MiB/s"))
return allErrs
}
// AWS constraint: throughput (MiBps) / iops <= 0.25 (maximum 0.25 MiBps per iops)
// When iops is 0 or omitted, AWS defaults to 3000 iops
// Validate that the throughput/iops ratio does not exceed the maximum allowed ratio
iops := int32(p.EC2RootVolume.IOPS)
if iops == 0 {
// Use AWS default of 3000 iops when iops is not set
iops = gp3DefaultIOPS
}
// Use integer comparison to avoid floating point precision issues
// throughput / iops <= 0.25 is equivalent to throughput * 4 <= iops
if throughput*4 > iops {
// Calculate minimum required iops: iops >= throughput / 0.25
// Round up to nearest 100 for safety
calculatedIOPS := ((throughput * 4) + 99) / 100 * 100
// According to AWS documentation, gp3 volumes have a baseline of 3,000 IOPS
minIOPS := max(calculatedIOPS, gp3DefaultIOPS)
// Calculate ratio for error message display
ratio := float32(throughput) / float32(iops)
allErrs = append(allErrs, field.Invalid(
fldPath.Child("throughput"),
throughput,
fmt.Sprintf("throughput (MiBps) to iops ratio of %.6f is too high; maximum is %.6f MiBps per iops. When iops is not set, AWS defaults to %d iops. Please set iops to at least %d to satisfy the constraint", ratio, maxThroughputIopsRatio, gp3DefaultIOPS, minIOPS),
))
}
default:
allErrs = append(allErrs, field.Invalid(fldPath.Child("throughput"), throughput, fmt.Sprintf("throughput not supported for type %s", volumeType)))
Expand Down
42 changes: 38 additions & 4 deletions pkg/types/aws/validation/machinepool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func TestValidateMachinePool(t *testing.T) {
IOPS: 10000,
},
},
expected: fmt.Sprintf("test-path.iops: Invalid value: 10000: iops not supported for type gp2"),
expected: "test-path.iops: Invalid value: 10000: iops not supported for type gp2",
},
{
name: "invalid zone",
Expand All @@ -96,7 +96,7 @@ func TestValidateMachinePool(t *testing.T) {
Size: 128,
},
},
expected: fmt.Sprintf("test-path.type: Invalid value: \"bad-volume-type\": failed to find volume type bad-volume-type"),
expected: "test-path.type: Invalid value: \"bad-volume-type\": failed to find volume type bad-volume-type",
},
{
name: "invalid volume size using negative",
Expand All @@ -107,7 +107,7 @@ func TestValidateMachinePool(t *testing.T) {
IOPS: 10000,
},
},
expected: fmt.Sprintf("test-path.size: Invalid value: -1: volume size value must be a positive number"),
expected: "test-path.size: Invalid value: -1: volume size value must be a positive number",
},
{
name: "invalid metadata auth option",
Expand All @@ -119,15 +119,49 @@ func TestValidateMachinePool(t *testing.T) {
expected: `^test-path\.authentication: Invalid value: \"foobarbaz\": must be either Required or Optional$`,
},
{
name: "valid root volume throughput, within allowed range",
name: "valid root volume throughput with sufficient iops",
pool: &aws.MachinePool{
EC2RootVolume: aws.EC2RootVolume{
Type: "gp3",
Size: 100,
Throughput: ptr.To(int32(1200)),
IOPS: 4800, // 1200 / 4800 = 0.25, which is the maximum allowed ratio
},
},
},
{
name: "valid root volume throughput with default iops (within ratio)",
pool: &aws.MachinePool{
EC2RootVolume: aws.EC2RootVolume{
Type: "gp3",
Size: 100,
Throughput: ptr.To(int32(750)), // 750 / 3000 = 0.25, which is the maximum allowed ratio
},
},
},
{
name: "invalid root volume throughput, exceeds ratio with default iops",
pool: &aws.MachinePool{
EC2RootVolume: aws.EC2RootVolume{
Type: "gp3",
Size: 100,
Throughput: ptr.To(int32(1200)), // 1200 / 3000 = 0.4 > 0.25
},
},
expected: `^test-path\.throughput: Invalid value: 1200: throughput \(MiBps\) to iops ratio of 0\.400000 is too high; maximum is 0\.250000 MiBps per iops\. When iops is not set, AWS defaults to 3000 iops\. Please set iops to at least 4800 to satisfy the constraint$`,
},
{
name: "invalid root volume throughput, exceeds ratio with explicit iops",
pool: &aws.MachinePool{
EC2RootVolume: aws.EC2RootVolume{
Type: "gp3",
Size: 100,
Throughput: ptr.To(int32(1000)),
IOPS: 3000, // 1000 / 3000 = 0.333 > 0.25
},
},
expected: `^test-path\.throughput: Invalid value: 1000: throughput \(MiBps\) to iops ratio of 0\.333333 is too high; maximum is 0\.250000 MiBps per iops\. When iops is not set, AWS defaults to 3000 iops\. Please set iops to at least 4000 to satisfy the constraint$`,
},
{
name: "valid root volume throughput, nil or unspecified",
pool: &aws.MachinePool{
Expand Down