diff --git a/data/data/install.openshift.io_installconfigs.yaml b/data/data/install.openshift.io_installconfigs.yaml index f9aa81ea52..6107019f8a 100644 --- a/data/data/install.openshift.io_installconfigs.yaml +++ b/data/data/install.openshift.io_installconfigs.yaml @@ -221,6 +221,13 @@ spec: (GiB). minimum: 0 type: integer + throughput: + description: |- + Throughput to provision in MiB/s supported for the volume type. This + is currently only applicable to volumes of type gp3. + format: int64 + minimum: 0 + type: integer type: description: Type defines the type of the volume. type: string @@ -1739,6 +1746,13 @@ spec: gibibytes (GiB). minimum: 0 type: integer + throughput: + description: |- + Throughput to provision in MiB/s supported for the volume type. This + is currently only applicable to volumes of type gp3. + format: int64 + minimum: 0 + type: integer type: description: Type defines the type of the volume. type: string @@ -3197,6 +3211,13 @@ spec: (GiB). minimum: 0 type: integer + throughput: + description: |- + Throughput to provision in MiB/s supported for the volume type. This + is currently only applicable to volumes of type gp3. + format: int64 + minimum: 0 + type: integer type: description: Type defines the type of the volume. type: string @@ -4848,6 +4869,13 @@ spec: (GiB). minimum: 0 type: integer + throughput: + description: |- + Throughput to provision in MiB/s supported for the volume type. This + is currently only applicable to volumes of type gp3. + format: int64 + minimum: 0 + type: integer type: description: Type defines the type of the volume. type: string diff --git a/docs/user/aws/customization.md b/docs/user/aws/customization.md index 2bf3fd6603..cbb1d3ac23 100644 --- a/docs/user/aws/customization.md +++ b/docs/user/aws/customization.md @@ -18,6 +18,8 @@ Beyond the [platform-agnostic `install-config.yaml` properties](../customization * `rootVolume` (optional object): Defines the root volume for EC2 instances in the machine pool. * `iops` (optional integer): The amount of provisioned [IOPS][volume-iops]. This is only valid for `type` `io1`. + * `throughput` (optional integer): The amount of throughput in MiB/s [Throughput Performance][volume-throughput]. + This is only valid for `type` `gp3`. * `size` (optional integer): Size of the root volume in gibibytes (GiB). * `type` (optional string): The [type of volume][volume-type]. * `kmsKeyARN` (optional string): The [ARN of KMS key][kms-key] that should be used to encrypt the EBS volume. @@ -119,4 +121,5 @@ sshKey: ssh-ed25519 AAAA... [kms-key-default]: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_GetEbsDefaultKmsKeyId.html [kms-key]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html [volume-iops]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-io-characteristics.html +[volume-throughput]: https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html#gp3-ebs-volume-type [volume-type]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html diff --git a/pkg/asset/machines/aws/awsmachines.go b/pkg/asset/machines/aws/awsmachines.go index bb33f8a6fc..eafaaa230b 100644 --- a/pkg/asset/machines/aws/awsmachines.go +++ b/pkg/asset/machines/aws/awsmachines.go @@ -107,6 +107,7 @@ func GenerateMachines(clusterID string, in *MachineInput) ([]*asset.RuntimeFile, Size: int64(mpool.EC2RootVolume.Size), Type: capa.VolumeType(mpool.EC2RootVolume.Type), IOPS: int64(mpool.EC2RootVolume.IOPS), + Throughput: ptr.To(mpool.EC2RootVolume.Throughput), Encrypted: ptr.To(true), EncryptionKey: mpool.KMSKeyARN, }, diff --git a/pkg/asset/machines/aws/awsmachines_test.go b/pkg/asset/machines/aws/awsmachines_test.go index c1766555ba..ee4b0a9ee1 100644 --- a/pkg/asset/machines/aws/awsmachines_test.go +++ b/pkg/asset/machines/aws/awsmachines_test.go @@ -115,7 +115,8 @@ func TestGenerateMachines(t *testing.T) { }, SSHKeyName: ptr.To(""), RootVolume: &capa.Volume{ - Encrypted: ptr.To(true), + Encrypted: ptr.To(true), + Throughput: ptr.To(int64(0)), }, UncompressedUserData: ptr.To(true), Ignition: &capa.Ignition{ @@ -171,7 +172,8 @@ func TestGenerateMachines(t *testing.T) { }, SSHKeyName: ptr.To(""), RootVolume: &capa.Volume{ - Encrypted: ptr.To(true), + Encrypted: ptr.To(true), + Throughput: ptr.To(int64(0)), }, UncompressedUserData: ptr.To(true), Ignition: &capa.Ignition{ @@ -227,8 +229,11 @@ func TestGenerateMachines(t *testing.T) { fmt.Sprintf("%s-subnet-public-%s", stubClusterID, machineZone), }}}, }, - SSHKeyName: ptr.To(""), - RootVolume: &capa.Volume{Encrypted: ptr.To(true)}, + SSHKeyName: ptr.To(""), + RootVolume: &capa.Volume{ + Encrypted: ptr.To(true), + Throughput: ptr.To(int64(0)), + }, UncompressedUserData: ptr.To(true), Ignition: &capa.Ignition{ StorageType: capa.IgnitionStorageTypeOptionUnencryptedUserData, diff --git a/pkg/asset/machines/aws/machines.go b/pkg/asset/machines/aws/machines.go index b0b21c531a..d126b7d24d 100644 --- a/pkg/asset/machines/aws/machines.go +++ b/pkg/asset/machines/aws/machines.go @@ -239,11 +239,12 @@ func provider(in *machineProviderInput) (*machineapi.AWSMachineProviderConfig, e BlockDevices: []machineapi.BlockDeviceMappingSpec{ { EBS: &machineapi.EBSBlockDeviceSpec{ - VolumeType: pointer.String(in.root.Type), - VolumeSize: pointer.Int64(int64(in.root.Size)), - Iops: pointer.Int64(int64(in.root.IOPS)), - Encrypted: pointer.Bool(true), - KMSKey: machineapi.AWSResourceReference{ARN: pointer.String(in.root.KMSKeyARN)}, + VolumeType: pointer.String(in.root.Type), + VolumeSize: pointer.Int64(int64(in.root.Size)), + Iops: pointer.Int64(int64(in.root.IOPS)), + ThroughputMib: pointer.Int32(int32(in.root.Throughput)), + Encrypted: pointer.Bool(true), + KMSKey: machineapi.AWSResourceReference{ARN: pointer.String(in.root.KMSKeyARN)}, }, }, }, diff --git a/pkg/types/aws/machinepool.go b/pkg/types/aws/machinepool.go index 60ee8d977f..084b36e5f5 100644 --- a/pkg/types/aws/machinepool.go +++ b/pkg/types/aws/machinepool.go @@ -71,6 +71,9 @@ func (a *MachinePool) Set(required *MachinePool) { if required.EC2RootVolume.IOPS != 0 { a.EC2RootVolume.IOPS = required.EC2RootVolume.IOPS } + if required.EC2RootVolume.Throughput != 0 { + a.EC2RootVolume.Throughput = required.EC2RootVolume.Throughput + } if required.EC2RootVolume.Size != 0 { a.EC2RootVolume.Size = required.EC2RootVolume.Size } @@ -107,6 +110,13 @@ type EC2RootVolume struct { // +optional IOPS int `json:"iops"` + // Throughput to provision in MiB/s supported for the volume type. This + // is currently only applicable to volumes of type gp3. + // + // +kubebuilder:validation:Minimum=0 + // +optional + Throughput int64 `json:"throughput"` + // Size defines the size of the volume in gibibytes (GiB). // // +kubebuilder:validation:Minimum=0 diff --git a/pkg/types/aws/validation/machinepool.go b/pkg/types/aws/validation/machinepool.go index b4db532fe5..fac5a0abe7 100644 --- a/pkg/types/aws/validation/machinepool.go +++ b/pkg/types/aws/validation/machinepool.go @@ -46,6 +46,7 @@ func ValidateMachinePool(platform *aws.Platform, p *aws.MachinePool, fldPath *fi if p.EC2RootVolume.Type != "" { allErrs = append(allErrs, validateVolumeSize(p, fldPath)...) allErrs = append(allErrs, validateIOPS(p, fldPath)...) + allErrs = append(allErrs, validateThroughput(p, fldPath)...) } if p.EC2Metadata.Authentication != "" && !validMetadataAuthValues.Has(p.EC2Metadata.Authentication) { @@ -108,6 +109,25 @@ func validateIOPS(p *aws.MachinePool, fldPath *field.Path) field.ErrorList { return allErrs } +func validateThroughput(p *aws.MachinePool, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + volumeType := strings.ToLower(p.EC2RootVolume.Type) + throughput := p.EC2RootVolume.Throughput + + switch volumeType { + case "gp3": + if throughput != 0 && (throughput < 125 || throughput > 2000) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("throughput"), throughput, "throughput must be between 125 MiB/s and 2000 MiB/s")) + } + default: + if throughput != 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("throughput"), throughput, fmt.Sprintf("throughput not supported for type %s", volumeType))) + } + } + + return allErrs +} + // ValidateAMIID check the AMI ID is set for a machine pool. func ValidateAMIID(platform *aws.Platform, p *aws.MachinePool, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{}