Skip to content

Commit

Permalink
Merge pull request #21 from fabi200123/add-ebs-spec
Browse files Browse the repository at this point in the history
Add EBS extra-specs params
  • Loading branch information
gabriel-samfira authored Nov 27, 2024
2 parents 9f168e3 + 228c41e commit b85b7fc
Show file tree
Hide file tree
Showing 4 changed files with 424 additions and 19 deletions.
87 changes: 86 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,33 @@ To this end, this provider supports the following extra specs schema:
"type": "string",
"description": "The name of the Key Pair to use for the instance."
},
"iops": {
"type": "integer",
"description": "Specifies the number of IOPS (Input/Output Operations Per Second) provisioned for the volume. Required for io1 and io2 volumes. Optional for gp3 volumes."
},
"throughput": {
"type": "integer",
"maximum": 1000,
"minimum": 125,
"description": "Specifies the throughput (MiB/s) provisioned for the volume. Valid only for gp3 volumes."
},
"volume_size": {
"type": "integer",
"description": "Specifies the size of the volume in GiB."
},
"volume_type": {
"type": "string",
"enum": [
"gp2",
"gp3",
"io1",
"io2",
"st1",
"sc1",
"standard"
],
"description": "Specifies the EBS volume type."
},
"security_group_ids": {
"type": "array",
"description": "The security groups IDs to associate with the instance. Default: Amazon EC2 uses the default security group.",
Expand Down Expand Up @@ -156,6 +183,10 @@ An example extra specs json would look like this:
{
"subnet_id":"subnet-0e7a29d5cf6e54789",
"ssh_key_name":"Garm-test",
"iops": 3000,
"throughput": 200,
"volume_size": 50,
"volume_type": "gp3",
"security_group_ids": ["sg-018c35963edfb1cce", "sg-018c35963edfb1cee"],
"disable_updates": true,
"enable_boot_debug": true,
Expand Down Expand Up @@ -195,4 +226,58 @@ garm-cli pool update --extra-specs='{"subnet_id":"subnet-0e7a29d5cf6e54789"}' <P
You can also set a spec when creating a new pool, using the same flag.
Workers in that pool will be created taking into account the specs you set on the pool.
Workers in that pool will be created taking into account the specs you set on the pool.
#### Supported Volume Parameters for Garm AWS Provider
**NOTE**: The EBS Volume attached to the runner is configured to be deleted on termination and is set to have the `device name` set as `/dev/sda`.
- `iops`
**Description**: Specifies the number of IOPS (Input/Output Operations Per Second) provisioned for the volume.
**Usage**:
**Required** for `io1` and `io2` volumes.
Optional for `gp3` volumes, with a default of 3,000 IOPS.
Not applicable for `gp2`, `st1`, `sc1`, or `standard` volumes.
**Valid Ranges**:
`gp3`: 3,000 - 16,000 IOPS
`io1`: 100 - 64,000 IOPS
`io2`: 100 - 256,000 IOPS (up to 32,000 IOPS on non-Nitro instances)
**Notes**:
For `gp2`, IOPS represents baseline performance and burst credit accumulation.
- `throughput`
**Description**: Specifies the throughput (MiB/s) for the volume.
**Usage**:
**Valid only for `gp3` volumes**.
Not applicable for `gp2`, `io1`, `io2`, `st1`, `sc1`, or `standard` volumes.
**Valid Range**: 125 - 1,000 MiB/s
- `volume_size`
**Description**: Specifies the size of the volume in GiB.
**Usage**:
Required unless a snapshot ID is provided.
Must be equal to or larger than the snapshot size if specified.
**Valid Ranges by Volume Type**:
`gp2 and gp3`: 1 - 16,384 GiB
`io1`: 4 - 16,384 GiB
`io2`: 4 - 65,536 GiB
`st1 and sc1`: 125 - 16,384 GiB
`standard`: 1 - 1,024 GiB
- `volume_type`
**Description**: Specifies the EBS volume type.
**Supported Values**:
`gp2`: General-purpose SSD with baseline and burstable IOPS.
`gp3`: Next-generation SSD with configurable IOPS and throughput.
`io1`: High-performance SSD for critical workloads, requiring IOPS specification.
`io2`: High-performance SSD with enhanced durability, requiring IOPS specification.
`st1`: Throughput-optimized HDD for large sequential workloads.
`sc1`: Cold HDD for less-frequently accessed workloads.
`standard`: Magnetic storage for infrequent access.
**Default**: `gp2`
**Note:** Ensure your instance type supports the IOPS and throughput configurations specified. For instance types built on the Nitro system, higher IOPS and throughput limits are supported. For more details on volume types and their use cases, refer to the [Amazon EBS User Guide](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-volume-types.html).
12 changes: 12 additions & 0 deletions internal/client/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,18 @@ func (a *AwsCli) CreateRunningInstance(ctx context.Context, spec *spec.RunnerSpe
SecurityGroupIds: spec.SecurityGroupIds,
UserData: aws.String(udata),
KeyName: spec.SSHKeyName,
BlockDeviceMappings: []types.BlockDeviceMapping{
{
DeviceName: aws.String("/dev/sda1"),
Ebs: &types.EbsBlockDevice{
DeleteOnTermination: aws.Bool(true),
Iops: spec.Iops,
Throughput: spec.Throughput,
VolumeSize: spec.VolumeSize,
VolumeType: spec.VolumeType,
},
},
},
TagSpecifications: []types.TagSpecification{
{
ResourceType: types.ResourceTypeInstance,
Expand Down
94 changes: 87 additions & 7 deletions internal/spec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"

"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/cloudbase/garm-provider-aws/config"
"github.com/cloudbase/garm-provider-common/cloudconfig"
"github.com/cloudbase/garm-provider-common/params"
Expand Down Expand Up @@ -73,13 +74,16 @@ func newExtraSpecsFromBootstrapData(data params.BootstrapInstance) (*extraSpecs,
}

type extraSpecs struct {
SubnetID *string `json:"subnet_id,omitempty" jsonschema:"pattern=^subnet-[0-9a-fA-F]{17}$"`
SSHKeyName *string `json:"ssh_key_name,omitempty" jsonschema:"description=The name of the Key Pair to use for the instance."`
SecurityGroupIds []string `json:"security_group_ids,omitempty" jsonschema:"description=The security groups IDs to associate with the instance. Default: Amazon EC2 uses the default security group."`
DisableUpdates *bool `json:"disable_updates,omitempty" jsonschema:"description=Disable automatic updates on the VM."`
EnableBootDebug *bool `json:"enable_boot_debug,omitempty" jsonschema:"description=Enable boot debug on the VM"`
ExtraPackages []string `json:"extra_packages,omitempty" jsonschema:"description=Extra packages to install on the VM"`
// The Cloudconfig struct from common package
SubnetID *string `json:"subnet_id,omitempty" jsonschema:"pattern=^subnet-[0-9a-fA-F]{17}$,description=The ID of the subnet formatted as subnet-xxxxxxxxxxxxxxxxx."`
SSHKeyName *string `json:"ssh_key_name,omitempty" jsonschema:"description=The name of the Key Pair to use for the instance."`
Iops *int32 `json:"iops,omitempty" jsonschema:"description=Specifies the number of IOPS (Input/Output Operations Per Second) provisioned for the volume. Required for io1 and io2 volumes. Optional for gp3 volumes."`
Throughput *int32 `json:"throughput,omitempty" jsonschema:"description=Specifies the throughput (MiB/s) provisioned for the volume. Valid only for gp3 volumes.,minimum=125,maximum=1000"`
VolumeSize *int32 `json:"volume_size,omitempty" jsonschema:"description=Specifies the size of the volume in GiB. Required unless a snapshot ID is provided."`
VolumeType types.VolumeType `json:"volume_type,omitempty" jsonschema:"enum=gp2,enum=gp3,enum=io1,enum=io2,enum=st1,enum=sc1,enum=standard,description=Specifies the EBS volume type."`
SecurityGroupIds []string `json:"security_group_ids,omitempty" jsonschema:"description=The security group IDs to associate with the instance. Default: Amazon EC2 uses the default security group."`
DisableUpdates *bool `json:"disable_updates,omitempty" jsonschema:"description=Disable automatic updates on the VM."`
EnableBootDebug *bool `json:"enable_boot_debug,omitempty" jsonschema:"description=Enable boot debug on the VM."`
ExtraPackages []string `json:"extra_packages,omitempty" jsonschema:"description=Extra packages to install on the VM."`
cloudconfig.CloudConfigSpec
}

Expand Down Expand Up @@ -122,6 +126,10 @@ type RunnerSpec struct {
SecurityGroupIds []string
SubnetID string
SSHKeyName *string
Iops *int32
Throughput *int32
VolumeSize *int32
VolumeType types.VolumeType
ControllerID string
}

Expand All @@ -132,6 +140,62 @@ func (r *RunnerSpec) Validate() error {
if r.BootstrapParams.Name == "" {
return fmt.Errorf("missing bootstrap params")
}
if r.Iops != nil {
switch r.VolumeType {
case types.VolumeTypeIo1:
if *r.Iops < 100 || *r.Iops > 64000 {
return fmt.Errorf("EBS iops for volume type %s must be between 100 and 64000", r.VolumeType)
}
case types.VolumeTypeIo2:
if *r.Iops < 100 || *r.Iops > 256000 {
return fmt.Errorf("EBS iops for volume type %s must be between 100 and 256000", r.VolumeType)
}
case types.VolumeTypeGp3:
if *r.Iops < 3000 || *r.Iops > 16000 {
return fmt.Errorf("EBS iops for volume type %s must be between 3000 and 16000", r.VolumeType)
}
default:
return fmt.Errorf("EBS iops is only valid for volume types io1, io2 and gp3")
}
}
if r.Throughput != nil && r.VolumeType != types.VolumeTypeGp3 {
return fmt.Errorf("EBS throughput is only valid for volume type gp3")
}
if r.VolumeSize != nil {
switch r.VolumeType {
case types.VolumeTypeIo1:
if *r.VolumeSize < 4 || *r.VolumeSize > 16384 {
return fmt.Errorf("EBS volume size for volume type %s must be between 4 and 16384", r.VolumeType)
}
case types.VolumeTypeIo2:
if *r.VolumeSize < 4 || *r.VolumeSize > 16384 {
return fmt.Errorf("EBS volume size for volume type %s must be between 4 and 16384", r.VolumeType)
}
case types.VolumeTypeGp2, types.VolumeTypeGp3:
if *r.VolumeSize < 1 || *r.VolumeSize > 16384 {
return fmt.Errorf("EBS volume size for volume type %s must be between 1 and 16384", r.VolumeType)
}
case types.VolumeTypeSt1, types.VolumeTypeSc1:
if *r.VolumeSize < 125 || *r.VolumeSize > 16384 {
return fmt.Errorf("EBS volume size for volume type %s must be between 125 and 16384", r.VolumeType)
}
case types.VolumeTypeStandard, "":
if *r.VolumeSize < 1 || *r.VolumeSize > 1024 {
return fmt.Errorf("EBS volume size for volume type standard must be between 1 and 1024")
}
default:
return fmt.Errorf("EBS volume size is only valid for volume types io1, io2, gp2, gp3, st1, sc1 and standard")
}
}

if r.VolumeType != "" {
switch r.VolumeType {
case types.VolumeTypeIo1, types.VolumeTypeIo2:
if r.Iops == nil {
return fmt.Errorf("the parameter iops must be specified for %s volumes", r.VolumeType)
}
}
}
return nil
}

Expand All @@ -140,6 +204,22 @@ func (r *RunnerSpec) MergeExtraSpecs(extraSpecs *extraSpecs) {
r.SubnetID = *extraSpecs.SubnetID
}

if extraSpecs.Iops != nil {
r.Iops = extraSpecs.Iops
}

if extraSpecs.Throughput != nil {
r.Throughput = extraSpecs.Throughput
}

if extraSpecs.VolumeSize != nil {
r.VolumeSize = extraSpecs.VolumeSize
}

if extraSpecs.VolumeType != "" {
r.VolumeType = extraSpecs.VolumeType
}

if extraSpecs.SSHKeyName != nil {
r.SSHKeyName = extraSpecs.SSHKeyName
}
Expand Down
Loading

0 comments on commit b85b7fc

Please sign in to comment.