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

service/ec2: Additional error handling for VPC Endpoint and VPC Endpoint Service deletion, sweeper fixes for Route Tables, VPC Endpoints, and VPC Endpoint Services #16656

Merged
merged 4 commits into from
Feb 3, 2021
Merged
Changes from 1 commit
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
Next Next commit
service/ec2: Additional error handling for VPC Endpoint and VPC Endpo…
…int Service deletion, sweeper fixes for Route Tables, VPC Endpoints, and VPC Endpoint Services

The `DeleteVpcEndpoints` and `DeleteVpcEndpointServiceConfigurations` APIs will sometimes return failures in an `Unsuccessful` array in the response, instead of a normal error. Previously the resource and sweeper did not account for this type of error response and would timeout on deletion after never reporting underlying issue:

```
2020/12/08 18:43:52 Sweeper Tests ran unsuccessfully:
...
  - aws_vpc_endpoint_service: error waiting for VPC Endpoint Service (vpce-svc-0c300eaebde5aec19) to delete: timeout while waiting for state to become 'Deleted' (last state: 'Available', timeout: 10m0s)
...
  - aws_vpc_endpoint: error waiting for VPC Endpoint (vpce-0395ac1f6cc86b11a) to delete: timeout while waiting for state to become 'deleted' (last state: 'available', timeout: 10m0s)
```

Now the resource will handle this response type, the VPC Endpoint sweepers have been refactored to use the resource deletion function, and the VPC Endpoint sweepers will correctly show the unsuccessful deletions while immediately continuing on to the next item:

```
2020/12/08 20:46:59 Sweeper Tests ran unsuccessfully:
  - aws_vpc_endpoint_service: 1 error occurred:
  * error deleting EC2 VPC Endpoint Service (vpce-svc-0c300eaebde5aec19): error deleting EC2 VPC Endpoint Service (vpce-svc-0c300eaebde5aec19): 1 error occurred:
  * vpce-svc-0c300eaebde5aec19: ExistingVpcEndpointConnections: Service has existing active VPC Endpoint connections!
...
  - aws_vpc_endpoint: 1 error occurred:
  * error deleting EC2 VPC Endpoint (vpce-0395ac1f6cc86b11a): error deleting EC2 VPC Endpoint (vpce-0395ac1f6cc86b11a): 1 error occurred:
  * vpce-0395ac1f6cc86b11a: InvalidParameter: Endpoint must be removed from route table before deletion
```

To fix the underlying cause of these errors, the Route Table sweeper needed to be added as a VPC Endpoint dependency and the Route Table sweeper needed to delete non-local/non-public-IGW routes if the Route Table was the main route table for the VPC (as main Route Tables cannot be deleted):

