From deb64e51bd8cde5156474e2d1a1dcd2c9b6d466a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Dec 2020 16:58:44 -0500 Subject: [PATCH 1/7] r/aws_ebs_volume: Add 'throughput' attribute. Acceptance test output: $ make testacc TEST=./aws TESTARGS='-run=TestAccAWSEBSVolume_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEBSVolume_ -timeout 120m === RUN TestAccAWSEBSVolume_basic === PAUSE TestAccAWSEBSVolume_basic === RUN TestAccAWSEBSVolume_updateAttachedEbsVolume === PAUSE TestAccAWSEBSVolume_updateAttachedEbsVolume === RUN TestAccAWSEBSVolume_updateSize === PAUSE TestAccAWSEBSVolume_updateSize === RUN TestAccAWSEBSVolume_updateType === PAUSE TestAccAWSEBSVolume_updateType === RUN TestAccAWSEBSVolume_updateIops_Io1 === PAUSE TestAccAWSEBSVolume_updateIops_Io1 === RUN TestAccAWSEBSVolume_updateIops_Io2 === PAUSE TestAccAWSEBSVolume_updateIops_Io2 === RUN TestAccAWSEBSVolume_kmsKey === PAUSE TestAccAWSEBSVolume_kmsKey === RUN TestAccAWSEBSVolume_NoIops === PAUSE TestAccAWSEBSVolume_NoIops === RUN TestAccAWSEBSVolume_InvalidIopsForType === PAUSE TestAccAWSEBSVolume_InvalidIopsForType === RUN TestAccAWSEBSVolume_withTags === PAUSE TestAccAWSEBSVolume_withTags === RUN TestAccAWSEBSVolume_multiAttach === PAUSE TestAccAWSEBSVolume_multiAttach === RUN TestAccAWSEBSVolume_outpost === PAUSE TestAccAWSEBSVolume_outpost === RUN TestAccAWSEBSVolume_disappears === PAUSE TestAccAWSEBSVolume_disappears === CONT TestAccAWSEBSVolume_basic === CONT TestAccAWSEBSVolume_NoIops === CONT TestAccAWSEBSVolume_disappears === CONT TestAccAWSEBSVolume_outpost === CONT TestAccAWSEBSVolume_multiAttach === CONT TestAccAWSEBSVolume_withTags === CONT TestAccAWSEBSVolume_InvalidIopsForType === CONT TestAccAWSEBSVolume_updateIops_Io1 === CONT TestAccAWSEBSVolume_kmsKey === CONT TestAccAWSEBSVolume_updateIops_Io2 === CONT TestAccAWSEBSVolume_updateSize === CONT TestAccAWSEBSVolume_updateType === CONT TestAccAWSEBSVolume_updateAttachedEbsVolume === CONT TestAccAWSEBSVolume_outpost data_source_aws_outposts_outposts_test.go:66: skipping since no Outposts found --- SKIP: TestAccAWSEBSVolume_outpost (2.30s) --- PASS: TestAccAWSEBSVolume_InvalidIopsForType (17.21s) --- PASS: TestAccAWSEBSVolume_disappears (48.51s) --- PASS: TestAccAWSEBSVolume_multiAttach (54.90s) --- PASS: TestAccAWSEBSVolume_NoIops (55.61s) --- PASS: TestAccAWSEBSVolume_withTags (59.70s) --- PASS: TestAccAWSEBSVolume_basic (59.80s) --- PASS: TestAccAWSEBSVolume_kmsKey (60.16s) --- PASS: TestAccAWSEBSVolume_updateIops_Io2 (86.60s) --- PASS: TestAccAWSEBSVolume_updateIops_Io1 (87.46s) --- PASS: TestAccAWSEBSVolume_updateSize (87.58s) --- PASS: TestAccAWSEBSVolume_updateType (88.66s) --- PASS: TestAccAWSEBSVolume_updateAttachedEbsVolume (187.70s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 187.763s --- aws/resource_aws_ebs_volume.go | 44 +++++++++++++++---------- aws/resource_aws_ebs_volume_test.go | 14 ++++++++ website/docs/r/ebs_volume.html.markdown | 3 +- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/aws/resource_aws_ebs_volume.go b/aws/resource_aws_ebs_volume.go index 439287fada5..4b03b230082 100644 --- a/aws/resource_aws_ebs_volume.go +++ b/aws/resource_aws_ebs_volume.go @@ -11,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) @@ -83,6 +84,11 @@ func resourceAwsEbsVolume() *schema.Resource { Computed: true, }, "tags": tagsSchema(), + "throughput": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(125, 1000), + }, }, } } @@ -112,6 +118,9 @@ func resourceAwsEbsVolumeCreate(d *schema.ResourceData, meta interface{}) error if value, ok := d.GetOk("outpost_arn"); ok { request.OutpostArn = aws.String(value.(string)) } + if value, ok := d.GetOk("throughput"); ok { + request.Throughput = aws.Int64(int64(value.(int))) + } // IOPs are only valid, and required for, storage type io1 and io2. The current minimum // is 100. Hard validation in place to return an error if IOPs are provided @@ -168,27 +177,27 @@ func resourceAwsEbsVolumeCreate(d *schema.ResourceData, meta interface{}) error func resourceAWSEbsVolumeUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - requestUpdate := false - params := &ec2.ModifyVolumeInput{ - VolumeId: aws.String(d.Id()), - } + if d.HasChangesExcept("tags") { + params := &ec2.ModifyVolumeInput{ + VolumeId: aws.String(d.Id()), + } - if d.HasChange("size") { - requestUpdate = true - params.Size = aws.Int64(int64(d.Get("size").(int))) - } + if d.HasChange("size") { + params.Size = aws.Int64(int64(d.Get("size").(int))) + } - if d.HasChange("type") { - requestUpdate = true - params.VolumeType = aws.String(d.Get("type").(string)) - } + if d.HasChange("type") { + params.VolumeType = aws.String(d.Get("type").(string)) + } - if d.HasChange("iops") { - requestUpdate = true - params.Iops = aws.Int64(int64(d.Get("iops").(int))) - } + if d.HasChange("iops") { + params.Iops = aws.Int64(int64(d.Get("iops").(int))) + } + + if d.HasChange("throughput") { + params.Throughput = aws.Int64(int64(d.Get("throughput").(int))) + } - if requestUpdate { result, err := conn.ModifyVolume(params) if err != nil { return err @@ -286,6 +295,7 @@ func resourceAwsEbsVolumeRead(d *schema.ResourceData, meta interface{}) error { d.Set("snapshot_id", aws.StringValue(volume.SnapshotId)) d.Set("outpost_arn", aws.StringValue(volume.OutpostArn)) d.Set("multi_attach_enabled", volume.MultiAttachEnabled) + d.Set("throughput", aws.Int64Value(volume.Throughput)) if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(volume.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return fmt.Errorf("error setting tags: %s", err) diff --git a/aws/resource_aws_ebs_volume_test.go b/aws/resource_aws_ebs_volume_test.go index 36214365714..e72082557dc 100644 --- a/aws/resource_aws_ebs_volume_test.go +++ b/aws/resource_aws_ebs_volume_test.go @@ -89,6 +89,7 @@ func TestAccAWSEBSVolume_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "type", "gp2"), resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { @@ -115,6 +116,7 @@ func TestAccAWSEBSVolume_updateAttachedEbsVolume(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "size", "10"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { @@ -127,6 +129,7 @@ func TestAccAWSEBSVolume_updateAttachedEbsVolume(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "size", "20"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, }, @@ -148,6 +151,7 @@ func TestAccAWSEBSVolume_updateSize(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "size", "1"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { @@ -160,6 +164,7 @@ func TestAccAWSEBSVolume_updateSize(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "size", "10"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, }, @@ -181,6 +186,7 @@ func TestAccAWSEBSVolume_updateType(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "type", "gp2"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { @@ -193,6 +199,7 @@ func TestAccAWSEBSVolume_updateType(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "type", "sc1"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, }, @@ -214,6 +221,7 @@ func TestAccAWSEBSVolume_updateIops_Io1(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "iops", "100"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { @@ -226,6 +234,7 @@ func TestAccAWSEBSVolume_updateIops_Io1(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "iops", "200"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, }, @@ -247,6 +256,7 @@ func TestAccAWSEBSVolume_updateIops_Io2(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "iops", "100"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { @@ -259,6 +269,7 @@ func TestAccAWSEBSVolume_updateIops_Io2(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "iops", "200"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, }, @@ -284,6 +295,7 @@ func TestAccAWSEBSVolume_kmsKey(t *testing.T) { testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "encrypted", "true"), resource.TestCheckResourceAttrPair(resourceName, "kms_key_id", kmsKeyResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { @@ -308,6 +320,7 @@ func TestAccAWSEBSVolume_NoIops(t *testing.T) { Config: testAccAwsEbsVolumeConfigWithNoIops, Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { @@ -378,6 +391,7 @@ func TestAccAWSEBSVolume_multiAttach(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVolumeExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "throughput", "0"), ), }, { diff --git a/website/docs/r/ebs_volume.html.markdown b/website/docs/r/ebs_volume.html.markdown index 4bf9dced640..9607cbe39dd 100644 --- a/website/docs/r/ebs_volume.html.markdown +++ b/website/docs/r/ebs_volume.html.markdown @@ -36,9 +36,10 @@ The following arguments are supported: * `size` - (Optional) The size of the drive in GiBs. * `snapshot_id` (Optional) A snapshot to base the EBS volume off of. * `outpost_arn` - (Optional) The Amazon Resource Name (ARN) of the Outpost. -* `type` - (Optional) The type of EBS volume. Can be "standard", "gp2", "io1", "io2", "sc1" or "st1" (Default: "gp2"). +* `type` - (Optional) The type of EBS volume. Can be `standard`, `gp2`, `gp3`, `io1`, `io2`, `sc1` or `st1` (Default: `gp2`). * `kms_key_id` - (Optional) The ARN for the KMS encryption key. When specifying `kms_key_id`, `encrypted` needs to be set to true. * `tags` - (Optional) A map of tags to assign to the resource. +* `throughput` - (Optional) The throughput that the volume supports, in MiB/s. Only valid for `type` of `gp3`. ~> **NOTE**: When changing the `size`, `iops` or `type` of an instance, there are [considerations](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/considerations.html) to be aware of that Amazon have written about this. From 75fe2fc2db824015ee4b82b05145519facfa7b27 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Dec 2020 17:38:25 -0500 Subject: [PATCH 2/7] r/aws_ebs_volume: Add gp3-specific throughput tests. Acceptance test output: $ make testacc TEST=./aws TESTARGS='-run=TestAccAWSEBSVolume_gp3' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEBSVolume_gp3 -timeout 120m === RUN TestAccAWSEBSVolume_gp3_basic === PAUSE TestAccAWSEBSVolume_gp3_basic === RUN TestAccAWSEBSVolume_gp3_throughput === PAUSE TestAccAWSEBSVolume_gp3_throughput === CONT TestAccAWSEBSVolume_gp3_basic === CONT TestAccAWSEBSVolume_gp3_throughput --- PASS: TestAccAWSEBSVolume_gp3_basic (29.45s) --- PASS: TestAccAWSEBSVolume_gp3_throughput (52.39s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 52.448s --- aws/resource_aws_ebs_volume.go | 17 ++-- aws/resource_aws_ebs_volume_test.go | 127 ++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_ebs_volume.go b/aws/resource_aws_ebs_volume.go index 4b03b230082..2987dc1f776 100644 --- a/aws/resource_aws_ebs_volume.go +++ b/aws/resource_aws_ebs_volume.go @@ -62,15 +62,17 @@ func resourceAwsEbsVolume() *schema.Resource { ForceNew: true, }, "size": { - Type: schema.TypeInt, - Optional: true, - Computed: true, + Type: schema.TypeInt, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"size", "snapshot_id"}, }, "snapshot_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ExactlyOneOf: []string{"size", "snapshot_id"}, }, "outpost_arn": { Type: schema.TypeString, @@ -87,6 +89,7 @@ func resourceAwsEbsVolume() *schema.Resource { "throughput": { Type: schema.TypeInt, Optional: true, + Computed: true, ValidateFunc: validation.IntBetween(125, 1000), }, }, diff --git a/aws/resource_aws_ebs_volume_test.go b/aws/resource_aws_ebs_volume_test.go index e72082557dc..158d2d81561 100644 --- a/aws/resource_aws_ebs_volume_test.go +++ b/aws/resource_aws_ebs_volume_test.go @@ -430,6 +430,100 @@ func TestAccAWSEBSVolume_outpost(t *testing.T) { }) } +func TestAccAWSEBSVolume_gp3_basic(t *testing.T) { + var v ec2.Volume + resourceName := "aws_ebs_volume.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: resourceName, + Providers: testAccProviders, + CheckDestroy: testAccCheckVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEbsVolumeConfigSizeType(rName, 10, "gp3"), + Check: resource.ComposeTestCheckFunc( + testAccCheckVolumeExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`volume/vol-.+`)), + resource.TestCheckResourceAttr(resourceName, "encrypted", "false"), + testCheckResourceAttrGreaterThanValue(resourceName, "iops", "0"), + resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), + resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), + resource.TestCheckResourceAttr(resourceName, "size", "10"), + resource.TestCheckResourceAttr(resourceName, "snapshot_id", ""), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + testCheckResourceAttrGreaterThanValue(resourceName, "throughput", "0"), + resource.TestCheckResourceAttr(resourceName, "type", "gp3"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEBSVolume_gp3_throughput(t *testing.T) { + var v ec2.Volume + resourceName := "aws_ebs_volume.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: resourceName, + Providers: testAccProviders, + CheckDestroy: testAccCheckVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEbsVolumeConfigGp3Throughput(rName, 400), + Check: resource.ComposeTestCheckFunc( + testAccCheckVolumeExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`volume/vol-.+`)), + resource.TestCheckResourceAttr(resourceName, "encrypted", "false"), + testCheckResourceAttrGreaterThanValue(resourceName, "iops", "0"), + resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), + resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), + resource.TestCheckResourceAttr(resourceName, "size", "10"), + resource.TestCheckResourceAttr(resourceName, "snapshot_id", ""), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "throughput", "400"), + resource.TestCheckResourceAttr(resourceName, "type", "gp3"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsEbsVolumeConfigGp3Throughput(rName, 600), + Check: resource.ComposeTestCheckFunc( + testAccCheckVolumeExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`volume/vol-.+`)), + resource.TestCheckResourceAttr(resourceName, "encrypted", "false"), + testCheckResourceAttrGreaterThanValue(resourceName, "iops", "0"), + resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), + resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), + resource.TestCheckResourceAttr(resourceName, "size", "10"), + resource.TestCheckResourceAttr(resourceName, "snapshot_id", ""), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "throughput", "600"), + resource.TestCheckResourceAttr(resourceName, "type", "gp3"), + ), + }, + }, + }) +} + func TestAccAWSEBSVolume_disappears(t *testing.T) { var v ec2.Volume resourceName := "aws_ebs_volume.test" @@ -927,3 +1021,36 @@ resource "aws_ebs_volume" "test" { } `, rName) } + +func testAccAwsEbsVolumeConfigSizeType(rName string, size int, volumeType string) string { + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + fmt.Sprintf(` +resource "aws_ebs_volume" "test" { + availability_zone = data.aws_availability_zones.available.names[0] + type = %[3]q + size = %[2]d + + tags = { + Name = %[1]q + } +} +`, rName, size, volumeType)) +} + +func testAccAwsEbsVolumeConfigGp3Throughput(rName string, throughput int) string { + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + fmt.Sprintf(` +resource "aws_ebs_volume" "test" { + availability_zone = data.aws_availability_zones.available.names[0] + type = "gp3" + size = 10 + throughput = %[2]d + + tags = { + Name = %[1]q + } +} +`, rName, throughput)) +} From 5f4532ad779a254b1db232fd29dc57d309aa0c80 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Dec 2020 13:29:31 -0500 Subject: [PATCH 3/7] r/aws_ebs_volume: Add CustomizeDiff to validate 'iops' vs. 'type'. Acceptance test output: $ make testacc TEST=./aws TESTARGS='-run=TestAccAWSEBSVolume_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEBSVolume_ -timeout 120m === RUN TestAccAWSEBSVolume_basic === PAUSE TestAccAWSEBSVolume_basic === RUN TestAccAWSEBSVolume_updateAttachedEbsVolume === PAUSE TestAccAWSEBSVolume_updateAttachedEbsVolume === RUN TestAccAWSEBSVolume_updateSize === PAUSE TestAccAWSEBSVolume_updateSize === RUN TestAccAWSEBSVolume_updateType === PAUSE TestAccAWSEBSVolume_updateType === RUN TestAccAWSEBSVolume_updateIops_Io1 === PAUSE TestAccAWSEBSVolume_updateIops_Io1 === RUN TestAccAWSEBSVolume_updateIops_Io2 === PAUSE TestAccAWSEBSVolume_updateIops_Io2 === RUN TestAccAWSEBSVolume_kmsKey === PAUSE TestAccAWSEBSVolume_kmsKey === RUN TestAccAWSEBSVolume_NoIops === PAUSE TestAccAWSEBSVolume_NoIops === RUN TestAccAWSEBSVolume_InvalidIopsForType === PAUSE TestAccAWSEBSVolume_InvalidIopsForType === RUN TestAccAWSEBSVolume_withTags === PAUSE TestAccAWSEBSVolume_withTags === RUN TestAccAWSEBSVolume_multiAttach === PAUSE TestAccAWSEBSVolume_multiAttach === RUN TestAccAWSEBSVolume_outpost === PAUSE TestAccAWSEBSVolume_outpost === RUN TestAccAWSEBSVolume_gp3_basic === PAUSE TestAccAWSEBSVolume_gp3_basic === RUN TestAccAWSEBSVolume_gp3_throughput === PAUSE TestAccAWSEBSVolume_gp3_throughput === RUN TestAccAWSEBSVolume_disappears === PAUSE TestAccAWSEBSVolume_disappears === CONT TestAccAWSEBSVolume_basic === CONT TestAccAWSEBSVolume_disappears === CONT TestAccAWSEBSVolume_gp3_throughput === CONT TestAccAWSEBSVolume_gp3_basic === CONT TestAccAWSEBSVolume_outpost === CONT TestAccAWSEBSVolume_multiAttach === CONT TestAccAWSEBSVolume_withTags === CONT TestAccAWSEBSVolume_InvalidIopsForType === CONT TestAccAWSEBSVolume_NoIops === CONT TestAccAWSEBSVolume_kmsKey === CONT TestAccAWSEBSVolume_updateIops_Io2 === CONT TestAccAWSEBSVolume_updateIops_Io1 === CONT TestAccAWSEBSVolume_updateType === CONT TestAccAWSEBSVolume_updateSize === CONT TestAccAWSEBSVolume_updateAttachedEbsVolume === CONT TestAccAWSEBSVolume_outpost data_source_aws_outposts_outposts_test.go:66: skipping since no Outposts found --- SKIP: TestAccAWSEBSVolume_outpost (2.20s) --- PASS: TestAccAWSEBSVolume_InvalidIopsForType (16.56s) --- PASS: TestAccAWSEBSVolume_disappears (51.43s) --- PASS: TestAccAWSEBSVolume_withTags (59.32s) --- PASS: TestAccAWSEBSVolume_NoIops (59.53s) --- PASS: TestAccAWSEBSVolume_basic (59.58s) --- PASS: TestAccAWSEBSVolume_gp3_basic (62.54s) --- PASS: TestAccAWSEBSVolume_multiAttach (62.95s) --- PASS: TestAccAWSEBSVolume_kmsKey (64.01s) --- PASS: TestAccAWSEBSVolume_updateType (90.32s) --- PASS: TestAccAWSEBSVolume_gp3_throughput (91.32s) --- PASS: TestAccAWSEBSVolume_updateIops_Io2 (91.91s) --- PASS: TestAccAWSEBSVolume_updateSize (92.72s) --- PASS: TestAccAWSEBSVolume_updateIops_Io1 (92.86s) --- PASS: TestAccAWSEBSVolume_updateAttachedEbsVolume (189.76s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 189.874s --- aws/resource_aws_ebs_volume.go | 66 ++++++++++++++++--------- aws/resource_aws_ebs_volume_test.go | 2 +- website/docs/r/ebs_volume.html.markdown | 4 +- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/aws/resource_aws_ebs_volume.go b/aws/resource_aws_ebs_volume.go index 2987dc1f776..f8e317cbdef 100644 --- a/aws/resource_aws_ebs_volume.go +++ b/aws/resource_aws_ebs_volume.go @@ -1,6 +1,7 @@ package aws import ( + "context" "fmt" "log" "time" @@ -25,6 +26,8 @@ func resourceAwsEbsVolume() *schema.Resource { State: schema.ImportStatePassthrough, }, + CustomizeDiff: resourceAwsEbsVolumeCustomizeDiff, + Schema: map[string]*schema.Schema{ "arn": { Type: schema.TypeString, @@ -45,9 +48,6 @@ func resourceAwsEbsVolume() *schema.Resource { Type: schema.TypeInt, Optional: true, Computed: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return (d.Get("type").(string) != ec2.VolumeTypeIo1 && new == "0") || (d.Get("type").(string) != ec2.VolumeTypeIo2 && new == "0") - }, }, "kms_key_id": { Type: schema.TypeString, @@ -106,6 +106,9 @@ func resourceAwsEbsVolumeCreate(d *schema.ResourceData, meta interface{}) error if value, ok := d.GetOk("encrypted"); ok { request.Encrypted = aws.Bool(value.(bool)) } + if value, ok := d.GetOk("iops"); ok { + request.Iops = aws.Int64(int64(value.(int))) + } if value, ok := d.GetOk("kms_key_id"); ok { request.KmsKeyId = aws.String(value.(string)) } @@ -124,28 +127,8 @@ func resourceAwsEbsVolumeCreate(d *schema.ResourceData, meta interface{}) error if value, ok := d.GetOk("throughput"); ok { request.Throughput = aws.Int64(int64(value.(int))) } - - // IOPs are only valid, and required for, storage type io1 and io2. The current minimum - // is 100. Hard validation in place to return an error if IOPs are provided - // for an unsupported storage type. - // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/12667 - var t string if value, ok := d.GetOk("type"); ok { - t = value.(string) - request.VolumeType = aws.String(t) - } - - if iops := d.Get("iops").(int); iops > 0 { - if t != ec2.VolumeTypeIo1 && t != ec2.VolumeTypeIo2 { - if t == "" { - // Volume creation would default to gp2 - t = ec2.VolumeTypeGp2 - } - return fmt.Errorf("error creating ebs_volume: iops attribute not supported for type %s", t) - } - // We add the iops value without validating it's size, to allow AWS to - // enforce a size requirement (currently 100) - request.Iops = aws.Int64(int64(iops)) + request.VolumeType = aws.String(value.(string)) } log.Printf("[DEBUG] EBS Volume create opts: %s", request) @@ -386,3 +369,38 @@ func resourceAwsEbsVolumeDelete(d *schema.ResourceData, meta interface{}) error return nil } + +func resourceAwsEbsVolumeCustomizeDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { + iops := diff.Get("iops").(int) + volumeType := diff.Get("type").(string) + + if diff.Id() == "" { + // Create. + + // Iops is required for io1 and io2 volumes. + // The default for gp3 volumes is 3,000 IOPS. + // This parameter is not supported for gp2, st1, sc1, or standard volumes. + // Hard validation in place to return an error if IOPs are provided + // for an unsupported storage type. + // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/12667 + switch volumeType { + case ec2.VolumeTypeIo1, ec2.VolumeTypeIo2: + if iops == 0 { + return fmt.Errorf("'iops' must be set when 'type' is '%s'", volumeType) + } + case ec2.VolumeTypeGp3: + default: + if iops != 0 { + return fmt.Errorf("'iops' must not be set when 'type' is '%s'", volumeType) + } + } + } else { + // Update. + + if diff.HasChange("iops") && volumeType != ec2.VolumeTypeIo1 && volumeType != ec2.VolumeTypeIo2 && iops == 0 { + return diff.Clear("iops") + } + } + + return nil +} diff --git a/aws/resource_aws_ebs_volume_test.go b/aws/resource_aws_ebs_volume_test.go index 158d2d81561..e9abb0b82ee 100644 --- a/aws/resource_aws_ebs_volume_test.go +++ b/aws/resource_aws_ebs_volume_test.go @@ -342,7 +342,7 @@ func TestAccAWSEBSVolume_InvalidIopsForType(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAwsEbsVolumeConfigWithInvalidIopsForType, - ExpectError: regexp.MustCompile(`error creating ebs_volume: iops attribute not supported for type gp2`), + ExpectError: regexp.MustCompile(`'iops' must not be set when 'type' is`), }, }, }) diff --git a/website/docs/r/ebs_volume.html.markdown b/website/docs/r/ebs_volume.html.markdown index 9607cbe39dd..e0c1e716ead 100644 --- a/website/docs/r/ebs_volume.html.markdown +++ b/website/docs/r/ebs_volume.html.markdown @@ -31,7 +31,7 @@ The following arguments are supported: * `availability_zone` - (Required) The AZ where the EBS volume will exist. * `encrypted` - (Optional) If true, the disk will be encrypted. -* `iops` - (Optional) The amount of IOPS to provision for the disk. Only valid for `type` of `io1` or `io2`. +* `iops` - (Optional) The amount of IOPS to provision for the disk. Only valid for `type` of `io1`, `io2` or `gp3`. * `multi_attach_enabled` - (Optional) Specifies whether to enable Amazon EBS Multi-Attach. Multi-Attach is supported exclusively on `io1` volumes. * `size` - (Optional) The size of the drive in GiBs. * `snapshot_id` (Optional) A snapshot to base the EBS volume off of. @@ -41,7 +41,7 @@ The following arguments are supported: * `tags` - (Optional) A map of tags to assign to the resource. * `throughput` - (Optional) The throughput that the volume supports, in MiB/s. Only valid for `type` of `gp3`. -~> **NOTE**: When changing the `size`, `iops` or `type` of an instance, there are [considerations](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/considerations.html) to be aware of that Amazon have written about this. +~> **NOTE**: When changing the `size`, `iops` or `type` of an instance, there are [considerations](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/considerations.html) to be aware of. ## Attributes Reference From 52044eea0af58c19082811b820f4f57511545e8f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Dec 2020 14:31:10 -0500 Subject: [PATCH 4/7] r/aws_ebs_volume: Add test of IOPS for gp3. Acceptance test output: $ make testacc TEST=./aws TESTARGS='-run=TestAccAWSEBSVolume_gp3' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEBSVolume_gp3 -timeout 120m === RUN TestAccAWSEBSVolume_gp3_basic === PAUSE TestAccAWSEBSVolume_gp3_basic === RUN TestAccAWSEBSVolume_gp3_iops === PAUSE TestAccAWSEBSVolume_gp3_iops === RUN TestAccAWSEBSVolume_gp3_throughput === PAUSE TestAccAWSEBSVolume_gp3_throughput === CONT TestAccAWSEBSVolume_gp3_basic === CONT TestAccAWSEBSVolume_gp3_throughput === CONT TestAccAWSEBSVolume_gp3_iops --- PASS: TestAccAWSEBSVolume_gp3_basic (31.18s) --- PASS: TestAccAWSEBSVolume_gp3_iops (55.62s) --- PASS: TestAccAWSEBSVolume_gp3_throughput (55.71s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 55.751s --- aws/resource_aws_ebs_volume.go | 6 ++- aws/resource_aws_ebs_volume_test.go | 82 +++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_ebs_volume.go b/aws/resource_aws_ebs_volume.go index f8e317cbdef..750d0c913b7 100644 --- a/aws/resource_aws_ebs_volume.go +++ b/aws/resource_aws_ebs_volume.go @@ -180,8 +180,10 @@ func resourceAWSEbsVolumeUpdate(d *schema.ResourceData, meta interface{}) error params.Iops = aws.Int64(int64(d.Get("iops").(int))) } - if d.HasChange("throughput") { - params.Throughput = aws.Int64(int64(d.Get("throughput").(int))) + // "If no throughput value is specified, the existing value is retained." + // Not currently correct, so always specify any non-zero throughput value. + if v := d.Get("throughput").(int); v > 0 { + params.Throughput = aws.Int64(int64(v)) } result, err := conn.ModifyVolume(params) diff --git a/aws/resource_aws_ebs_volume_test.go b/aws/resource_aws_ebs_volume_test.go index e9abb0b82ee..0663568fd73 100644 --- a/aws/resource_aws_ebs_volume_test.go +++ b/aws/resource_aws_ebs_volume_test.go @@ -447,7 +447,7 @@ func TestAccAWSEBSVolume_gp3_basic(t *testing.T) { testAccCheckVolumeExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`volume/vol-.+`)), resource.TestCheckResourceAttr(resourceName, "encrypted", "false"), - testCheckResourceAttrGreaterThanValue(resourceName, "iops", "0"), + resource.TestCheckResourceAttr(resourceName, "iops", "3000"), resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), @@ -455,7 +455,7 @@ func TestAccAWSEBSVolume_gp3_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "snapshot_id", ""), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), - testCheckResourceAttrGreaterThanValue(resourceName, "throughput", "0"), + resource.TestCheckResourceAttr(resourceName, "throughput", "125"), resource.TestCheckResourceAttr(resourceName, "type", "gp3"), ), }, @@ -468,6 +468,62 @@ func TestAccAWSEBSVolume_gp3_basic(t *testing.T) { }) } +func TestAccAWSEBSVolume_gp3_iops(t *testing.T) { + var v ec2.Volume + resourceName := "aws_ebs_volume.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: resourceName, + Providers: testAccProviders, + CheckDestroy: testAccCheckVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEbsVolumeConfigGp3Iops(rName, 4000), + Check: resource.ComposeTestCheckFunc( + testAccCheckVolumeExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`volume/vol-.+`)), + resource.TestCheckResourceAttr(resourceName, "encrypted", "false"), + resource.TestCheckResourceAttr(resourceName, "iops", "4000"), + resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), + resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), + resource.TestCheckResourceAttr(resourceName, "size", "10"), + resource.TestCheckResourceAttr(resourceName, "snapshot_id", ""), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "throughput", "200"), + resource.TestCheckResourceAttr(resourceName, "type", "gp3"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsEbsVolumeConfigGp3Iops(rName, 5000), + Check: resource.ComposeTestCheckFunc( + testAccCheckVolumeExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`volume/vol-.+`)), + resource.TestCheckResourceAttr(resourceName, "encrypted", "false"), + resource.TestCheckResourceAttr(resourceName, "iops", "5000"), + resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), + resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), + resource.TestCheckResourceAttr(resourceName, "size", "10"), + resource.TestCheckResourceAttr(resourceName, "snapshot_id", ""), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "throughput", "200"), + resource.TestCheckResourceAttr(resourceName, "type", "gp3"), + ), + }, + }, + }) +} + func TestAccAWSEBSVolume_gp3_throughput(t *testing.T) { var v ec2.Volume resourceName := "aws_ebs_volume.test" @@ -485,7 +541,7 @@ func TestAccAWSEBSVolume_gp3_throughput(t *testing.T) { testAccCheckVolumeExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`volume/vol-.+`)), resource.TestCheckResourceAttr(resourceName, "encrypted", "false"), - testCheckResourceAttrGreaterThanValue(resourceName, "iops", "0"), + resource.TestCheckResourceAttr(resourceName, "iops", "3000"), resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), @@ -508,7 +564,7 @@ func TestAccAWSEBSVolume_gp3_throughput(t *testing.T) { testAccCheckVolumeExists(resourceName, &v), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`volume/vol-.+`)), resource.TestCheckResourceAttr(resourceName, "encrypted", "false"), - testCheckResourceAttrGreaterThanValue(resourceName, "iops", "0"), + resource.TestCheckResourceAttr(resourceName, "iops", "3000"), resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), resource.TestCheckResourceAttr(resourceName, "multi_attach_enabled", "false"), resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), @@ -1054,3 +1110,21 @@ resource "aws_ebs_volume" "test" { } `, rName, throughput)) } + +func testAccAwsEbsVolumeConfigGp3Iops(rName string, iops int) string { + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + fmt.Sprintf(` +resource "aws_ebs_volume" "test" { + availability_zone = data.aws_availability_zones.available.names[0] + type = "gp3" + iops = %[2]d + size = 10 + throughput = 200 + + tags = { + Name = %[1]q + } +} +`, rName, iops)) +} From 09218ce895f0c3a91206d0261313fc9d15d1d76a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Dec 2020 14:42:44 -0500 Subject: [PATCH 5/7] d/aws_ebs_volume: Add 'throughput' attribute. Acceptance test output: $ make testacc TEST=./aws TESTARGS='-run=TestAccAWSEbsVolumeDataSource_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEbsVolumeDataSource_ -timeout 120m === RUN TestAccAWSEbsVolumeDataSource_basic === PAUSE TestAccAWSEbsVolumeDataSource_basic === RUN TestAccAWSEbsVolumeDataSource_multipleFilters === PAUSE TestAccAWSEbsVolumeDataSource_multipleFilters === CONT TestAccAWSEbsVolumeDataSource_basic === CONT TestAccAWSEbsVolumeDataSource_multipleFilters --- PASS: TestAccAWSEbsVolumeDataSource_basic (25.19s) --- PASS: TestAccAWSEbsVolumeDataSource_multipleFilters (26.17s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 26.219s --- aws/data_source_aws_ebs_volume.go | 5 +++++ aws/data_source_aws_ebs_volume_test.go | 1 + website/docs/d/ebs_volume.html.markdown | 1 + 3 files changed, 7 insertions(+) diff --git a/aws/data_source_aws_ebs_volume.go b/aws/data_source_aws_ebs_volume.go index 0ff85a21d2c..ec718a173d5 100644 --- a/aws/data_source_aws_ebs_volume.go +++ b/aws/data_source_aws_ebs_volume.go @@ -67,6 +67,10 @@ func dataSourceAwsEbsVolume() *schema.Resource { Computed: true, }, "tags": tagsSchemaComputed(), + "throughput": { + Type: schema.TypeInt, + Computed: true, + }, }, } } @@ -150,6 +154,7 @@ func volumeDescriptionAttributes(d *schema.ResourceData, client *AWSClient, volu d.Set("volume_type", volume.VolumeType) d.Set("outpost_arn", volume.OutpostArn) d.Set("multi_attach_enabled", volume.MultiAttachEnabled) + d.Set("throughput", volume.Throughput) if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(volume.Tags).IgnoreAws().IgnoreConfig(client.IgnoreTagsConfig).Map()); err != nil { return fmt.Errorf("error setting tags: %s", err) diff --git a/aws/data_source_aws_ebs_volume_test.go b/aws/data_source_aws_ebs_volume_test.go index 6ceaef16bf6..a956929510d 100644 --- a/aws/data_source_aws_ebs_volume_test.go +++ b/aws/data_source_aws_ebs_volume_test.go @@ -25,6 +25,7 @@ func TestAccAWSEbsVolumeDataSource_basic(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "tags", resourceName, "tags"), resource.TestCheckResourceAttrPair(dataSourceName, "outpost_arn", resourceName, "outpost_arn"), resource.TestCheckResourceAttrPair(dataSourceName, "multi_attach_enabled", resourceName, "multi_attach_enabled"), + resource.TestCheckResourceAttrPair(dataSourceName, "throughput", resourceName, "throughput"), ), }, }, diff --git a/website/docs/d/ebs_volume.html.markdown b/website/docs/d/ebs_volume.html.markdown index 0a4d9d5c72b..a3d74495dc7 100644 --- a/website/docs/d/ebs_volume.html.markdown +++ b/website/docs/d/ebs_volume.html.markdown @@ -57,5 +57,6 @@ In addition to all arguments above, the following attributes are exported: * `volume_type` - The type of EBS volume. * `kms_key_id` - The ARN for the KMS encryption key. * `tags` - A map of tags for the resource. +* `throughput` - The throughput that the volume supports, in MiB/s. [1]: http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-volumes.html From 9784e23aeee0a9828a28f099ba81f67ad13e72e4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Dec 2020 14:57:45 -0500 Subject: [PATCH 6/7] r/aws_ebs_volume: Remove calls to 'aws.xxxValue()' in 'd.Set()' (#15893). Acceptance test output: $ make testacc TEST=./aws TESTARGS='-run=TestAccAWSEBSVolume_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEBSVolume_ -timeout 120m === RUN TestAccAWSEBSVolume_basic === PAUSE TestAccAWSEBSVolume_basic === RUN TestAccAWSEBSVolume_updateAttachedEbsVolume === PAUSE TestAccAWSEBSVolume_updateAttachedEbsVolume === RUN TestAccAWSEBSVolume_updateSize === PAUSE TestAccAWSEBSVolume_updateSize === RUN TestAccAWSEBSVolume_updateType === PAUSE TestAccAWSEBSVolume_updateType === RUN TestAccAWSEBSVolume_updateIops_Io1 === PAUSE TestAccAWSEBSVolume_updateIops_Io1 === RUN TestAccAWSEBSVolume_updateIops_Io2 === PAUSE TestAccAWSEBSVolume_updateIops_Io2 === RUN TestAccAWSEBSVolume_kmsKey === PAUSE TestAccAWSEBSVolume_kmsKey === RUN TestAccAWSEBSVolume_NoIops === PAUSE TestAccAWSEBSVolume_NoIops === RUN TestAccAWSEBSVolume_InvalidIopsForType === PAUSE TestAccAWSEBSVolume_InvalidIopsForType === RUN TestAccAWSEBSVolume_withTags === PAUSE TestAccAWSEBSVolume_withTags === RUN TestAccAWSEBSVolume_multiAttach === PAUSE TestAccAWSEBSVolume_multiAttach === RUN TestAccAWSEBSVolume_outpost === PAUSE TestAccAWSEBSVolume_outpost === RUN TestAccAWSEBSVolume_gp3_basic === PAUSE TestAccAWSEBSVolume_gp3_basic === RUN TestAccAWSEBSVolume_gp3_iops === PAUSE TestAccAWSEBSVolume_gp3_iops === RUN TestAccAWSEBSVolume_gp3_throughput === PAUSE TestAccAWSEBSVolume_gp3_throughput === RUN TestAccAWSEBSVolume_disappears === PAUSE TestAccAWSEBSVolume_disappears === CONT TestAccAWSEBSVolume_basic === CONT TestAccAWSEBSVolume_withTags === CONT TestAccAWSEBSVolume_disappears === CONT TestAccAWSEBSVolume_gp3_throughput === CONT TestAccAWSEBSVolume_gp3_iops === CONT TestAccAWSEBSVolume_gp3_basic === CONT TestAccAWSEBSVolume_outpost === CONT TestAccAWSEBSVolume_multiAttach === CONT TestAccAWSEBSVolume_updateIops_Io2 === CONT TestAccAWSEBSVolume_InvalidIopsForType === CONT TestAccAWSEBSVolume_NoIops === CONT TestAccAWSEBSVolume_kmsKey === CONT TestAccAWSEBSVolume_updateType === CONT TestAccAWSEBSVolume_updateIops_Io1 === CONT TestAccAWSEBSVolume_updateSize === CONT TestAccAWSEBSVolume_updateAttachedEbsVolume === CONT TestAccAWSEBSVolume_outpost data_source_aws_outposts_outposts_test.go:66: skipping since no Outposts found --- SKIP: TestAccAWSEBSVolume_outpost (2.13s) --- PASS: TestAccAWSEBSVolume_InvalidIopsForType (17.40s) --- PASS: TestAccAWSEBSVolume_disappears (52.68s) --- PASS: TestAccAWSEBSVolume_NoIops (55.37s) --- PASS: TestAccAWSEBSVolume_gp3_basic (64.85s) --- PASS: TestAccAWSEBSVolume_basic (65.87s) --- PASS: TestAccAWSEBSVolume_multiAttach (66.08s) --- PASS: TestAccAWSEBSVolume_withTags (66.99s) --- PASS: TestAccAWSEBSVolume_kmsKey (67.13s) --- PASS: TestAccAWSEBSVolume_updateIops_Io1 (95.66s) --- PASS: TestAccAWSEBSVolume_gp3_iops (96.68s) --- PASS: TestAccAWSEBSVolume_updateType (98.48s) --- PASS: TestAccAWSEBSVolume_updateIops_Io2 (98.71s) --- PASS: TestAccAWSEBSVolume_gp3_throughput (99.21s) --- PASS: TestAccAWSEBSVolume_updateSize (99.32s) --- PASS: TestAccAWSEBSVolume_updateAttachedEbsVolume (181.29s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 181.371s --- aws/resource_aws_ebs_volume.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_ebs_volume.go b/aws/resource_aws_ebs_volume.go index 750d0c913b7..9cbbad1d572 100644 --- a/aws/resource_aws_ebs_volume.go +++ b/aws/resource_aws_ebs_volume.go @@ -275,21 +275,21 @@ func resourceAwsEbsVolumeRead(d *schema.ResourceData, meta interface{}) error { Service: "ec2", } d.Set("arn", arn.String()) - d.Set("availability_zone", aws.StringValue(volume.AvailabilityZone)) - d.Set("encrypted", aws.BoolValue(volume.Encrypted)) - d.Set("iops", aws.Int64Value(volume.Iops)) - d.Set("kms_key_id", aws.StringValue(volume.KmsKeyId)) - d.Set("size", aws.Int64Value(volume.Size)) - d.Set("snapshot_id", aws.StringValue(volume.SnapshotId)) - d.Set("outpost_arn", aws.StringValue(volume.OutpostArn)) + d.Set("availability_zone", volume.AvailabilityZone) + d.Set("encrypted", volume.Encrypted) + d.Set("iops", volume.Iops) + d.Set("kms_key_id", volume.KmsKeyId) + d.Set("size", volume.Size) + d.Set("snapshot_id", volume.SnapshotId) + d.Set("outpost_arn", volume.OutpostArn) d.Set("multi_attach_enabled", volume.MultiAttachEnabled) - d.Set("throughput", aws.Int64Value(volume.Throughput)) + d.Set("throughput", volume.Throughput) if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(volume.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return fmt.Errorf("error setting tags: %s", err) } - d.Set("type", aws.StringValue(volume.VolumeType)) + d.Set("type", volume.VolumeType) return nil } @@ -390,7 +390,9 @@ func resourceAwsEbsVolumeCustomizeDiff(_ context.Context, diff *schema.ResourceD if iops == 0 { return fmt.Errorf("'iops' must be set when 'type' is '%s'", volumeType) } + case ec2.VolumeTypeGp3: + default: if iops != 0 { return fmt.Errorf("'iops' must not be set when 'type' is '%s'", volumeType) From 1216edc5f7abd73a34d9f18c113694f49e5b64b0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 7 Dec 2020 10:11:32 -0500 Subject: [PATCH 7/7] r/aws_ebs_volume: Throughput is valid only for gp3 volumes. Acceptance test output: $ make testacc TEST=./aws TESTARGS='-run=TestAccAWSEBSVolume_InvalidThroughputForType' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSEBSVolume_InvalidThroughputForType -timeout 120m === RUN TestAccAWSEBSVolume_InvalidThroughputForType === PAUSE TestAccAWSEBSVolume_InvalidThroughputForType === CONT TestAccAWSEBSVolume_InvalidThroughputForType --- PASS: TestAccAWSEBSVolume_InvalidThroughputForType (4.68s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 4.732s --- aws/resource_aws_ebs_volume.go | 13 ++++++++++ aws/resource_aws_ebs_volume_test.go | 38 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/aws/resource_aws_ebs_volume.go b/aws/resource_aws_ebs_volume.go index 9cbbad1d572..1002f1cff7d 100644 --- a/aws/resource_aws_ebs_volume.go +++ b/aws/resource_aws_ebs_volume.go @@ -374,6 +374,8 @@ func resourceAwsEbsVolumeDelete(d *schema.ResourceData, meta interface{}) error func resourceAwsEbsVolumeCustomizeDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { iops := diff.Get("iops").(int) + multiAttachEnabled := diff.Get("multi_attach_enabled").(bool) + throughput := diff.Get("throughput").(int) volumeType := diff.Get("type").(string) if diff.Id() == "" { @@ -398,9 +400,20 @@ func resourceAwsEbsVolumeCustomizeDiff(_ context.Context, diff *schema.ResourceD return fmt.Errorf("'iops' must not be set when 'type' is '%s'", volumeType) } } + + // MultiAttachEnabled is supported with io1 volumes only. + if multiAttachEnabled && volumeType != ec2.VolumeTypeIo1 { + return fmt.Errorf("'multi_attach_enabled' must not be set when 'type' is '%s'", volumeType) + } + + // Throughput is valid only for gp3 volumes. + if throughput > 0 && volumeType != ec2.VolumeTypeGp3 { + return fmt.Errorf("'throughput' must not be set when 'type' is '%s'", volumeType) + } } else { // Update. + // Setting 'iops = 0' is a no-op if the volume type does not require Iops to be specified. if diff.HasChange("iops") && volumeType != ec2.VolumeTypeIo1 && volumeType != ec2.VolumeTypeIo2 && iops == 0 { return diff.Clear("iops") } diff --git a/aws/resource_aws_ebs_volume_test.go b/aws/resource_aws_ebs_volume_test.go index 0663568fd73..8550bbebe5e 100644 --- a/aws/resource_aws_ebs_volume_test.go +++ b/aws/resource_aws_ebs_volume_test.go @@ -348,6 +348,21 @@ func TestAccAWSEBSVolume_InvalidIopsForType(t *testing.T) { }) } +func TestAccAWSEBSVolume_InvalidThroughputForType(t *testing.T) { + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEbsVolumeConfigWithInvalidThroughputForType, + ExpectError: regexp.MustCompile(`'throughput' must not be set when 'type' is`), + }, + }, + }) +} + func TestAccAWSEBSVolume_withTags(t *testing.T) { var v ec2.Volume resourceName := "aws_ebs_volume.test" @@ -1033,6 +1048,29 @@ resource "aws_ebs_volume" "test" { } ` +const testAccAwsEbsVolumeConfigWithInvalidThroughputForType = ` +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +resource "aws_ebs_volume" "test" { + availability_zone = data.aws_availability_zones.available.names[0] + size = 10 + iops = 100 + throughput = 500 + type = "io1" + + tags = { + Name = "TerraformTest" + } +} +` + func testAccAwsEbsVolumeConfigOutpost() string { return ` data "aws_outposts_outposts" "test" {}