Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to automatically upgrade too small volumes to the minimum size #2273

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
1 change: 1 addition & 0 deletions docs/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The AWS EBS CSI Driver supports [tagging](tagging.md) through `StorageClass.para
| "type" | io1, io2, gp2, gp3, sc1, st1, standard, sbp1, sbg1 | gp3* | EBS volume type. |
| "iopsPerGB" | | | I/O operations per second per GiB. Can be specified for IO1, IO2, and GP3 volumes. |
| "allowAutoIOPSPerGBIncrease" | true, false | false | When `"true"`, the CSI driver increases IOPS for a volume when `iopsPerGB * <volume size>` is too low to fit into IOPS range supported by AWS. This allows dynamic provisioning to always succeed, even when user specifies too small PVC capacity or `iopsPerGB` value. On the other hand, it may introduce additional costs, as such volumes have higher IOPS than requested in `iopsPerGB`. |
| "allowAutoVolumeSizeIncrease" | true, false | false | When `"true"`, the CSI driver adjust volume size when it is too low to fit into Volume size range supported by AWS. This allows dynamic provisioning to always succeed, even when user specifies too small PVC size. On the other hand, it may introduce additional costs, as such volumes have higher size than requested. |
| "iops" | | | I/O operations per second. Can be specified for IO1, IO2, and GP3 volumes. |
| "throughput" | | 125 | Throughput in MiB/s. Only effective when gp3 volume type is specified. If empty, it will set to 125MiB/s as documented [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html). |
| "encrypted" | true, false | false | Whether the volume should be encrypted or not. Valid values are "true" or "false". |
Expand Down
80 changes: 67 additions & 13 deletions pkg/cloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,23 @@ const (
io1MinTotalIOPS = 100
io1MaxTotalIOPS = 64000
io1MaxIOPSPerGB = 50
io1MinSize = 4
io1MaxSize = 16 * 1024 // 16 TiB
io2MinTotalIOPS = 100
io2MaxTotalIOPS = 64000
io2BlockExpressMaxTotalIOPS = 256000
io2MaxIOPSPerGB = 500
io2MinSize = 4
io2MaxSize = 64 * 1024 // 64 TiB
gp3MaxTotalIOPS = 16000
gp3MinTotalIOPS = 3000
gp3MaxIOPSPerGB = 500
gpMinSize = 1
gpMaxSize = 16 * 1024 // 16 TiB
hddMinSize = 125
hddMaxSize = 16 * 1024 // 16 TiB
standardMinSize = 1
standardMaxSize = 1024
)

var (
Expand Down Expand Up @@ -204,18 +214,19 @@ type Disk struct {

// DiskOptions represents parameters to create an EBS volume.
type DiskOptions struct {
CapacityBytes int64
Tags map[string]string
VolumeType string
IOPSPerGB int32
AllowIOPSPerGBIncrease bool
IOPS int32
Throughput int32
AvailabilityZone string
OutpostArn string
Encrypted bool
BlockExpress bool
MultiAttachEnabled bool
CapacityBytes int64
Tags map[string]string
VolumeType string
IOPSPerGB int32
AllowIOPSPerGBIncrease bool
AllowVolumeSizeIncrease bool
IOPS int32
Throughput int32
AvailabilityZone string
OutpostArn string
Encrypted bool
BlockExpress bool
MultiAttachEnabled bool
// KmsKeyID represents a fully qualified resource name to the key to use for encryption.
// example: arn:aws:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef
KmsKeyID string
Expand Down Expand Up @@ -533,6 +544,8 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
minIops int32
maxIopsPerGb int32
requestedIops int32
minVolumeSize int32
maxVolumeSize int32
)

capacityGiB := util.BytesToGiB(diskOptions.CapacityBytes)
Expand All @@ -548,11 +561,22 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
}

switch createType {
case VolumeTypeGP2, VolumeTypeSC1, VolumeTypeST1, VolumeTypeSBG1, VolumeTypeSBP1, VolumeTypeStandard:
case VolumeTypeSBG1, VolumeTypeSBP1:
case VolumeTypeSC1, VolumeTypeST1:
minVolumeSize = hddMinSize
maxVolumeSize = hddMaxSize
case VolumeTypeStandard:
minVolumeSize = standardMinSize
maxVolumeSize = standardMaxSize
case VolumeTypeGP2:
minVolumeSize = gpMinSize
maxVolumeSize = gpMaxSize
case VolumeTypeIO1:
maxIops = io1MaxTotalIOPS
minIops = io1MinTotalIOPS
maxIopsPerGb = io1MaxIOPSPerGB
minVolumeSize = io1MinSize
maxVolumeSize = io1MaxSize
case VolumeTypeIO2:
if diskOptions.BlockExpress {
maxIops = io2BlockExpressMaxTotalIOPS
Expand All @@ -561,11 +585,15 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
}
minIops = io2MinTotalIOPS
maxIopsPerGb = io2MaxIOPSPerGB
minVolumeSize = io2MinSize
maxVolumeSize = io2MaxSize
case VolumeTypeGP3:
maxIops = gp3MaxTotalIOPS
minIops = gp3MinTotalIOPS
maxIopsPerGb = gp3MaxIOPSPerGB
throughput = diskOptions.Throughput
minVolumeSize = gpMinSize
maxVolumeSize = gpMaxSize
default:
return nil, fmt.Errorf("invalid AWS VolumeType %q", diskOptions.VolumeType)
}
Expand All @@ -574,6 +602,10 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
return nil, errors.New("CreateDisk: multi-attach is only supported for io2 volumes")
}

if maxVolumeSize > 0 {
capacityGiB = capVolumeSize(createType, capacityGiB, minVolumeSize, maxVolumeSize, diskOptions.AllowVolumeSizeIncrease)
}

if maxIops > 0 {
if diskOptions.IOPS > 0 {
requestedIops = diskOptions.IOPS
Expand Down Expand Up @@ -1946,3 +1978,25 @@ func capIOPS(volumeType string, requestedCapacityGiB int32, requestedIops int32,
}
return iops
}

// Calculate actual Volume Size for a volume and cap it at supported AWS limits.
func capVolumeSize(volumeType string, requestedCapacityGiB int32, minSize, maxSize int32, allowIncrease bool) int32 {
// If requestedCapacityGiB is zero the user did not request a specific amount, and the default will be used instead
if requestedCapacityGiB == 0 {
return 0
}

size := requestedCapacityGiB

if size < minSize {
if allowIncrease {
size = minSize
klog.V(5).InfoS("[Debug] Increased Volume Size to the min supported limit", "volumeType", volumeType, "requestedCapacityGiB", requestedCapacityGiB, "limit", minSize)
}
}
if size > maxSize {
size = maxSize
klog.V(5).InfoS("[Debug] Capped Volume Size, volume at the max supported limit", "volumeType", volumeType, "requestedCapacityGiB", requestedCapacityGiB, "limit", maxSize)
}
return size
}
202 changes: 202 additions & 0 deletions pkg/cloud/cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,208 @@ func TestCreateDisk(t *testing.T) {
},
expErr: nil,
},
{
name: "success: io1 with too low capacity and AllowVolumeSizeIncrease",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.GiBToBytes(1),
Tags: map[string]string{VolumeNameTagKey: "vol-test", AwsEbsDriverTagKey: "true"},
VolumeType: VolumeTypeIO1,
AllowVolumeSizeIncrease: true,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: 1,
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(4),
},
expErr: nil,
},
{
name: "success: io1 with too high capacity",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.TiBToBytes(17),
Tags: map[string]string{VolumeNameTagKey: "vol-test"},
VolumeType: VolumeTypeIO1,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: util.TiBToGiB(17),
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(util.TiBToGiB(16)),
},
expErr: nil,
},
{
name: "success: io2 with too low capacity and AllowVolumeSizeIncrease",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.GiBToBytes(1),
Tags: map[string]string{VolumeNameTagKey: "vol-test", AwsEbsDriverTagKey: "true"},
VolumeType: VolumeTypeIO2,
AllowVolumeSizeIncrease: true,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: 1,
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(4),
},
expErr: nil,
},
{
name: "success: io2 with too high capacity",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.TiBToBytes(65),
Tags: map[string]string{VolumeNameTagKey: "vol-test"},
VolumeType: VolumeTypeIO2,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: util.TiBToGiB(65),
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(util.TiBToGiB(64)),
},
expErr: nil,
},
{
name: "success: gp2 with too high capacity",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.TiBToBytes(17),
Tags: map[string]string{VolumeNameTagKey: "vol-test"},
VolumeType: VolumeTypeGP2,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: util.TiBToGiB(17),
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(util.TiBToGiB(16)),
},
expErr: nil,
},
{
name: "success: gp3 with too high capacity",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.TiBToBytes(17),
Tags: map[string]string{VolumeNameTagKey: "vol-test"},
VolumeType: VolumeTypeGP3,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: util.TiBToGiB(17),
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(util.TiBToGiB(16)),
},
expErr: nil,
},
{
name: "success: st1 with too low capacity and AllowVolumeSizeIncrease",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.GiBToBytes(100),
Tags: map[string]string{VolumeNameTagKey: "vol-test", AwsEbsDriverTagKey: "true"},
VolumeType: VolumeTypeST1,
AllowVolumeSizeIncrease: true,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: 100,
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(125),
},
expErr: nil,
},
{
name: "success: st1 with too high capacity",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.TiBToBytes(17),
Tags: map[string]string{VolumeNameTagKey: "vol-test"},
VolumeType: VolumeTypeST1,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: util.TiBToGiB(17),
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(util.TiBToGiB(16)),
},
expErr: nil,
},
{
name: "success: sc1 with too low capacity and AllowVolumeSizeIncrease",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.GiBToBytes(100),
Tags: map[string]string{VolumeNameTagKey: "vol-test", AwsEbsDriverTagKey: "true"},
VolumeType: VolumeTypeSC1,
AllowVolumeSizeIncrease: true,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: 100,
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(125),
},
expErr: nil,
},
{
name: "success: sc1 with too high capacity",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.TiBToBytes(17),
Tags: map[string]string{VolumeNameTagKey: "vol-test"},
VolumeType: VolumeTypeSC1,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: util.TiBToGiB(17),
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(util.TiBToGiB(16)),
},
expErr: nil,
},
{
name: "success: standard with too high capacity",
volumeName: "vol-test-name",
diskOptions: &DiskOptions{
CapacityBytes: util.TiBToBytes(2),
Tags: map[string]string{VolumeNameTagKey: "vol-test"},
VolumeType: VolumeTypeStandard,
},
expDisk: &Disk{
VolumeID: "vol-test",
CapacityGiB: util.TiBToGiB(2),
AvailabilityZone: defaultZone,
},
expCreateVolumeInput: &ec2.CreateVolumeInput{
Size: aws.Int32(util.TiBToGiB(1)),
},
expErr: nil,
},
{
name: "success: create volume when zone is snow and add tags",
volumeName: "vol-test-name",
Expand Down
3 changes: 3 additions & 0 deletions pkg/driver/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ const (
// AllowAutoIOPSPerGBIncreaseKey represents key for allowing automatic increase of IOPS.
AllowAutoIOPSPerGBIncreaseKey = "allowautoiopspergbincrease"

// AllowAutoVolumeSizeIncreaseKey represents key for allowing automatic increase of Volume size.
AllowAutoVolumeSizeIncreaseKey = "allowautovolumesizeincrease"

// Iops represents key for IOPS for volume.
IopsKey = "iops"

Expand Down
Loading