```
2020/12/08 21:12:50 [DEBUG] Running Sweepers for region (us-west-2):
2020/12/08 21:12:50 [DEBUG] Running Sweeper (aws_route_table) in region (us-west-2)
2020/12/08 21:12:50 [INFO] AWS Auth provider used: "SharedCredentialsProvider"
2020/12/08 21:12:50 [DEBUG] Trying to get account information via sts:GetCallerIdentity
2020/12/08 21:12:50 [DEBUG] Trying to get account information via sts:GetCallerIdentity
2020/12/08 21:12:52 [DEBUG] Deleting EC2 Route Table (rtb-09af9318dcc5ccaf9) Route
2020/12/08 21:12:52 [DEBUG] Sweeper (aws_vpc_endpoint_service) has dependency (aws_vpc_endpoint), running..
2020/12/08 21:12:52 [DEBUG] Sweeper (aws_vpc_endpoint) has dependency (aws_route_table), running..
2020/12/08 21:12:52 [DEBUG] Sweeper (aws_route_table) already ran in region (us-west-2)
2020/12/08 21:12:52 [DEBUG] Running Sweeper (aws_vpc_endpoint) in region (us-west-2)
2020/12/08 21:12:53 [INFO] Deleting EC2 VPC Endpoint: vpce-0395ac1f6cc86b11a
2020/12/08 21:12:53 [DEBUG] Waiting for state to become: [deleted]
2020/12/08 21:12:58 [DEBUG] Reading VPC Endpoint: vpce-0395ac1f6cc86b11a
2020/12/08 21:12:59 [TRACE] Waiting 5s before next try
2020/12/08 21:13:04 [DEBUG] Reading VPC Endpoint: vpce-0395ac1f6cc86b11a
2020/12/08 21:13:04 [TRACE] Waiting 10s before next try
2020/12/08 21:13:14 [DEBUG] Reading VPC Endpoint: vpce-0395ac1f6cc86b11a
2020/12/08 21:13:15 [DEBUG] Running Sweeper (aws_vpc_endpoint_service) in region (us-west-2)
2020/12/08 21:13:15 [INFO] Deleting EC2 VPC Endpoint Service: vpce-svc-0c300eaebde5aec19
2020/12/08 21:13:16 [DEBUG] Waiting for state to become: [Deleted]
2020/12/08 21:13:21 [DEBUG] Reading VPC Endpoint Service Configuration: vpce-svc-0c300eaebde5aec19
2020/12/08 21:13:21 [DEBUG] Sweeper (aws_vpc_endpoint) has dependency (aws_route_table), running..
2020/12/08 21:13:21 [DEBUG] Sweeper (aws_route_table) already ran in region (us-west-2)
2020/12/08 21:13:21 [DEBUG] Sweeper (aws_vpc_endpoint) already ran in region (us-west-2)
2020/12/08 21:13:21 Sweeper Tests ran successfully:
  - aws_vpc_endpoint_service
  - aws_route_table
  - aws_vpc_endpoint
ok    github.com/terraform-providers/terraform-provider-aws/aws 33.689s
```

Output from acceptance testing:

```
--- PASS: TestAccAWSVpcEndpoint_disappears (37.60s)
--- PASS: TestAccAWSVpcEndpoint_gatewayBasic (38.82s)
--- PASS: TestAccAWSVpcEndpoint_gatewayPolicy (72.11s)
--- PASS: TestAccAWSVpcEndpoint_gatewayWithRouteTableAndPolicy (87.14s)
--- PASS: TestAccAWSVpcEndpoint_interfaceBasic (78.17s)
--- PASS: TestAccAWSVpcEndpoint_interfaceNonAWSServiceAcceptOnCreate (276.65s)
--- PASS: TestAccAWSVpcEndpoint_interfaceNonAWSServiceAcceptOnUpdate (333.65s)
--- PASS: TestAccAWSVpcEndpoint_interfaceWithSubnetAndSecurityGroup (448.87s)
--- PASS: TestAccAWSVpcEndpoint_tags (89.87s)
--- PASS: TestAccAWSVpcEndpoint_VpcEndpointType_GatewayLoadBalancer (274.15s)

--- PASS: TestAccAWSVpcEndpointService_AllowedPrincipals (280.60s)
--- PASS: TestAccAWSVpcEndpointService_basic (252.94s)
--- PASS: TestAccAWSVpcEndpointService_disappears (258.46s)
--- PASS: TestAccAWSVpcEndpointService_GatewayLoadBalancerArns (208.91s)
--- PASS: TestAccAWSVpcEndpointService_tags (288.75s)
```

Note: When working with assume role credentials, some of these test configurations can error due to the STS `GetCallerIdentity` ARN:

```
=== CONT  TestAccAWSVpcEndpoint_VpcEndpointType_GatewayLoadBalancer
    resource_aws_vpc_endpoint_test.go:519: Step 1/2 error: Error running apply:
        Error: error adding VPC Endpoint Service permissions: InvalidPrincipal: Invalid Principal: 'arn:aws:sts::--OMITTED--:assumed-role/terraform_team1_dev-admin/--OMITTED--'
          status code: 400, request id: 375c4645-3761-49b1-9758-3c9b5a51c115

=== CONT  TestAccAWSVpcEndpointService_AllowedPrincipals
    resource_aws_vpc_endpoint_service_test.go:125: Step 1/3 error: Error running apply:
        Error: error adding VPC Endpoint Service permissions: InvalidPrincipal: Invalid Principal: 'arn:aws:sts::--OMITTED--:assumed-role/terraform_team1_dev-admin/--OMITTED--'
          status code: 400, request id: f3e9a77f-3c7d-4acc-9127-f931c4ffbb37
```

