Skip to content

Commit

Permalink
service/ec2: Add throughput attribute to aws_instance resource and da…
Browse files Browse the repository at this point in the history
…ta source (#16620)

Reference: #16514

Output from acceptance testing (test failure unrelated and noted in #16769):

```
--- FAIL: TestAccAWSInstance_instanceProfileChange (317.03s)
--- PASS: TestAccAWSInstance_addSecondaryInterface (165.55s)
--- PASS: TestAccAWSInstance_addSecurityGroupNetworkInterface (159.08s)
--- PASS: TestAccAWSInstance_associatePublic_defaultPrivate (96.96s)
--- PASS: TestAccAWSInstance_associatePublic_defaultPublic (88.29s)
--- PASS: TestAccAWSInstance_associatePublic_explicitPrivate (98.92s)
--- PASS: TestAccAWSInstance_associatePublic_explicitPublic (78.79s)
--- PASS: TestAccAWSInstance_associatePublic_overridePrivate (87.75s)
--- PASS: TestAccAWSInstance_associatePublic_overridePublic (78.38s)
--- PASS: TestAccAWSInstance_associatePublicIPAndPrivateIP (259.61s)
--- PASS: TestAccAWSInstance_atLeastOneOtherEbsVolume (147.90s)
--- PASS: TestAccAWSInstance_basic (76.37s)
--- PASS: TestAccAWSInstance_blockDevices (84.41s)
--- PASS: TestAccAWSInstance_changeInstanceType (374.14s)
--- PASS: TestAccAWSInstance_CreditSpecification_Empty_NonBurstable (332.05s)
--- PASS: TestAccAWSInstance_creditSpecification_isNotAppliedToNonBurstable (122.53s)
--- PASS: TestAccAWSInstance_creditSpecification_standardCpuCredits (125.02s)
--- PASS: TestAccAWSInstance_creditSpecification_standardCpuCredits_t2Tot3Taint (294.22s)
--- PASS: TestAccAWSInstance_creditSpecification_unknownCpuCredits_t2 (108.40s)
--- PASS: TestAccAWSInstance_creditSpecification_unknownCpuCredits_t3 (310.98s)
--- PASS: TestAccAWSInstance_creditSpecification_unlimitedCpuCredits (135.78s)
--- PASS: TestAccAWSInstance_creditSpecification_unlimitedCpuCredits_t2Tot3Taint (288.61s)
--- PASS: TestAccAWSInstance_creditSpecification_unspecifiedDefaultsToStandard (94.79s)
--- PASS: TestAccAWSInstance_CreditSpecification_UnspecifiedToEmpty_NonBurstable (124.98s)
--- PASS: TestAccAWSInstance_creditSpecification_updateCpuCredits (336.93s)
--- PASS: TestAccAWSInstance_creditSpecificationT3_standardCpuCredits (216.49s)
--- PASS: TestAccAWSInstance_creditSpecificationT3_unlimitedCpuCredits (224.03s)
--- PASS: TestAccAWSInstance_creditSpecificationT3_unspecifiedDefaultsToUnlimited (107.79s)
--- PASS: TestAccAWSInstance_creditSpecificationT3_updateCpuCredits (317.85s)
--- PASS: TestAccAWSInstance_dedicatedInstance (126.43s)
--- PASS: TestAccAWSInstance_disableApiTermination (154.23s)
--- PASS: TestAccAWSInstance_disappears (151.57s)
--- PASS: TestAccAWSInstance_EbsBlockDevice_InvalidIopsForVolumeType (11.08s)
--- PASS: TestAccAWSInstance_EbsBlockDevice_InvalidThroughputForVolumeType (11.48s)
--- PASS: TestAccAWSInstance_EbsBlockDevice_KmsKeyArn (72.43s)
--- PASS: TestAccAWSInstance_EbsRootDevice_basic (191.38s)
--- PASS: TestAccAWSInstance_EbsRootDevice_ModifyAll (153.72s)
--- PASS: TestAccAWSInstance_EbsRootDevice_ModifyDeleteOnTermination (173.98s)
--- PASS: TestAccAWSInstance_EbsRootDevice_ModifyIOPS_Io1 (230.44s)
--- PASS: TestAccAWSInstance_EbsRootDevice_ModifyIOPS_Io2 (246.25s)
--- PASS: TestAccAWSInstance_EbsRootDevice_ModifySize (225.70s)
--- PASS: TestAccAWSInstance_EbsRootDevice_ModifyThroughput_Gp3 (221.01s)
--- PASS: TestAccAWSInstance_EbsRootDevice_ModifyType (230.40s)
--- PASS: TestAccAWSInstance_EbsRootDevice_MultipleBlockDevices_ModifyDeleteOnTermination (118.15s)
--- PASS: TestAccAWSInstance_EbsRootDevice_MultipleBlockDevices_ModifySize (138.47s)
--- PASS: TestAccAWSInstance_EbsRootDevice_MultipleDynamicEBSBlockDevices (209.99s)
--- PASS: TestAccAWSInstance_Empty_PrivateIP (247.91s)
--- PASS: TestAccAWSInstance_enclaveOptions (524.76s)
--- PASS: TestAccAWSInstance_forceNewAndTagsDrift (301.26s)
--- PASS: TestAccAWSInstance_getPasswordData_falseToTrue (195.55s)
--- PASS: TestAccAWSInstance_getPasswordData_trueToFalse (284.94s)
--- PASS: TestAccAWSInstance_GP2IopsDevice (86.28s)
--- PASS: TestAccAWSInstance_GP2WithIopsValue (9.66s)
--- PASS: TestAccAWSInstance_hibernation (284.60s)
--- PASS: TestAccAWSInstance_inDefaultVpcBySgId (96.80s)
--- PASS: TestAccAWSInstance_inDefaultVpcBySgName (186.86s)
--- PASS: TestAccAWSInstance_ipv6_supportAddressCount (101.07s)
--- PASS: TestAccAWSInstance_ipv6_supportAddressCountWithIpv4 (143.20s)
--- PASS: TestAccAWSInstance_ipv6AddressCountAndSingleAddressCausesError (13.53s)
--- PASS: TestAccAWSInstance_keyPairCheck (249.89s)
--- PASS: TestAccAWSInstance_metadataOptions (245.64s)
--- PASS: TestAccAWSInstance_NetworkInstanceRemovingAllSecurityGroups (285.45s)
--- PASS: TestAccAWSInstance_NetworkInstanceSecurityGroups (147.15s)
--- PASS: TestAccAWSInstance_NetworkInstanceVPCSecurityGroupIDs (202.59s)
--- PASS: TestAccAWSInstance_NewNetworkInterface_EmptyPrivateIPAndSecondaryPrivateIPs (344.11s)
--- PASS: TestAccAWSInstance_NewNetworkInterface_EmptyPrivateIPAndSecondaryPrivateIPsUpdate (163.40s)
--- PASS: TestAccAWSInstance_NewNetworkInterface_PrivateIPAndSecondaryPrivateIPs (355.32s)
--- PASS: TestAccAWSInstance_NewNetworkInterface_PrivateIPAndSecondaryPrivateIPsUpdate (144.22s)
--- PASS: TestAccAWSInstance_NewNetworkInterface_PublicIPAndSecondaryPrivateIPs (415.89s)
--- PASS: TestAccAWSInstance_noAMIEphemeralDevices (91.80s)
--- PASS: TestAccAWSInstance_placementGroup (117.55s)
--- PASS: TestAccAWSInstance_primaryNetworkInterface (132.58s)
--- PASS: TestAccAWSInstance_primaryNetworkInterfaceSourceDestCheck (244.37s)
--- PASS: TestAccAWSInstance_privateIP (259.77s)
--- PASS: TestAccAWSInstance_RootBlockDevice_KmsKeyArn (99.86s)
--- PASS: TestAccAWSInstance_rootBlockDeviceMismatch (234.60s)
--- PASS: TestAccAWSInstance_rootInstanceStore (118.53s)
--- PASS: TestAccAWSInstance_sourceDestCheck (258.89s)
--- PASS: TestAccAWSInstance_tags (318.09s)
--- PASS: TestAccAWSInstance_UserData_EmptyStringToUnspecified (237.74s)
--- PASS: TestAccAWSInstance_UserData_UnspecifiedToEmptyString (236.79s)
--- PASS: TestAccAWSInstance_userDataBase64 (88.37s)
--- PASS: TestAccAWSInstance_volumeTags (349.07s)
--- PASS: TestAccAWSInstance_volumeTagsComputed (291.98s)
--- PASS: TestAccAWSInstance_withIamInstanceProfile (276.41s)
--- SKIP: TestAccAWSInstance_inEc2Classic (1.59s)
--- SKIP: TestAccAWSInstance_outpost (1.89s)

--- PASS: TestAccAWSInstanceDataSource_AzUserData (149.91s)
--- PASS: TestAccAWSInstanceDataSource_basic (154.74s)
--- PASS: TestAccAWSInstanceDataSource_blockDevices (111.39s)
--- PASS: TestAccAWSInstanceDataSource_creditSpecification (188.40s)
--- PASS: TestAccAWSInstanceDataSource_EbsBlockDevice_KmsKeyId (153.00s)
--- PASS: TestAccAWSInstanceDataSource_enclaveOptions (332.35s)
--- PASS: TestAccAWSInstanceDataSource_getPasswordData_falseToTrue (207.01s)
--- PASS: TestAccAWSInstanceDataSource_getPasswordData_trueToFalse (240.13s)
--- PASS: TestAccAWSInstanceDataSource_GetUserData (204.58s)
--- PASS: TestAccAWSInstanceDataSource_GetUserData_NoUserData (194.61s)
--- PASS: TestAccAWSInstanceDataSource_gp2IopsDevice (393.36s)
--- PASS: TestAccAWSInstanceDataSource_gp3ThroughputDevice (134.01s)
--- PASS: TestAccAWSInstanceDataSource_keyPair (143.35s)
--- PASS: TestAccAWSInstanceDataSource_metadataOptions (311.32s)
--- PASS: TestAccAWSInstanceDataSource_PlacementGroup (358.55s)
--- PASS: TestAccAWSInstanceDataSource_privateIP (150.46s)
--- PASS: TestAccAWSInstanceDataSource_RootBlockDevice_KmsKeyId (163.76s)
--- PASS: TestAccAWSInstanceDataSource_rootInstanceStore (374.57s)
--- PASS: TestAccAWSInstanceDataSource_secondaryPrivateIPs (146.10s)
--- PASS: TestAccAWSInstanceDataSource_SecurityGroups (162.74s)
--- PASS: TestAccAWSInstanceDataSource_tags (161.67s)
--- PASS: TestAccAWSInstanceDataSource_VPC (170.24s)
--- PASS: TestAccAWSInstanceDataSource_VPCSecurityGroups (158.43s)

--- PASS: TestAccAWSInstancesDataSource_basic (320.63s)
--- PASS: TestAccAWSInstancesDataSource_instanceStateNames (321.13s)
--- PASS: TestAccAWSInstancesDataSource_tags (319.82s)
```
  • Loading branch information
rajivshah3 authored Dec 17, 2020
1 parent 3ae747b commit 68ae04c
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 15 deletions.
10 changes: 10 additions & 0 deletions aws/data_source_aws_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ func dataSourceAwsInstance() *schema.Resource {
Computed: true,
},

"throughput": {
Type: schema.TypeInt,
Computed: true,
},

"volume_size": {
Type: schema.TypeInt,
Computed: true,
Expand Down Expand Up @@ -263,6 +268,11 @@ func dataSourceAwsInstance() *schema.Resource {
Computed: true,
},

"throughput": {
Type: schema.TypeInt,
Computed: true,
},

"volume_size": {
Type: schema.TypeInt,
Computed: true,
Expand Down
49 changes: 49 additions & 0 deletions aws/data_source_aws_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,30 @@ func TestAccAWSInstanceDataSource_gp2IopsDevice(t *testing.T) {
})
}

func TestAccAWSInstanceDataSource_gp3ThroughputDevice(t *testing.T) {
resourceName := "aws_instance.test"
datasourceName := "data.aws_instance.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccInstanceDataSourceConfig_gp3ThroughputDevice,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(datasourceName, "ami", resourceName, "ami"),
resource.TestCheckResourceAttrPair(datasourceName, "instance_type", resourceName, "instance_type"),
resource.TestCheckResourceAttrPair(datasourceName, "root_block_device.#", resourceName, "root_block_device.#"),
resource.TestCheckResourceAttrPair(datasourceName, "root_block_device.0.volume_size", resourceName, "root_block_device.0.volume_size"),
resource.TestCheckResourceAttrPair(datasourceName, "root_block_device.0.volume_type", resourceName, "root_block_device.0.volume_type"),
resource.TestCheckResourceAttrPair(datasourceName, "root_block_device.0.device_name", resourceName, "root_block_device.0.device_name"),
resource.TestCheckResourceAttrPair(datasourceName, "root_block_device.0.throughput", resourceName, "root_block_device.0.throughput"),
),
},
},
})
}

