From 0cf9b55c5c6ae886f453468f1b0b8ef17205b6e7 Mon Sep 17 00:00:00 2001 From: Aleksandr Serbin Date: Sat, 31 Oct 2020 13:10:51 +0100 Subject: [PATCH 1/4] amazon-ebs: validate IOPS ratio --- builder/amazon/common/block_device.go | 12 ++++ builder/amazon/common/block_device_test.go | 83 ++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/builder/amazon/common/block_device.go b/builder/amazon/common/block_device.go index 6dc35d719..8b7fe5246 100644 --- a/builder/amazon/common/block_device.go +++ b/builder/amazon/common/block_device.go @@ -154,6 +154,11 @@ func (blockDevice BlockDevice) BuildEC2BlockDeviceMapping() *ec2.BlockDeviceMapp return mapping } +var iopsRatios = map[string]int64{ + "io1": 50, + "io2": 500, +} + func (b *BlockDevice) Prepare(ctx *interpolate.Context) error { if b.DeviceName == "" { return fmt.Errorf("The `device_name` must be specified " + @@ -166,6 +171,13 @@ func (b *BlockDevice) Prepare(ctx *interpolate.Context) error { "true` when setting a kms_key_id.", b.DeviceName) } + if ratio, ok := iopsRatios[b.VolumeType]; b.VolumeSize != 0 && ok { + if b.IOPS/b.VolumeSize > ratio { + return fmt.Errorf("The maximum ratio of provisioned IOPS to requested volume size "+ + "(in GiB) is %v:1 for %s volumes", ratio, b.VolumeType) + } + } + _, err := interpolate.RenderInterface(&b, ctx) return err } diff --git a/builder/amazon/common/block_device_test.go b/builder/amazon/common/block_device_test.go index b434ca02b..d6f9e665a 100644 --- a/builder/amazon/common/block_device_test.go +++ b/builder/amazon/common/block_device_test.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/google/go-cmp/cmp" "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/template/interpolate" ) func TestBlockDevice(t *testing.T) { @@ -182,3 +183,85 @@ func TestBlockDevice(t *testing.T) { } } } + +func TestIOPSValidation(t *testing.T) { + + cases := []struct { + device BlockDevice + ok bool + msg string + }{ + // volume size unknown + { + device: BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io1", + IOPS: 1000, + }, + ok: true, + }, + { + device: BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io2", + IOPS: 1000, + }, + ok: true, + }, + // ratio requirement satisfied + { + device: BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io1", + VolumeSize: 50, + IOPS: 1000, + }, + ok: true, + }, + { + device: BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io2", + VolumeSize: 100, + IOPS: 1000, + }, + ok: true, + }, + // ratio requirement not satisfied + { + device: BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io1", + VolumeSize: 10, + IOPS: 2000, + }, + ok: false, + msg: "The maximum ratio of provisioned IOPS to requested volume size (in GiB) is 50:1 for io1 volumes", + }, + { + device: BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io2", + VolumeSize: 50, + IOPS: 30000, + }, + ok: false, + msg: "The maximum ratio of provisioned IOPS to requested volume size (in GiB) is 500:1 for io2 volumes", + }, + } + + ctx := interpolate.Context{} + for _, testCase := range cases { + err := testCase.device.Prepare(&ctx) + if testCase.ok && err != nil { + t.Fatalf("should not error, but: %v", err) + } + if !testCase.ok { + if err == nil { + t.Fatalf("should error") + } else if err.Error() != testCase.msg { + t.Fatalf("wrong error: expected %s, found: %v", testCase.msg, err) + } + } + } +} From 09c2620c485f08bc518491af525d8c5fe017b067 Mon Sep 17 00:00:00 2001 From: Aleksandr Serbin Date: Wed, 4 Nov 2020 20:29:09 +0100 Subject: [PATCH 2/4] amazon: validate IOPS max and min values --- builder/amazon/common/block_device.go | 14 ++++++++++-- builder/amazon/common/block_device_test.go | 26 ++++++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/builder/amazon/common/block_device.go b/builder/amazon/common/block_device.go index 8b7fe5246..47d683532 100644 --- a/builder/amazon/common/block_device.go +++ b/builder/amazon/common/block_device.go @@ -13,6 +13,11 @@ import ( "github.com/hashicorp/packer/template/interpolate" ) +const ( + minIops = 100 + maxIops = 64000 +) + // These will be attached when launching your instance. Your // options here may vary depending on the type of VM you use. // @@ -173,11 +178,16 @@ func (b *BlockDevice) Prepare(ctx *interpolate.Context) error { if ratio, ok := iopsRatios[b.VolumeType]; b.VolumeSize != 0 && ok { if b.IOPS/b.VolumeSize > ratio { - return fmt.Errorf("The maximum ratio of provisioned IOPS to requested volume size "+ - "(in GiB) is %v:1 for %s volumes", ratio, b.VolumeType) + return fmt.Errorf("%s: the maximum ratio of provisioned IOPS to requested volume size "+ + "(in GiB) is %v:1 for %s volumes", b.DeviceName, ratio, b.VolumeType) } } + if b.IOPS < minIops || b.IOPS > maxIops { + return fmt.Errorf("IOPS must be between %d and %d for device %s", + minIops, maxIops, b.DeviceName) + } + _, err := interpolate.RenderInterface(&b, ctx) return err } diff --git a/builder/amazon/common/block_device_test.go b/builder/amazon/common/block_device_test.go index d6f9e665a..2e909117a 100644 --- a/builder/amazon/common/block_device_test.go +++ b/builder/amazon/common/block_device_test.go @@ -236,7 +236,7 @@ func TestIOPSValidation(t *testing.T) { IOPS: 2000, }, ok: false, - msg: "The maximum ratio of provisioned IOPS to requested volume size (in GiB) is 50:1 for io1 volumes", + msg: "/dev/sdb: the maximum ratio of provisioned IOPS to requested volume size (in GiB) is 50:1 for io1 volumes", }, { device: BlockDevice{ @@ -246,7 +246,29 @@ func TestIOPSValidation(t *testing.T) { IOPS: 30000, }, ok: false, - msg: "The maximum ratio of provisioned IOPS to requested volume size (in GiB) is 500:1 for io2 volumes", + msg: "/dev/sdb: the maximum ratio of provisioned IOPS to requested volume size (in GiB) is 500:1 for io2 volumes", + }, + // exceed max iops + { + device: BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io2", + VolumeSize: 500, + IOPS: 99999, + }, + ok: false, + msg: "IOPS must be between 100 and 64000 for device /dev/sdb", + }, + // lower than min iops + { + device: BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io2", + VolumeSize: 50, + IOPS: 10, + }, + ok: false, + msg: "IOPS must be between 100 and 64000 for device /dev/sdb", }, } From a763c8ab02ec45bd3d1d60ad3f67997e21c27f5c Mon Sep 17 00:00:00 2001 From: Aleksandr Serbin Date: Wed, 4 Nov 2020 20:49:04 +0100 Subject: [PATCH 3/4] amazon: validate IOPS only for io volumes --- builder/amazon/common/block_device.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builder/amazon/common/block_device.go b/builder/amazon/common/block_device.go index 47d683532..48d2a089a 100644 --- a/builder/amazon/common/block_device.go +++ b/builder/amazon/common/block_device.go @@ -181,11 +181,11 @@ func (b *BlockDevice) Prepare(ctx *interpolate.Context) error { return fmt.Errorf("%s: the maximum ratio of provisioned IOPS to requested volume size "+ "(in GiB) is %v:1 for %s volumes", b.DeviceName, ratio, b.VolumeType) } - } - if b.IOPS < minIops || b.IOPS > maxIops { - return fmt.Errorf("IOPS must be between %d and %d for device %s", - minIops, maxIops, b.DeviceName) + if b.IOPS < minIops || b.IOPS > maxIops { + return fmt.Errorf("IOPS must be between %d and %d for device %s", + minIops, maxIops, b.DeviceName) + } } _, err := interpolate.RenderInterface(&b, ctx) From ba1e60ff35bd50ed0f014faf46f4948ae588a9b6 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 20 Nov 2020 15:15:39 -0800 Subject: [PATCH 4/4] Update builder/amazon/common/block_device_test.go --- builder/amazon/common/block_device_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/amazon/common/block_device_test.go b/builder/amazon/common/block_device_test.go index d54ec5cca..c19d76b8e 100644 --- a/builder/amazon/common/block_device_test.go +++ b/builder/amazon/common/block_device_test.go @@ -7,7 +7,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/google/go-cmp/cmp" "github.com/hashicorp/packer/packer-plugin-sdk/template/config" - "github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate" + "github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate" ) func TestBlockDevice(t *testing.T) {