From 7f01d1d4087012c6d81654419ef4dda2cb4e5114 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 4 Sep 2019 11:00:20 -0400 Subject: [PATCH] service/cloudhsmv2: Implement sweeper To prevent sweeper errors such as: ``` 2019/09/04 02:12:15 [ERR] error running (aws_vpc): Error deleting Subnet (subnet-064c5a7cad5a28b9e): DependencyViolation: The subnet 'subnet-064c5a7cad5a28b9e' has dependencies and cannot be deleted. ``` Output from sweeper in AWS Commercial: ```console $ go test ./aws -v -sweep=us-east-1,us-west-2 -sweep-run=aws_cloudhsm_v2_cluster -timeout 10h 2019/09/04 10:27:24 [DEBUG] Running Sweepers for region (us-east-1): ... 2019/09/04 10:27:25 [INFO] Deleting CloudHSMv2 Cluster (cluster-wporekks46b) HSM: hsm-y4tvi5ukedh ... 2019/09/04 10:29:06 [INFO] Deleting CloudHSMv2 Cluster: cluster-wporekks46b ... 2019/09/04 10:30:57 Sweeper Tests ran: - aws_cloudhsm_v2_cluster 2019/09/04 10:30:57 [DEBUG] Running Sweepers for region (us-west-2): ... 2019/09/04 10:30:59 [INFO] Deleting CloudHSMv2 Cluster: cluster-w56d2uzszjf ... 2019/09/04 10:31:30 [INFO] Deleting CloudHSMv2 Cluster: cluster-zrvllautsay ... 2019/09/04 10:32:01 [INFO] Deleting CloudHSMv2 Cluster: cluster-chxrr77wb2v ... 2019/09/04 10:32:32 Sweeper Tests ran: - aws_cloudhsm_v2_cluster ``` Output from sweeper in AWS GovCloud (US): ```console $ go test ./aws -v -sweep=us-gov-west-1 -sweep-run=aws_cloudhsm_v2_cluster -timeout 10h 2019/09/04 10:28:52 [DEBUG] Running Sweepers for region (us-gov-west-1): ... 2019/09/04 10:28:54 Sweeper Tests ran: - aws_cloudhsm_v2_cluster ``` Output from acceptance testing: ``` --- PASS: TestAccAWSCloudHsm2Cluster_basic (285.06s) --- PASS: TestAccAWSCloudHsm2Hsm_basic (898.88s) ``` --- aws/resource_aws_cloudhsm2_cluster.go | 33 +++++----- aws/resource_aws_cloudhsm2_cluster_test.go | 74 ++++++++++++++++++++++ aws/resource_aws_cloudhsm2_hsm.go | 60 ++++++++++-------- aws/resource_aws_subnet_test.go | 1 + 4 files changed, 125 insertions(+), 43 deletions(-) diff --git a/aws/resource_aws_cloudhsm2_cluster.go b/aws/resource_aws_cloudhsm2_cluster.go index b8adbf005e9..fa40dda55ef 100644 --- a/aws/resource_aws_cloudhsm2_cluster.go +++ b/aws/resource_aws_cloudhsm2_cluster.go @@ -286,23 +286,11 @@ func resourceAwsCloudHsm2ClusterDelete(d *schema.ResourceData, meta interface{}) } if err != nil { - return err + return fmt.Errorf("error deleting CloudHSMv2 Cluster (%s): %s", d.Id(), err) } - log.Println("[INFO] Waiting for CloudHSMv2 Cluster to be deleted") - stateConf := &resource.StateChangeConf{ - Pending: []string{cloudhsmv2.ClusterStateDeleteInProgress}, - Target: []string{cloudhsmv2.ClusterStateDeleted}, - Refresh: resourceAwsCloudHsm2ClusterRefreshFunc(cloudhsm2, d.Id()), - Timeout: d.Timeout(schema.TimeoutCreate), - MinTimeout: 30 * time.Second, - Delay: 30 * time.Second, - } - - // Wait, catching any errors - _, errWait := stateConf.WaitForState() - if errWait != nil { - return fmt.Errorf("Error waiting for CloudHSMv2 Cluster state to be \"DELETED\": %s", errWait) + if err := waitForCloudhsmv2ClusterDeletion(cloudhsm2, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return fmt.Errorf("error waiting for CloudHSMv2 Cluster (%s) deletion: %s", d.Id(), err) } return nil @@ -367,3 +355,18 @@ func readCloudHsm2ClusterCertificates(cluster *cloudhsmv2.Cluster) []map[string] } return []map[string]interface{}{} } + +func waitForCloudhsmv2ClusterDeletion(conn *cloudhsmv2.CloudHSMV2, id string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{cloudhsmv2.ClusterStateDeleteInProgress}, + Target: []string{cloudhsmv2.ClusterStateDeleted}, + Refresh: resourceAwsCloudHsm2ClusterRefreshFunc(conn, id), + Timeout: timeout, + MinTimeout: 30 * time.Second, + Delay: 30 * time.Second, + } + + _, err := stateConf.WaitForState() + + return err +} diff --git a/aws/resource_aws_cloudhsm2_cluster_test.go b/aws/resource_aws_cloudhsm2_cluster_test.go index e13e8cb96f4..89cf39c6bdd 100644 --- a/aws/resource_aws_cloudhsm2_cluster_test.go +++ b/aws/resource_aws_cloudhsm2_cluster_test.go @@ -2,14 +2,88 @@ package aws import ( "fmt" + "log" "testing" + "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudhsmv2" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) +func init() { + resource.AddTestSweepers("aws_cloudhsm_v2_cluster", &resource.Sweeper{ + Name: "aws_cloudhsm_v2_cluster", + F: testSweepCloudhsmv2Clusters, + }) +} + +func testSweepCloudhsmv2Clusters(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).cloudhsmv2conn + + input := &cloudhsmv2.DescribeClustersInput{} + + err = conn.DescribeClustersPages(input, func(page *cloudhsmv2.DescribeClustersOutput, lastPage bool) bool { + for _, cluster := range page.Clusters { + clusterID := aws.StringValue(cluster.ClusterId) + input := &cloudhsmv2.DeleteClusterInput{ + ClusterId: cluster.ClusterId, + } + + for _, hsm := range cluster.Hsms { + hsmID := aws.StringValue(hsm.HsmId) + input := &cloudhsmv2.DeleteHsmInput{ + ClusterId: cluster.ClusterId, + HsmId: hsm.HsmId, + } + + log.Printf("[INFO] Deleting CloudHSMv2 Cluster (%s) HSM: %s", clusterID, hsmID) + _, err := conn.DeleteHsm(input) + + if err != nil { + log.Printf("[ERROR] Error deleting CloudHSMv2 Cluster (%s) HSM (%s): %s", clusterID, hsmID, err) + continue + } + + if err := waitForCloudhsmv2HsmDeletion(conn, hsmID, 120*time.Minute); err != nil { + log.Printf("[ERROR] Error waiting for CloudHSMv2 Cluster (%s) HSM (%s) deletion: %s", clusterID, hsmID, err) + } + } + + log.Printf("[INFO] Deleting CloudHSMv2 Cluster: %s", clusterID) + _, err := conn.DeleteCluster(input) + + if err != nil { + log.Printf("[ERROR] Error deleting CloudHSMv2 Cluster (%s): %s", clusterID, err) + continue + } + + if err := waitForCloudhsmv2ClusterDeletion(conn, clusterID, 120*time.Minute); err != nil { + log.Printf("[ERROR] Error waiting for CloudHSMv2 Cluster (%s) deletion: %s", clusterID, err) + } + } + + return !lastPage + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping CloudHSMv2 Cluster sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error describing CloudHSMv2 Clusters: %s", err) + } + + return nil +} + func TestAccAWSCloudHsm2Cluster_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/aws/resource_aws_cloudhsm2_hsm.go b/aws/resource_aws_cloudhsm2_hsm.go index ac6d7966d0b..d07a37e0459 100644 --- a/aws/resource_aws_cloudhsm2_hsm.go +++ b/aws/resource_aws_cloudhsm2_hsm.go @@ -100,17 +100,16 @@ func describeHsm(conn *cloudhsmv2.CloudHSMV2, hsmId string) (*cloudhsmv2.Hsm, er return hsm, nil } -func resourceAwsCloudHsm2HsmRefreshFunc( - d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { +func resourceAwsCloudHsm2HsmRefreshFunc(conn *cloudhsmv2.CloudHSMV2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - hsm, err := describeHsm(meta.(*AWSClient).cloudhsmv2conn, d.Id()) + hsm, err := describeHsm(conn, id) if hsm == nil { return 42, "destroyed", nil } if hsm.State != nil { - log.Printf("[DEBUG] CloudHSMv2 Cluster status (%s): %s", d.Id(), *hsm.State) + log.Printf("[DEBUG] CloudHSMv2 Cluster status (%s): %s", id, *hsm.State) } return hsm, aws.StringValue(hsm.State), err @@ -174,22 +173,9 @@ func resourceAwsCloudHsm2HsmCreate(d *schema.ResourceData, meta interface{}) err } d.SetId(aws.StringValue(output.Hsm.HsmId)) - log.Printf("[INFO] CloudHSMv2 HSM Id: %s", d.Id()) - log.Println("[INFO] Waiting for CloudHSMv2 HSM to be available") - stateConf := &resource.StateChangeConf{ - Pending: []string{cloudhsmv2.HsmStateCreateInProgress, "destroyed"}, - Target: []string{cloudhsmv2.HsmStateActive}, - Refresh: resourceAwsCloudHsm2HsmRefreshFunc(d, meta), - Timeout: d.Timeout(schema.TimeoutCreate), - MinTimeout: 30 * time.Second, - Delay: 30 * time.Second, - } - - // Wait, catching any errors - _, errWait := stateConf.WaitForState() - if errWait != nil { - return fmt.Errorf("Error waiting for CloudHSMv2 HSM state to be \"ACTIVE\": %s", errWait) + if err := waitForCloudhsmv2HsmActive(cloudhsm2, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return fmt.Errorf("error waiting for CloudHSMv2 HSM (%s) creation: %s", d.Id(), err) } return resourceAwsCloudHsm2HsmRead(d, meta) @@ -246,22 +232,40 @@ func resourceAwsCloudHsm2HsmDelete(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf("error deleting CloudHSM v2 HSM module (%s): %s", d.Id(), err) } - log.Println("[INFO] Waiting for CloudHSMv2 HSM to be deleted") + if err := waitForCloudhsmv2HsmDeletion(cloudhsm2, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return fmt.Errorf("error waiting for CloudHSMv2 HSM (%s) deletion: %s", d.Id(), err) + } + + return nil +} + +func waitForCloudhsmv2HsmActive(conn *cloudhsmv2.CloudHSMV2, id string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{cloudhsmv2.HsmStateCreateInProgress, "destroyed"}, + Target: []string{cloudhsmv2.HsmStateActive}, + Refresh: resourceAwsCloudHsm2HsmRefreshFunc(conn, id), + Timeout: timeout, + MinTimeout: 30 * time.Second, + Delay: 30 * time.Second, + } + + _, err := stateConf.WaitForState() + + return err +} + +func waitForCloudhsmv2HsmDeletion(conn *cloudhsmv2.CloudHSMV2, id string, timeout time.Duration) error { stateConf := &resource.StateChangeConf{ Pending: []string{cloudhsmv2.HsmStateDeleteInProgress}, Target: []string{"destroyed"}, - Refresh: resourceAwsCloudHsm2HsmRefreshFunc(d, meta), - Timeout: d.Timeout(schema.TimeoutCreate), + Refresh: resourceAwsCloudHsm2HsmRefreshFunc(conn, id), + Timeout: timeout, MinTimeout: 30 * time.Second, Delay: 30 * time.Second, } - // Wait, catching any errors - _, errWait := stateConf.WaitForState() - if errWait != nil { - return fmt.Errorf("Error waiting for CloudHSMv2 HSM state to be \"DELETED\": %s", errWait) - } + _, err := stateConf.WaitForState() - return nil + return err } diff --git a/aws/resource_aws_subnet_test.go b/aws/resource_aws_subnet_test.go index e0c89109eab..440f679f8e9 100644 --- a/aws/resource_aws_subnet_test.go +++ b/aws/resource_aws_subnet_test.go @@ -23,6 +23,7 @@ func init() { "aws_autoscaling_group", "aws_batch_compute_environment", "aws_beanstalk_environment", + "aws_cloudhsm_v2_cluster", "aws_db_subnet_group", "aws_directory_service_directory", "aws_ec2_client_vpn_endpoint",