func TestAccAWSInstanceDataSource_blockDevices(t *testing.T) {
resourceName := "aws_instance.test"
datasourceName := "data.aws_instance.test"
Expand Down Expand Up @@ -587,6 +611,24 @@ data "aws_instance" "test" {
}
`

// GP3ThroughputDevice
var testAccInstanceDataSourceConfig_gp3ThroughputDevice = testAccLatestAmazonLinuxHvmEbsAmiConfig() + `
resource "aws_instance" "test" {
ami = data.aws_ami.amzn-ami-minimal-hvm-ebs.id
instance_type = "t3.medium"
root_block_device {
volume_type = "gp3"
volume_size = 10
throughput = 300
}
}
data "aws_instance" "test" {
instance_id = aws_instance.test.id
}
`

// Block Device
var testAccInstanceDataSourceConfig_blockDevices = testAccLatestAmazonLinuxHvmEbsAmiConfig() + `
resource "aws_instance" "test" {
Expand Down Expand Up @@ -621,6 +663,13 @@ resource "aws_instance" "test" {
device_name = "/dev/sde"
virtual_name = "ephemeral0"
}
ebs_block_device {
device_name = "/dev/sdf"
volume_size = 10
volume_type = "gp3"
throughput = 300
}
}
data "aws_instance" "test" {
Expand Down
65 changes: 58 additions & 7 deletions aws/resource_aws_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,14 @@ func resourceAwsInstance() *schema.Resource {
ForceNew: true,
},

"throughput": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
DiffSuppressFunc: throughputDiffSuppressFunc,
},

"volume_size": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -496,6 +504,13 @@ func resourceAwsInstance() *schema.Resource {
DiffSuppressFunc: iopsDiffSuppressFunc,
},

"throughput": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
DiffSuppressFunc: throughputDiffSuppressFunc,
},

