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

skip_destroy support for RDS Option and Parameter Group Resources #29663

Merged
merged 17 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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
Loading