Will create followup issue for that problem.
bflad committed Dec 9, 2020
commit c0663cf5f4db648c73b2c1d1460be0ee3264e5ce
42 changes: 42 additions & 0 deletions aws/internal/service/ec2/errors.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package ec2

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/go-multierror"
)

const (
ErrCodeInvalidParameterValue = "InvalidParameterValue"
)
@@ -16,6 +24,14 @@ const (
InvalidGroupNotFound = "InvalidGroup.NotFound"
)

const (
ErrCodeInvalidVpcEndpointIdNotFound = "InvalidVpcEndpointId.NotFound"
)

const (
ErrCodeInvalidVpcEndpointServiceIdNotFound = "InvalidVpcEndpointServiceId.NotFound"
)

const (
ErrCodeInvalidVpcPeeringConnectionIDNotFound = "InvalidVpcPeeringConnectionID.NotFound"
)
@@ -24,3 +40,29 @@ const (
InvalidVpnGatewayAttachmentNotFound = "InvalidVpnGatewayAttachment.NotFound"
InvalidVpnGatewayIDNotFound = "InvalidVpnGatewayID.NotFound"
)

func UnsuccessfulItemError(apiObject *ec2.UnsuccessfulItemError) error {
if apiObject == nil {
return nil
}

return fmt.Errorf("%s: %s", aws.StringValue(apiObject.Code), aws.StringValue(apiObject.Message))
}