"volume_size": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -602,11 +617,19 @@ func resourceAwsInstance() *schema.Resource {
}

func iopsDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool {
// Suppress diff if volume_type is not io1 or io2 and iops is unset or configured as 0
// Suppress diff if volume_type is not io1, io2, or gp3 and iops is unset or configured as 0
i := strings.LastIndexByte(k, '.')
vt := k[:i+1] + "volume_type"
v := d.Get(vt).(string)
return (strings.ToLower(v) != ec2.VolumeTypeIo1 || strings.ToLower(v) != ec2.VolumeTypeIo2) && new == "0"
return (strings.ToLower(v) != ec2.VolumeTypeIo1 && strings.ToLower(v) != ec2.VolumeTypeIo2 && strings.ToLower(v) != ec2.VolumeTypeGp3) && new == "0"
}

func throughputDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool {
// Suppress diff if volume_type is not gp3 and throughput is unset or configured as 0
i := strings.LastIndexByte(k, '.')
vt := k[:i+1] + "volume_type"
v := d.Get(vt).(string)
return strings.ToLower(v) != ec2.VolumeTypeGp3 && new == "0"
}

func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
Expand Down Expand Up @@ -1421,7 +1444,7 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
if v, ok := d.Get("root_block_device.0.iops").(int); ok && v != 0 {
// Enforce IOPs usage with a valid volume type
// Reference: https://github.com/hashicorp/terraform-provider-aws/issues/12667
if t, ok := d.Get("root_block_device.0.volume_type").(string); ok && t != ec2.VolumeTypeIo1 && t != ec2.VolumeTypeIo2 {
if t, ok := d.Get("root_block_device.0.volume_type").(string); ok && t != ec2.VolumeTypeIo1 && t != ec2.VolumeTypeIo2 && t != ec2.VolumeTypeGp3 {
if t == "" {
// Volume defaults to gp2
t = ec2.VolumeTypeGp2
Expand All @@ -1432,6 +1455,16 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
input.Iops = aws.Int64(int64(v))
}
}
if d.HasChange("root_block_device.0.throughput") {
if v, ok := d.Get("root_block_device.0.throughput").(int); ok && v != 0 {
// Enforce throughput usage with a valid volume type
if t, ok := d.Get("root_block_device.0.volume_type").(string); ok && t != ec2.VolumeTypeGp3 {
return fmt.Errorf("error updating instance: throughput attribute not supported for type %s", t)
}
modifyVolume = true
input.Throughput = aws.Int64(int64(v))
}
}
if modifyVolume {
_, err := conn.ModifyVolume(&input)
if err != nil {
Expand Down Expand Up @@ -1765,6 +1798,9 @@ func readBlockDevicesFromInstance(instance *ec2.Instance, conn *ec2.EC2) (map[st
if vol.KmsKeyId != nil {
bd["kms_key_id"] = aws.StringValue(vol.KmsKeyId)
}
if vol.Throughput != nil {
bd["throughput"] = aws.Int64Value(vol.Throughput)
}
if instanceBd.DeviceName != nil {
bd["device_name"] = aws.StringValue(instanceBd.DeviceName)
}
Expand Down Expand Up @@ -1953,9 +1989,9 @@ func readBlockDeviceMappingsFromConfig(d *schema.ResourceData, conn *ec2.EC2) ([
if v, ok := bd["volume_type"].(string); ok && v != "" {
ebs.VolumeType = aws.String(v)
if iops, ok := bd["iops"].(int); ok && iops > 0 {
if ec2.VolumeTypeIo1 == strings.ToLower(v) || ec2.VolumeTypeIo2 == strings.ToLower(v) {
if ec2.VolumeTypeIo1 == strings.ToLower(v) || ec2.VolumeTypeIo2 == strings.ToLower(v) || ec2.VolumeTypeGp3 == strings.ToLower(v) {
// Condition: This parameter is required for requests to create io1 or io2
// volumes; it is not used in requests to create gp2, st1, sc1, or
// volumes and optional for gp3; it is not used in requests to create gp2, st1, sc1, or
// standard volumes.
// See: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html
ebs.Iops = aws.Int64(int64(iops))
Expand All @@ -1964,6 +2000,13 @@ func readBlockDeviceMappingsFromConfig(d *schema.ResourceData, conn *ec2.EC2) ([
// Reference: https://github.com/hashicorp/terraform-provider-aws/issues/12667
return nil, fmt.Errorf("error creating resource: iops attribute not supported for ebs_block_device with volume_type %s", v)
}
} else if throughput, ok := bd["throughput"].(int); ok && throughput > 0 {
// `throughput` is only valid for gp3
if ec2.VolumeTypeGp3 == strings.ToLower(v) {
ebs.Throughput = aws.Int64(int64(throughput))
} else {
return nil, fmt.Errorf("error creating resource: throughput attribute not supported for ebs_block_device with volume_type %s", v)
}
}
}

Expand Down Expand Up @@ -2019,8 +2062,8 @@ func readBlockDeviceMappingsFromConfig(d *schema.ResourceData, conn *ec2.EC2) ([
if v, ok := bd["volume_type"].(string); ok && v != "" {
ebs.VolumeType = aws.String(v)
if iops, ok := bd["iops"].(int); ok && iops > 0 {
if ec2.VolumeTypeIo1 == strings.ToLower(v) || ec2.VolumeTypeIo2 == strings.ToLower(v) {
// Only set the iops attribute if the volume type is io1 or io2. Setting otherwise
if ec2.VolumeTypeIo1 == strings.ToLower(v) || ec2.VolumeTypeIo2 == strings.ToLower(v) || ec2.VolumeTypeGp3 == strings.ToLower(v) {
// Only set the iops attribute if the volume type is io1, io2, or gp3. Setting otherwise
// can trigger a refresh/plan loop based on the computed value that is given
// from AWS, and prevent us from specifying 0 as a valid iops.
// See https://github.com/hashicorp/terraform/pull/4146
Expand All @@ -2031,6 +2074,14 @@ func readBlockDeviceMappingsFromConfig(d *schema.ResourceData, conn *ec2.EC2) ([
// Reference: https://github.com/hashicorp/terraform-provider-aws/issues/12667
return nil, fmt.Errorf("error creating resource: iops attribute not supported for root_block_device with volume_type %s", v)
}
} else if throughput, ok := bd["throughput"].(int); ok && throughput > 0 {
// throughput is only valid for gp3
if ec2.VolumeTypeGp3 == strings.ToLower(v) {
ebs.Throughput = aws.Int64(int64(throughput))
} else {
// Enforce throughput usage with a valid volume type
return nil, fmt.Errorf("error creating resource: throughput attribute not supported for root_block_device with volume_type %s", v)
}
}
}

Expand Down
Loading

0 comments on commit 68ae04c

Please sign in to comment.