Skip to content

Commit

Permalink
resource/aws_vpc: Handle read-after-create eventual consistency (#18391)
Browse files Browse the repository at this point in the history
* resource/aws_vpc: Handle read-after-create eventual consistency

Reference: #16796

Output from acceptance testing in AWS Commercial:

```
--- PASS: TestAccAWSVpc_AssignGeneratedIpv6CidrBlock (82.88s)
--- PASS: TestAccAWSVpc_basic (41.94s)
--- PASS: TestAccAWSVpc_bothDnsOptionsSet (50.15s)
--- PASS: TestAccAWSVpc_classiclinkDnsSupportOptionSet (42.24s)
--- PASS: TestAccAWSVpc_classiclinkOptionSet (42.26s)
--- PASS: TestAccAWSVpc_coreMismatchedDiffs (29.27s)
--- PASS: TestAccAWSVpc_defaultAndIgnoreTags (62.73s)
--- PASS: TestAccAWSVpc_defaultTags_providerAndResource_duplicateTag (4.83s)
--- PASS: TestAccAWSVpc_defaultTags_providerAndResource_nonOverlappingTag (70.21s)
--- PASS: TestAccAWSVpc_defaultTags_providerAndResource_overlappingTag (69.97s)
--- PASS: TestAccAWSVpc_defaultTags_providerOnly (65.84s)
--- PASS: TestAccAWSVpc_defaultTags_updateToProviderOnly (55.23s)
--- PASS: TestAccAWSVpc_defaultTags_updateToResourceOnly (50.23s)
--- PASS: TestAccAWSVpc_DisabledDnsSupport (49.90s)
--- PASS: TestAccAWSVpc_disappears (22.42s)
--- PASS: TestAccAWSVpc_ignoreTags (64.60s)
--- PASS: TestAccAWSVpc_tags (82.89s)
--- PASS: TestAccAWSVpc_Tenancy (84.83s)
--- PASS: TestAccAWSVpc_update (67.73s)
```

Output from acceptance testing in AWS GovCloud (US):

```
--- PASS: TestAccAWSVpc_AssignGeneratedIpv6CidrBlock (85.77s)
--- PASS: TestAccAWSVpc_basic (36.99s)
--- PASS: TestAccAWSVpc_bothDnsOptionsSet (48.65s)
--- PASS: TestAccAWSVpc_coreMismatchedDiffs (32.11s)
--- PASS: TestAccAWSVpc_defaultAndIgnoreTags (63.25s)
--- PASS: TestAccAWSVpc_defaultTags_providerAndResource_duplicateTag (4.70s)
--- PASS: TestAccAWSVpc_defaultTags_providerAndResource_nonOverlappingTag (59.36s)
--- PASS: TestAccAWSVpc_defaultTags_providerAndResource_overlappingTag (59.09s)
--- PASS: TestAccAWSVpc_defaultTags_providerOnly (60.21s)
--- PASS: TestAccAWSVpc_defaultTags_updateToProviderOnly (46.79s)
--- PASS: TestAccAWSVpc_defaultTags_updateToResourceOnly (48.55s)
--- PASS: TestAccAWSVpc_DisabledDnsSupport (47.71s)
--- PASS: TestAccAWSVpc_disappears (23.64s)
--- PASS: TestAccAWSVpc_ignoreTags (62.21s)
--- PASS: TestAccAWSVpc_tags (82.23s)
--- PASS: TestAccAWSVpc_Tenancy (83.42s)
--- PASS: TestAccAWSVpc_update (66.22s)
--- SKIP: TestAccAWSVpc_classiclinkDnsSupportOptionSet (15.15s)
--- SKIP: TestAccAWSVpc_classiclinkOptionSet (15.15s)
```

* Update CHANGELOG for #18391
  • Loading branch information
bflad committed Mar 25, 2021
1 parent e1ecea3 commit 12912ee
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .changelog/18391.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_vpc: Handle EC2 eventual consistency errors on creation
```
31 changes: 31 additions & 0 deletions aws/internal/service/ec2/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,37 @@ func VpcAttribute(conn *ec2.EC2, vpcID string, attribute string) (*bool, error)
return nil, fmt.Errorf("unimplemented VPC attribute: %s", attribute)
}

// VpcByID looks up a Vpc by ID. When not found, returns nil and potentially an API error.
func VpcByID(conn *ec2.EC2, id string) (*ec2.Vpc, error) {
input := &ec2.DescribeVpcsInput{
VpcIds: aws.StringSlice([]string{id}),
}

output, err := conn.DescribeVpcs(input)

if err != nil {
return nil, err
}

if output == nil {
return nil, nil
}

for _, vpc := range output.Vpcs {
if vpc == nil {
continue
}

if aws.StringValue(vpc.VpcId) != id {
continue
}

return vpc, nil
}

return nil, nil
}

// VpcPeeringConnectionByID returns the VPC peering connection corresponding to the specified identifier.
// Returns nil and potentially an error if no VPC peering connection is found.
func VpcPeeringConnectionByID(conn *ec2.EC2, id string) (*ec2.VpcPeeringConnection, error) {
Expand Down
1 change: 1 addition & 0 deletions aws/internal/service/ec2/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ func TransitGatewayPrefixListReferenceStateUpdated(conn *ec2.EC2, transitGateway
}

const (
VpcPropagationTimeout = 2 * time.Minute
VpcAttributePropagationTimeout = 5 * time.Minute
)

Expand Down
50 changes: 44 additions & 6 deletions aws/resource_aws_vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import (
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"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"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/waiter"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func resourceAwsVpc() *schema.Resource {
Expand Down Expand Up @@ -250,18 +252,54 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error {
defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

// Refresh the VPC state
vpcRaw, _, err := VPCStateRefreshFunc(conn, d.Id())()
var vpc *ec2.Vpc

err := resource.Retry(waiter.VpcPropagationTimeout, func() *resource.RetryError {
var err error

vpc, err = finder.VpcByID(conn, d.Id())

if d.IsNewResource() && tfawserr.ErrCodeEquals(err, "InvalidVpcID.NotFound") {
return resource.RetryableError(err)
}

if err != nil {
return resource.NonRetryableError(err)
}

if d.IsNewResource() && vpc == nil {
return resource.RetryableError(&resource.NotFoundError{
LastError: fmt.Errorf("EC2 VPC (%s) not found", d.Id()),
})
}

return nil
})

if tfresource.TimedOut(err) {
vpc, err = finder.VpcByID(conn, d.Id())
}

if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, "InvalidVpcID.NotFound") {
log.Printf("[WARN] EC2 VPC (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return err
return fmt.Errorf("error reading EC2 VPC (%s): %w", d.Id(), err)
}
if vpcRaw == nil {

if vpc == nil {
if d.IsNewResource() {
return fmt.Errorf("error reading EC2 VPC (%s): not found after creation", d.Id())
}

log.Printf("[WARN] EC2 VPC (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

// VPC stuff
vpc := vpcRaw.(*ec2.Vpc)
vpcid := d.Id()
d.Set("cidr_block", vpc.CidrBlock)
d.Set("dhcp_options_id", vpc.DhcpOptionsId)
Expand Down

0 comments on commit 12912ee

Please sign in to comment.