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

tests/provider: Add concurrency to sweep (DB instance) #15336

Merged
merged 9 commits into from
Apr 2, 2021
81 changes: 81 additions & 0 deletions aws/aws_sweeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import (
"os"
"testing"

"github.com/hashicorp/aws-sdk-go-base/tfawserr"
multierror "github.com/hashicorp/go-multierror"
"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/envvar"
)

Expand Down Expand Up @@ -52,3 +55,81 @@ func sharedClientForRegion(region string) (interface{}, error) {

return client, nil
}

type testSweepResource struct {
d *schema.ResourceData
meta interface{}
resource *schema.Resource
}

func NewTestSweepResource(resource *schema.Resource, d *schema.ResourceData, meta interface{}) *testSweepResource {
return &testSweepResource{
d: d,
meta: meta,
resource: resource,
}
}

func testSweepResourceOrchestrator(sweepResources []*testSweepResource) error {
var g multierror.Group

for _, sweepResource := range sweepResources {
sweepResource := sweepResource

g.Go(func() error {
return testAccDeleteResource(sweepResource.resource, sweepResource.d, sweepResource.meta)
})
}

return g.Wait().ErrorOrNil()
}

// Check sweeper API call error for reasons to skip sweeping
// These include missing API endpoints and unsupported API calls
func testSweepSkipSweepError(err error) bool {
// Ignore missing API endpoints
if isAWSErr(err, "RequestError", "send request failed") {
return true
}
// Ignore unsupported API calls
if isAWSErr(err, "UnsupportedOperation", "") {
return true
}
// Ignore more unsupported API calls
// InvalidParameterValue: Use of cache security groups is not permitted in this API version for your account.
if isAWSErr(err, "InvalidParameterValue", "not permitted in this API version for your account") {
return true
}
// InvalidParameterValue: Access Denied to API Version: APIGlobalDatabases
if isAWSErr(err, "InvalidParameterValue", "Access Denied to API Version") {
return true
}
// GovCloud has endpoints that respond with (no message provided):
// AccessDeniedException:
// Since acceptance test sweepers are best effort and this response is very common,
// we allow bypassing this error globally instead of individual test sweeper fixes.
if isAWSErr(err, "AccessDeniedException", "") {
return true
}
// Example: BadRequestException: vpc link not supported for region us-gov-west-1
if isAWSErr(err, "BadRequestException", "not supported") {
return true
}
// Example: InvalidAction: The action DescribeTransitGatewayAttachments is not valid for this web service
if isAWSErr(err, "InvalidAction", "is not valid") {
return true
}
// For example from GovCloud SES.SetActiveReceiptRuleSet.
if isAWSErr(err, "InvalidAction", "Unavailable Operation") {
return true
}
return false
}

// Check sweeper API call error for reasons to skip a specific resource
// These include AccessDenied or AccessDeniedException for individual resources, e.g. managed by central IT
func testSweepSkipResourceError(err error) bool {
// Since acceptance test sweepers are best effort, we allow bypassing this error globally
// instead of individual test sweeper fixes.
return tfawserr.ErrCodeContains(err, "AccessDenied")
}
105 changes: 23 additions & 82 deletions aws/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/organizations"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -1005,49 +1004,41 @@ func testAccAwsRegionProviderFunc(region string, providers *[]*schema.Provider)
}
}

