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

r/aws_rds_cluster: Fix InvalidDBClusterStateFault errors when deleting clusters that are members of a global cluster #40333

Merged
merged 15 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
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
3 changes: 3 additions & 0 deletions .changelog/40333.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_rds_cluster: Fix `InvalidDBClusterStateFault` errors when deleting clusters that are members of a global cluster
```
32 changes: 25 additions & 7 deletions internal/service/rds/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -1626,6 +1626,15 @@
if err != nil && !errs.IsA[*types.GlobalClusterNotFoundFault](err) && !tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "is not found in global cluster") {
return sdkdiag.AppendErrorf(diags, "removing RDS Cluster (%s) from RDS Global Cluster: %s", d.Id(), err)
}

if _, err := waitGlobalClusterMemberRemoved(ctx, conn, clusterARN, d.Timeout(schema.TimeoutUpdate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Cluster (%s) removal from RDS Global Cluster (%s): %s", d.Id(), os, err)
}

// Removal from a global cluster puts the cluster into 'promoting' state. Wait for it to become available again.
if _, err := waitDBClusterAvailable(ctx, conn, d.Id(), true, d.Timeout(schema.TimeoutUpdate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster (%s) available: %s", d.Id(), err)
}
}

if d.HasChange("iam_roles") {
Expand All @@ -1648,14 +1657,14 @@
return append(diags, resourceClusterRead(ctx, d, meta)...)
}

func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) {
func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).RDSClient(ctx)

// Automatically remove from global cluster to bypass this error on deletion:
// InvalidDBClusterStateFault: This cluster is a part of a global cluster, please remove it from globalcluster first
if d.Get("global_cluster_identifier").(string) != "" {
if globalClusterID := d.Get("global_cluster_identifier").(string); globalClusterID != "" {
clusterARN := d.Get(names.AttrARN).(string)
globalClusterID := d.Get("global_cluster_identifier").(string)
input := &rds.RemoveFromGlobalClusterInput{
DbClusterIdentifier: aws.String(clusterARN),
GlobalClusterIdentifier: aws.String(globalClusterID),
Expand All @@ -1666,6 +1675,14 @@
if err != nil && !errs.IsA[*types.GlobalClusterNotFoundFault](err) && !tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "is not found in global cluster") {
return sdkdiag.AppendErrorf(diags, "removing RDS Cluster (%s) from RDS Global Cluster (%s): %s", d.Id(), globalClusterID, err)
}

if _, err := waitGlobalClusterMemberRemoved(ctx, conn, clusterARN, d.Timeout(schema.TimeoutDelete)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Cluster (%s) removal from RDS Global Cluster (%s): %s", d.Id(), globalClusterID, err)
}

if _, err := waitDBClusterAvailable(ctx, conn, d.Id(), true, d.Timeout(schema.TimeoutCreate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster (%s) available: %s", d.Id(), err)
}
}

skipFinalSnapshot := d.Get("skip_final_snapshot").(bool)
Expand Down Expand Up @@ -1919,16 +1936,17 @@
}
}

func waitDBClusterAvailable(ctx context.Context, conn *rds.Client, id string, waitNoPendingModifiedValues bool, timeout time.Duration) (*types.DBCluster, error) {

Check failure on line 1939 in internal/service/rds/cluster.go

View workflow job for this annotation

GitHub Actions / 3 of 3

waitDBClusterAvailable - result 0 (*github.com/aws/aws-sdk-go-v2/service/rds/types.DBCluster) is never used (unparam)
pendingStatuses := []string{
clusterStatusCreating,
clusterStatusMigrating,
clusterStatusPreparingDataMigration,
clusterStatusRebooting,
clusterStatusBackingUp,
clusterStatusConfiguringIAMDatabaseAuth,
clusterStatusConfiguringEnhancedMonitoring,
clusterStatusCreating,
clusterStatusMigrating,
clusterStatusModifying,
clusterStatusPreparingDataMigration,
clusterStatusPromoting,
clusterStatusRebooting,
clusterStatusRenaming,
clusterStatusResettingMasterCredentials,
clusterStatusScalingCompute,
Expand Down
44 changes: 44 additions & 0 deletions internal/service/rds/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,28 @@ func TestAccRDSCluster_takeFinalSnapshot(t *testing.T) {
})
}

func TestAccRDSCluster_GlobalClusterIdentifierTakeFinalSnapshot(t *testing.T) {
ctx := acctest.Context(t)
var v types.DBCluster
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_rds_cluster.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckClusterDestroyWithFinalSnapshot(ctx),
Steps: []resource.TestStep{
{
Config: testAccClusterConfig_GlobalClusterID_finalSnapshot(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckClusterExists(ctx, resourceName, &v),
),
},
},
})
}

// This is a regression test to make sure that we always cover the scenario as highlighted in
// https://github.com/hashicorp/terraform/issues/11568
// Expected error updated to match API response
Expand Down Expand Up @@ -3594,6 +3616,28 @@ resource "aws_rds_cluster" "test" {
`, rName, tfrds.ClusterEngineAuroraMySQL)
}

func testAccClusterConfig_GlobalClusterID_finalSnapshot(rName string) string {
return fmt.Sprintf(`
resource "aws_rds_global_cluster" "test" {
global_cluster_identifier = %[1]q
engine = "aurora-postgresql"
engine_version = "11.9"
}

resource "aws_rds_cluster" "test" {
cluster_identifier = %[1]q
database_name = "test"
engine = aws_rds_global_cluster.test.engine
engine_version = aws_rds_global_cluster.test.engine_version
master_username = "tfacctest"
master_password = "avoid-plaintext-passwords"
final_snapshot_identifier = %[1]q
skip_final_snapshot = false
global_cluster_identifier = aws_rds_global_cluster.test.id
}
`, rName)
}

func testAccClusterConfig_withoutUserNameAndPassword(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "default" {
Expand Down
Loading