Skip to content

Commit

Permalink
Merge pull request #29663 from marco-hoyer/rds_resources_skip_destroy
Browse files Browse the repository at this point in the history
skip_destroy support for RDS Option and Parameter Group Resources
  • Loading branch information
ewbankkit authored Aug 8, 2024
2 parents 9e8ae24 + ced9672 commit 67557b1
Show file tree
Hide file tree
Showing 27 changed files with 1,382 additions and 1,303 deletions.
7 changes: 7 additions & 0 deletions .changelog/29663.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_db_option_group: Add `skip_destroy` argument
```

```release-note:enhancement
resource/aws_db_parameter_group: Add `skip_destroy` argument
```
416 changes: 196 additions & 220 deletions internal/service/rds/cluster.go

Large diffs are not rendered by default.

99 changes: 58 additions & 41 deletions internal/service/rds/cluster_activity_stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@ import (
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"
"github.com/hashicorp/aws-sdk-go-base/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/enum"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
"github.com/hashicorp/terraform-provider-aws/names"
)

// @SDKResource("aws_rds_cluster_activity_stream")
func ResourceClusterActivityStream() *schema.Resource {
// @SDKResource("aws_rds_cluster_activity_stream", name="Cluster Activity Stream")
func resourceClusterActivityStream() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceClusterActivityStreamCreate,
ReadWithoutTimeout: resourceClusterActivityStreamRead,
Expand All @@ -49,10 +51,10 @@ func ResourceClusterActivityStream() *schema.Resource {
ForceNew: true,
},
names.AttrMode: {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice(rds.ActivityStreamMode_Values(), false),
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateDiagFunc: enum.Validate[types.ActivityStreamMode](),
},
names.AttrResourceARN: {
Type: schema.TypeString,
Expand All @@ -66,26 +68,26 @@ func ResourceClusterActivityStream() *schema.Resource {

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

arn := d.Get(names.AttrResourceARN).(string)
input := &rds.StartActivityStreamInput{
ApplyImmediately: aws.Bool(true),
EngineNativeAuditFieldsIncluded: aws.Bool(d.Get("engine_native_audit_fields_included").(bool)),
KmsKeyId: aws.String(d.Get(names.AttrKMSKeyID).(string)),
Mode: aws.String(d.Get(names.AttrMode).(string)),
Mode: types.ActivityStreamMode(d.Get(names.AttrMode).(string)),
ResourceArn: aws.String(arn),
}

_, err := conn.StartActivityStreamWithContext(ctx, input)
_, err := conn.StartActivityStream(ctx, input)

if err != nil {
return sdkdiag.AppendErrorf(diags, "creating RDS Cluster Activity Stream (%s): %s", arn, err)
}

d.SetId(arn)

if err := waitActivityStreamStarted(ctx, conn, d.Id()); err != nil {
if _, err := waitActivityStreamStarted(ctx, conn, d.Id()); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster Activity Stream (%s) start: %s", d.Id(), err)
}

Expand All @@ -94,9 +96,9 @@ func resourceClusterActivityStreamCreate(ctx context.Context, d *schema.Resource

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

output, err := FindDBClusterWithActivityStream(ctx, conn, d.Id())
output, err := findDBClusterWithActivityStream(ctx, conn, d.Id())

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] RDS Cluster Activity Stream (%s) not found, removing from state", d.Id())
Expand All @@ -118,42 +120,48 @@ func resourceClusterActivityStreamRead(ctx context.Context, d *schema.ResourceDa

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

log.Printf("[DEBUG] Deleting RDS Cluster Activity Stream: %s", d.Id())
_, err := conn.StopActivityStreamWithContext(ctx, &rds.StopActivityStreamInput{
_, err := conn.StopActivityStream(ctx, &rds.StopActivityStreamInput{
ApplyImmediately: aws.Bool(true),
ResourceArn: aws.String(d.Id()),
})

if tfawserr.ErrMessageContains(err, errCodeInvalidParameterCombination, "Activity Streams feature expected to be started, but is stopped") {
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "stopping RDS Cluster Activity Stream (%s): %s", d.Id(), err)
}

if err := waitActivityStreamStopped(ctx, conn, d.Id()); err != nil {
if _, err := waitActivityStreamStopped(ctx, conn, d.Id()); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster Activity Stream (%s) stop: %s", d.Id(), err)
}

return diags
}

func FindDBClusterWithActivityStream(ctx context.Context, conn *rds.RDS, arn string) (*rds.DBCluster, error) {
output, err := FindDBClusterByID(ctx, conn, arn)
func findDBClusterWithActivityStream(ctx context.Context, conn *rds.Client, arn string) (*types.DBCluster, error) {
output, err := findDBClusterByID(ctx, conn, arn)

if err != nil {
return nil, err
}

if status := aws.StringValue(output.ActivityStreamStatus); status == rds.ActivityStreamStatusStopped {
if status := output.ActivityStreamStatus; status == types.ActivityStreamStatusStopped {
return nil, &retry.NotFoundError{
Message: status,
Message: string(status),
}
}

return output, nil
}

func statusDBClusterActivityStream(ctx context.Context, conn *rds.RDS, arn string) retry.StateRefreshFunc {
func statusDBClusterActivityStream(ctx context.Context, conn *rds.Client, arn string) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := FindDBClusterWithActivityStream(ctx, conn, arn)
output, err := findDBClusterWithActivityStream(ctx, conn, arn)

if tfresource.NotFound(err) {
return nil, "", nil
Expand All @@ -163,41 +171,50 @@ func statusDBClusterActivityStream(ctx context.Context, conn *rds.RDS, arn strin
return nil, "", err
}

return output, aws.StringValue(output.ActivityStreamStatus), nil
return output, string(output.ActivityStreamStatus), nil
}
}

const (
dbClusterActivityStreamStartedTimeout = 30 * time.Minute
dbClusterActivityStreamStoppedTimeout = 30 * time.Minute
)

func waitActivityStreamStarted(ctx context.Context, conn *rds.RDS, arn string) error {
func waitActivityStreamStarted(ctx context.Context, conn *rds.Client, arn string) (*types.DBCluster, error) {
const (
timeout = 30 * time.Minute
)
stateConf := &retry.StateChangeConf{
Pending: []string{rds.ActivityStreamStatusStarting},
Target: []string{rds.ActivityStreamStatusStarted},
Pending: enum.Slice(types.ActivityStreamStatusStarting),
Target: enum.Slice(types.ActivityStreamStatusStarted),
Refresh: statusDBClusterActivityStream(ctx, conn, arn),
Timeout: dbClusterActivityStreamStartedTimeout,
Timeout: timeout,
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second,
}

_, err := stateConf.WaitForStateContext(ctx)
outputRaw, err := stateConf.WaitForStateContext(ctx)

return err
if output, ok := outputRaw.(*types.DBCluster); ok {
return output, err
}

return nil, err
}

func waitActivityStreamStopped(ctx context.Context, conn *rds.RDS, arn string) error {
func waitActivityStreamStopped(ctx context.Context, conn *rds.Client, arn string) (*types.DBCluster, error) {
const (
timeout = 30 * time.Minute
)
stateConf := &retry.StateChangeConf{
Pending: []string{rds.ActivityStreamStatusStopping},
Pending: enum.Slice(types.ActivityStreamStatusStopping),
Target: []string{},
Refresh: statusDBClusterActivityStream(ctx, conn, arn),
Timeout: dbClusterActivityStreamStoppedTimeout,
Timeout: timeout,
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second,
}

_, err := stateConf.WaitForStateContext(ctx)
outputRaw, err := stateConf.WaitForStateContext(ctx)

if output, ok := outputRaw.(*types.DBCluster); ok {
return output, err
}

return err
return nil, err
}
22 changes: 9 additions & 13 deletions internal/service/rds/cluster_activity_stream_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
Expand All @@ -22,14 +21,14 @@ import (

func TestAccRDSClusterActivityStream_basic(t *testing.T) {
ctx := acctest.Context(t)
var dbCluster rds.DBCluster
var dbCluster types.DBCluster
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_rds_cluster_activity_stream.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartition(t, endpoints.AwsPartitionID)
acctest.PreCheckPartition(t, names.StandardPartitionID)
},
ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Expand All @@ -55,14 +54,14 @@ func TestAccRDSClusterActivityStream_basic(t *testing.T) {

func TestAccRDSClusterActivityStream_disappears(t *testing.T) {
ctx := acctest.Context(t)
var dbCluster rds.DBCluster
var dbCluster types.DBCluster
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_rds_cluster_activity_stream.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartition(t, endpoints.AwsPartitionID)
acctest.PreCheckPartition(t, names.StandardPartitionID)
},
ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Expand All @@ -80,20 +79,17 @@ func TestAccRDSClusterActivityStream_disappears(t *testing.T) {
})
}

func testAccCheckClusterActivityStreamExists(ctx context.Context, n string, v *rds.DBCluster) resource.TestCheckFunc {
func testAccCheckClusterActivityStreamExists(ctx context.Context, n string, v *types.DBCluster) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("RDS Cluster Activity Stream ID is not set")
}

conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx)
conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx)

output, err := tfrds.FindDBClusterWithActivityStream(ctx, conn, rs.Primary.ID)

if err != nil {
return err
}
Expand All @@ -106,7 +102,7 @@ func testAccCheckClusterActivityStreamExists(ctx context.Context, n string, v *r

func testAccCheckClusterActivityStreamDestroy(ctx context.Context) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx)
conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx)

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_rds_cluster_activity_stream" {
Expand Down
Loading

0 comments on commit 67557b1

Please sign in to comment.