func UnsuccessfulItemsError(apiObjects []*ec2.UnsuccessfulItem) error {
var errors *multierror.Error

for _, apiObject := range apiObjects {
if apiObject == nil {
continue
}

err := UnsuccessfulItemError(apiObject.Error)

if err != nil {
errors = multierror.Append(errors, fmt.Errorf("%s: %w", aws.StringValue(apiObject.ResourceId), err))
}
}

return errors.ErrorOrNil()
}
79 changes: 70 additions & 9 deletions aws/resource_aws_route_table_test.go
Original file line number Diff line number Diff line change
@@ -4,11 +4,13 @@ import (
"fmt"
"log"
"regexp"
"strings"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
@@ -23,46 +25,105 @@ func init() {

func testSweepRouteTables(region string) error {
client, err := sharedClientForRegion(region)

if err != nil {
return fmt.Errorf("error getting client: %s", err)
return fmt.Errorf("error getting client: %w", err)
}

conn := client.(*AWSClient).ec2conn

var sweeperErrs *multierror.Error

input := &ec2.DescribeRouteTablesInput{}

err = conn.DescribeRouteTablesPages(input, func(page *ec2.DescribeRouteTablesOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, routeTable := range page.RouteTables {
if routeTable == nil {
continue
}

id := aws.StringValue(routeTable.RouteTableId)
isMainRouteTableAssociation := false

for _, routeTableAssociation := range routeTable.Associations {
if routeTableAssociation == nil {
continue
}

if aws.BoolValue(routeTableAssociation.Main) {
isMainRouteTableAssociation = true
break
}

associationID := aws.StringValue(routeTableAssociation.RouteTableAssociationId)

input := &ec2.DisassociateRouteTableInput{
AssociationId: routeTableAssociation.RouteTableAssociationId,
}

log.Printf("[DEBUG] Deleting Route Table Association: %s", input)
log.Printf("[DEBUG] Deleting EC2 Route Table Association: %s", associationID)
_, err := conn.DisassociateRouteTable(input)

if err != nil {
log.Printf("[ERROR] Error deleting Route Table Association (%s): %s", aws.StringValue(routeTableAssociation.RouteTableAssociationId), err)
sweeperErr := fmt.Errorf("error deleting EC2 Route Table (%s) Association (%s): %w", id, associationID, err)
log.Printf("[ERROR] %s", sweeperErr)
sweeperErrs = multierror.Append(sweeperErrs, sweeperErr)
continue
}
}

if isMainRouteTableAssociation {
log.Printf("[DEBUG] Skipping Main Route Table: %s", aws.StringValue(routeTable.RouteTableId))
for _, route := range routeTable.Routes {
if route == nil {
continue
}

if aws.StringValue(route.GatewayId) == "local" {
continue
}

// Prevent deleting default VPC route for Internet Gateway
// which some testing is still reliant on operating correctly
if strings.HasPrefix(aws.StringValue(route.GatewayId), "igw-") && aws.StringValue(route.DestinationCidrBlock) == "0.0.0.0/0" {
continue
}

input := &ec2.DeleteRouteInput{
DestinationCidrBlock: route.DestinationCidrBlock,
DestinationIpv6CidrBlock: route.DestinationIpv6CidrBlock,
RouteTableId: routeTable.RouteTableId,
}

log.Printf("[DEBUG] Deleting EC2 Route Table (%s) Route", id)
_, err := conn.DeleteRoute(input)

if err != nil {
sweeperErr := fmt.Errorf("error deleting EC2 Route Table (%s) Route: %w", id, err)
log.Printf("[ERROR] %s", sweeperErr)
sweeperErrs = multierror.Append(sweeperErrs, sweeperErr)
continue
}
}

continue
}

input := &ec2.DeleteRouteTableInput{
RouteTableId: routeTable.RouteTableId,
}

log.Printf("[DEBUG] Deleting Route Table: %s", input)
log.Printf("[DEBUG] Deleting EC2 Route Table: %s", id)
_, err := conn.DeleteRouteTable(input)

if err != nil {
log.Printf("[ERROR] Error deleting Route Table (%s): %s", aws.StringValue(routeTable.RouteTableId), err)
sweeperErr := fmt.Errorf("error deleting EC2 Route Table (%s): %w", id, err)
log.Printf("[ERROR] %s", sweeperErr)
sweeperErrs = multierror.Append(sweeperErrs, sweeperErr)
continue
}
}

@@ -71,14 +132,14 @@ func testSweepRouteTables(region string) error {

if testSweepSkipSweepError(err) {
log.Printf("[WARN] Skipping EC2 Route Table sweep for %s: %s", region, err)
return nil
return sweeperErrs.ErrorOrNil()
}

if err != nil {
return fmt.Errorf("Error describing Route Tables: %s", err)
sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EC2 Route Tables: %w", err))
}

return nil
return sweeperErrs.ErrorOrNil()
}

func TestAccAWSRouteTable_basic(t *testing.T) {
28 changes: 20 additions & 8 deletions aws/resource_aws_vpc_endpoint.go
Original file line number Diff line number Diff line change
@@ -9,11 +9,13 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"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/structure"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2"
)

const (
@@ -358,20 +360,30 @@ func resourceAwsVpcEndpointUpdate(d *schema.ResourceData, meta interface{}) erro
func resourceAwsVpcEndpointDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

log.Printf("[DEBUG] Deleting VPC Endpoint: %s", d.Id())
_, err := conn.DeleteVpcEndpoints(&ec2.DeleteVpcEndpointsInput{
input := &ec2.DeleteVpcEndpointsInput{
VpcEndpointIds: aws.StringSlice([]string{d.Id()}),
})
}

output, err := conn.DeleteVpcEndpoints(input)

if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidVpcEndpointIdNotFound) {
return nil
}

if err != nil {
if isAWSErr(err, "InvalidVpcEndpointId.NotFound", "") {
log.Printf("[DEBUG] VPC Endpoint %s is already gone", d.Id())
} else {
return fmt.Errorf("Error deleting VPC Endpoint: %s", err)
return fmt.Errorf("error deleting EC2 VPC Endpoint (%s): %w", d.Id(), err)
}

if output != nil && len(output.Unsuccessful) > 0 {
err := tfec2.UnsuccessfulItemsError(output.Unsuccessful)

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

if err := vpcEndpointWaitUntilDeleted(conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil {
return fmt.Errorf("error waiting for VPC Endpoint (%s) to delete: %s", d.Id(), err)
return fmt.Errorf("error waiting for EC2 VPC Endpoint (%s) to delete: %w", d.Id(), err)
}

return nil
28 changes: 20 additions & 8 deletions aws/resource_aws_vpc_endpoint_service.go
Original file line number Diff line number Diff line change
@@ -9,9 +9,11 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2"
)

func resourceAwsVpcEndpointService() *schema.Resource {
@@ -273,20 +275,30 @@ func resourceAwsVpcEndpointServiceUpdate(d *schema.ResourceData, meta interface{
func resourceAwsVpcEndpointServiceDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

log.Printf("[DEBUG] Deleting VPC Endpoint Service: %s", d.Id())
_, err := conn.DeleteVpcEndpointServiceConfigurations(&ec2.DeleteVpcEndpointServiceConfigurationsInput{
input := &ec2.DeleteVpcEndpointServiceConfigurationsInput{
ServiceIds: aws.StringSlice([]string{d.Id()}),
})
}

output, err := conn.DeleteVpcEndpointServiceConfigurations(input)

if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidVpcEndpointServiceIdNotFound) {
return nil
}

if err != nil {
if isAWSErr(err, "InvalidVpcEndpointServiceId.NotFound", "") {
log.Printf("[DEBUG] VPC Endpoint Service %s is already gone", d.Id())
} else {
return fmt.Errorf("Error deleting VPC Endpoint Service: %s", err.Error())
return fmt.Errorf("error deleting EC2 VPC Endpoint Service (%s): %w", d.Id(), err)
}

if output != nil && len(output.Unsuccessful) > 0 {
err := tfec2.UnsuccessfulItemsError(output.Unsuccessful)

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

if err := waitForVpcEndpointServiceDeletion(conn, d.Id()); err != nil {
return fmt.Errorf("Error waiting for VPC Endpoint Service %s to delete: %s", d.Id(), err.Error())
return fmt.Errorf("error waiting for EC2 VPC Endpoint Service (%s) to delete: %w", d.Id(), err)
}

return nil
61 changes: 33 additions & 28 deletions aws/resource_aws_vpc_endpoint_service_test.go
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
@@ -25,58 +26,62 @@ func init() {

func testSweepEc2VpcEndpointServices(region string) error {
client, err := sharedClientForRegion(region)

if err != nil {
return fmt.Errorf("error getting client: %s", err)
return fmt.Errorf("error getting client: %w", err)
}

conn := client.(*AWSClient).ec2conn
input := &ec2.DescribeVpcEndpointServiceConfigurationsInput{}

for {
output, err := conn.DescribeVpcEndpointServiceConfigurations(input)
var sweeperErrs *multierror.Error

if testSweepSkipSweepError(err) {
log.Printf("[WARN] Skipping EC2 VPC Endpoint Service sweep for %s: %s", region, err)
return nil
}
input := &ec2.DescribeVpcEndpointServiceConfigurationsInput{}

if err != nil {
return fmt.Errorf("error retrieving EC2 VPC Endpoint Services: %s", err)
err = conn.DescribeVpcEndpointServiceConfigurationsPages(input, func(page *ec2.DescribeVpcEndpointServiceConfigurationsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, serviceConfiguration := range output.ServiceConfigurations {
for _, serviceConfiguration := range page.ServiceConfigurations {
if serviceConfiguration == nil {
continue
}

if aws.StringValue(serviceConfiguration.ServiceState) == ec2.ServiceStateDeleted {
continue
}

id := aws.StringValue(serviceConfiguration.ServiceId)
input := &ec2.DeleteVpcEndpointServiceConfigurationsInput{
ServiceIds: []*string{serviceConfiguration.ServiceId},
}

log.Printf("[INFO] Deleting EC2 VPC Endpoint Service: %s", id)
_, err := conn.DeleteVpcEndpointServiceConfigurations(input)

if isAWSErr(err, "InvalidVpcEndpointServiceId.NotFound", "") {
continue
}
r := resourceAwsVpcEndpointService()
d := r.Data(nil)
d.SetId(id)

if err != nil {
return fmt.Errorf("error deleting EC2 VPC Endpoint Service (%s): %s", id, err)
}
err := r.Delete(d, client)

if err := waitForVpcEndpointServiceDeletion(conn, id); err != nil {
return fmt.Errorf("error waiting for VPC Endpoint Service (%s) to delete: %s", id, err)
if err != nil {
sweeperErr := fmt.Errorf("error deleting EC2 VPC Endpoint Service (%s): %w", id, err)
log.Printf("[ERROR] %s", sweeperErr)
sweeperErrs = multierror.Append(sweeperErrs, sweeperErr)
continue
}
}

if aws.StringValue(output.NextToken) == "" {
break
}
return !lastPage
})

input.NextToken = output.NextToken
if testSweepSkipSweepError(err) {
log.Printf("[WARN] Skipping EC2 VPC Endpoint Service sweep for %s: %s", region, err)
return sweeperErrs.ErrorOrNil()
}

return nil
if err != nil {
sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EC2 VPC Endpoint Services: %w", err))
}

return sweeperErrs.ErrorOrNil()
}

func TestAccAWSVpcEndpointService_basic(t *testing.T) {
66 changes: 36 additions & 30 deletions aws/resource_aws_vpc_endpoint_test.go
Original file line number Diff line number Diff line change
@@ -7,11 +7,11 @@ import (
"strconv"
"strings"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
@@ -21,64 +21,70 @@ func init() {
resource.AddTestSweepers("aws_vpc_endpoint", &resource.Sweeper{
Name: "aws_vpc_endpoint",
F: testSweepEc2VpcEndpoints,
Dependencies: []string{
"aws_route_table",
},
})
}

func testSweepEc2VpcEndpoints(region string) error {
client, err := sharedClientForRegion(region)

if err != nil {
return fmt.Errorf("error getting client: %s", err)
return fmt.Errorf("error getting client: %w", err)
}

conn := client.(*AWSClient).ec2conn
input := &ec2.DescribeVpcEndpointsInput{}

for {
output, err := conn.DescribeVpcEndpoints(input)
var sweeperErrs *multierror.Error

if testSweepSkipSweepError(err) {
log.Printf("[WARN] Skipping EC2 VPC Endpoint sweep for %s: %s", region, err)
return nil
}
input := &ec2.DescribeVpcEndpointsInput{}

if err != nil {
return fmt.Errorf("error retrieving EC2 VPC Endpoints: %s", err)
err = conn.DescribeVpcEndpointsPages(input, func(page *ec2.DescribeVpcEndpointsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, vpcEndpoint := range output.VpcEndpoints {
for _, vpcEndpoint := range page.VpcEndpoints {
if vpcEndpoint == nil {
continue
}

if aws.StringValue(vpcEndpoint.State) != "available" {
continue
}

id := aws.StringValue(vpcEndpoint.VpcEndpointId)

input := &ec2.DeleteVpcEndpointsInput{
VpcEndpointIds: []*string{aws.String(id)},
}

log.Printf("[INFO] Deleting EC2 VPC Endpoint: %s", id)
_, err := conn.DeleteVpcEndpoints(input)

if isAWSErr(err, "InvalidVpcEndpointId.NotFound", "") {
continue
}
r := resourceAwsVpcEndpoint()
d := r.Data(nil)
d.SetId(id)

if err != nil {
return fmt.Errorf("error deleting EC2 VPC Endpoint (%s): %s", id, err)
}
err := r.Delete(d, client)

if err := vpcEndpointWaitUntilDeleted(conn, id, 10*time.Minute); err != nil {
return fmt.Errorf("error waiting for VPC Endpoint (%s) to delete: %s", id, err)
if err != nil {
sweeperErr := fmt.Errorf("error deleting EC2 VPC Endpoint (%s): %w", id, err)
log.Printf("[ERROR] %s", sweeperErr)
sweeperErrs = multierror.Append(sweeperErrs, sweeperErr)
continue
}
}

if aws.StringValue(output.NextToken) == "" {
break
}
return !lastPage
})

input.NextToken = output.NextToken
if testSweepSkipSweepError(err) {
log.Printf("[WARN] Skipping EC2 VPC Endpoint sweep for %s: %s", region, err)
return sweeperErrs.ErrorOrNil()
}

return nil
if err != nil {
sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EC2 VPC Endpoints: %w", err))
}

return sweeperErrs.ErrorOrNil()
}

func TestAccAWSVpcEndpoint_gatewayBasic(t *testing.T) {