func testAccCheckResourceDisappears(provider *schema.Provider, resource *schema.Resource, resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resourceState, ok := s.RootModule().Resources[resourceName]
func testAccDeleteResource(resource *schema.Resource, d *schema.ResourceData, meta interface{}) error {
if resource.DeleteContext != nil || resource.DeleteWithoutTimeout != nil {
var diags diag.Diagnostics

if !ok {
return fmt.Errorf("resource not found: %s", resourceName)
if resource.DeleteContext != nil {
diags = resource.DeleteContext(context.Background(), d, meta)
} else {
diags = resource.DeleteWithoutTimeout(context.Background(), d, meta)
}

if resourceState.Primary.ID == "" {
return fmt.Errorf("resource ID missing: %s", resourceName)
for i := range diags {
if diags[i].Severity == diag.Error {
return fmt.Errorf("error deleting resource: %s", diags[i].Summary)
}
}

if resource.DeleteContext != nil || resource.DeleteWithoutTimeout != nil {
var diags diag.Diagnostics
return nil
}

if resource.DeleteContext != nil {
diags = resource.DeleteContext(context.Background(), resource.Data(resourceState.Primary), provider.Meta())
} else {
diags = resource.DeleteWithoutTimeout(context.Background(), resource.Data(resourceState.Primary), provider.Meta())
}
return resource.Delete(d, meta)
}

for i := range diags {
if diags[i].Severity == diag.Error {
return fmt.Errorf("error deleting resource: %s", diags[i].Summary)
}
}
func testAccCheckResourceDisappears(provider *schema.Provider, resource *schema.Resource, resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resourceState, ok := s.RootModule().Resources[resourceName]

return nil
if !ok {
return fmt.Errorf("resource not found: %s", resourceName)
}

if resource.DeleteWithoutTimeout != nil {
gdavison marked this conversation as resolved.
Show resolved Hide resolved
diags := resource.DeleteWithoutTimeout(context.Background(), resource.Data(resourceState.Primary), provider.Meta())

for i := range diags {
if diags[i].Severity == diag.Error {
return fmt.Errorf("error deleting resource: %s", diags[i].Summary)
}
}

return nil
if resourceState.Primary.ID == "" {
return fmt.Errorf("resource ID missing: %s", resourceName)
}

return resource.Delete(resource.Data(resourceState.Primary), provider.Meta())
return testAccDeleteResource(resource, resource.Data(resourceState.Primary), provider.Meta())
}
}

Expand Down Expand Up @@ -1190,56 +1181,6 @@ func testAccPreCheckSkipError(err error) bool {
return false
}

// Check sweeper API call error for reasons to skip sweeping
// These include missing API endpoints and unsupported API calls
func testSweepSkipSweepError(err error) bool {
// Ignore missing API endpoints
if isAWSErr(err, "RequestError", "send request failed") {
return true
}
// Ignore unsupported API calls
if isAWSErr(err, "UnsupportedOperation", "") {
return true
}
// Ignore more unsupported API calls
// InvalidParameterValue: Use of cache security groups is not permitted in this API version for your account.
if isAWSErr(err, "InvalidParameterValue", "not permitted in this API version for your account") {
return true
}
// InvalidParameterValue: Access Denied to API Version: APIGlobalDatabases
if isAWSErr(err, "InvalidParameterValue", "Access Denied to API Version") {
return true
}
// GovCloud has endpoints that respond with (no message provided):
// AccessDeniedException:
// Since acceptance test sweepers are best effort and this response is very common,
// we allow bypassing this error globally instead of individual test sweeper fixes.
if isAWSErr(err, "AccessDeniedException", "") {
return true
}
// Example: BadRequestException: vpc link not supported for region us-gov-west-1
if isAWSErr(err, "BadRequestException", "not supported") {
return true
}
// Example: InvalidAction: The action DescribeTransitGatewayAttachments is not valid for this web service
if isAWSErr(err, "InvalidAction", "is not valid") {
return true
}
// For example from GovCloud SES.SetActiveReceiptRuleSet.
if isAWSErr(err, "InvalidAction", "Unavailable Operation") {
return true
}
return false
}

// Check sweeper API call error for reasons to skip a specific resource
// These include AccessDenied or AccessDeniedException for individual resources, e.g. managed by central IT
func testSweepSkipResourceError(err error) bool {
// Since acceptance test sweepers are best effort, we allow bypassing this error globally
// instead of individual test sweeper fixes.
return tfawserr.ErrCodeContains(err, "AccessDenied")
}

func TestAccProvider_DefaultTags_EmptyConfigurationBlock(t *testing.T) {
var providers []*schema.Provider

Expand Down
27 changes: 8 additions & 19 deletions aws/resource_aws_db_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"log"
"regexp"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/rds"
Expand All @@ -28,27 +27,17 @@ func testSweepDbInstances(region string) error {
if err != nil {
return fmt.Errorf("error getting client: %s", err)
}

conn := client.(*AWSClient).rdsconn
sweepResources := make([]*testSweepResource, 0)

err = conn.DescribeDBInstancesPages(&rds.DescribeDBInstancesInput{}, func(out *rds.DescribeDBInstancesOutput, lastPage bool) bool {
for _, dbi := range out.DBInstances {
log.Printf("[INFO] Deleting DB instance: %s", *dbi.DBInstanceIdentifier)

_, err := conn.DeleteDBInstance(&rds.DeleteDBInstanceInput{
DBInstanceIdentifier: dbi.DBInstanceIdentifier,
SkipFinalSnapshot: aws.Bool(true),
})
if err != nil {
log.Printf("[ERROR] Failed to delete DB instance %s: %s",
*dbi.DBInstanceIdentifier, err)
continue
}

err = waitUntilAwsDbInstanceIsDeleted(*dbi.DBInstanceIdentifier, conn, 40*time.Minute)
if err != nil {
log.Printf("[ERROR] Failure while waiting for DB instance %s to be deleted: %s",
*dbi.DBInstanceIdentifier, err)
}
r := resourceAwsDbInstance()
d := r.Data(nil)
d.SetId(aws.StringValue(dbi.DBInstanceIdentifier))
d.Set("skip_final_snapshot", true)
sweepResources = append(sweepResources, NewTestSweepResource(r, d, client))
}
return !lastPage
})
Expand All @@ -60,7 +49,7 @@ func testSweepDbInstances(region string) error {
return fmt.Errorf("Error retrieving DB instances: %s", err)
}

return nil
return testSweepResourceOrchestrator(sweepResources)
}

func TestAccAWSDBInstance_basic(t *testing.T) {
Expand Down