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

F aws db snapshot data source tags filter #31600

Merged
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/31600.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
data-source/aws_db_snapshot: Add `tags` argument
```

```release-note:bug
resource/aws_db_instance_automated_backups_replication: Fix `unexpected state 'Pending'` errors on resource Create
```
2 changes: 1 addition & 1 deletion internal/errs/sdkdiag/diags.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func Warnings(diags diag.Diagnostics) diag.Diagnostics {
return tfslices.Filter(diags, severityFilter(diag.Warning))
}

func severityFilter(s diag.Severity) tfslices.FilterFunc[diag.Diagnostic] {
func severityFilter(s diag.Severity) tfslices.Predicate[diag.Diagnostic] {
return func(d diag.Diagnostic) bool {
return d.Severity == s
}
Expand Down
8 changes: 6 additions & 2 deletions internal/service/cloudformation/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ func WaitStackDeleted(ctx context.Context, conn *cloudformation.CloudFormation,
return output, err
}

func findStackEventsForOperation(ctx context.Context, conn *cloudformation.CloudFormation, name, requestToken string, f slices.FilterFunc[*cloudformation.StackEvent]) ([]*cloudformation.StackEvent, error) {
func findStackEventsForOperation(ctx context.Context, conn *cloudformation.CloudFormation, name, requestToken string, filter slices.Predicate[*cloudformation.StackEvent]) ([]*cloudformation.StackEvent, error) {
input := &cloudformation.DescribeStackEventsInput{
StackName: aws.String(name),
}
Expand All @@ -611,6 +611,10 @@ func findStackEventsForOperation(ctx context.Context, conn *cloudformation.Cloud
}

for _, v := range page.StackEvents {
if v == nil {
continue
}

if currentToken := aws.StringValue(v.ClientRequestToken); !tokenSeen {
if currentToken != requestToken {
continue
Expand All @@ -622,7 +626,7 @@ func findStackEventsForOperation(ctx context.Context, conn *cloudformation.Cloud
}
}

if f(v) {
if filter(v) {
output = append(output, v)
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/service/inspector2/enabler.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ func statusEnablerAccountAndResourceTypes(ctx context.Context, conn *inspector2.
}) {
return true
}
if v.Status == types.StatusEnabled && tfslices.All(maps.Values(v.ResourceStatuses), tfslices.FilterEquals(types.StatusDisabled)) {
if v.Status == types.StatusEnabled && tfslices.All(maps.Values(v.ResourceStatuses), tfslices.PredicateEquals(types.StatusDisabled)) {
return true
}
return false
Expand Down
6 changes: 0 additions & 6 deletions internal/service/rds/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,6 @@ const (
InstanceStatusUpgrading = "upgrading"
)

const (
InstanceAutomatedBackupStatusPending = "pending"
InstanceAutomatedBackupStatusReplicating = "replicating"
InstanceAutomatedBackupStatusRetained = "retained"
)

const (
EventSubscriptionStatusActive = "active"
EventSubscriptionStatusCreating = "creating"
Expand Down
76 changes: 0 additions & 76 deletions internal/service/rds/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,82 +195,6 @@ func FindEventSubscriptionByID(ctx context.Context, conn *rds.RDS, id string) (*
return output.EventSubscriptionsList[0], nil
}

func FindDBInstanceAutomatedBackupByARN(ctx context.Context, conn *rds.RDS, arn string) (*rds.DBInstanceAutomatedBackup, error) {
input := &rds.DescribeDBInstanceAutomatedBackupsInput{
DBInstanceAutomatedBackupsArn: aws.String(arn),
}

output, err := findDBInstanceAutomatedBackup(ctx, conn, input)
if err != nil {
return nil, err
}

if status := aws.StringValue(output.Status); status == InstanceAutomatedBackupStatusRetained {
// If the automated backup is retained, the replication is stopped.
return nil, &retry.NotFoundError{
Message: status,
LastRequest: input,
}
}

// Eventual consistency check.
if aws.StringValue(output.DBInstanceAutomatedBackupsArn) != arn {
return nil, &retry.NotFoundError{
LastRequest: input,
}
}

return output, nil
}

func findDBInstanceAutomatedBackup(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBInstanceAutomatedBackupsInput) (*rds.DBInstanceAutomatedBackup, error) {
output, err := findDBInstanceAutomatedBackups(ctx, conn, input)
if err != nil {
return nil, err
}

if len(output) == 0 || output[0] == nil {
return nil, tfresource.NewEmptyResultError(input)
}

if count := len(output); count > 1 {
return nil, tfresource.NewTooManyResultsError(count, input)
}

return output[0], nil
}

func findDBInstanceAutomatedBackups(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBInstanceAutomatedBackupsInput) ([]*rds.DBInstanceAutomatedBackup, error) {
var output []*rds.DBInstanceAutomatedBackup

err := conn.DescribeDBInstanceAutomatedBackupsPagesWithContext(ctx, input, func(page *rds.DescribeDBInstanceAutomatedBackupsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, v := range page.DBInstanceAutomatedBackups {
if v != nil {
output = append(output, v)
}
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBInstanceAutomatedBackupNotFoundFault) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

return output, nil
}

func FindGlobalClusterByDBClusterARN(ctx context.Context, conn *rds.RDS, dbClusterARN string) (*rds.GlobalCluster, error) {
input := &rds.DescribeGlobalClustersInput{}
globalClusters, err := findGlobalClusters(ctx, conn, input)
Expand Down
168 changes: 160 additions & 8 deletions internal/service/rds/instance_automated_backups_replication.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,27 @@ package rds
import (
"context"
"log"
"strconv"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/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-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)

const (
InstanceAutomatedBackupsReplicationCreateTimeout = 75 * time.Minute
InstanceAutomatedBackupsReplicationDeleteTimeout = 75 * time.Minute
InstanceAutomatedBackupStatusPending = "Pending"
InstanceAutomatedBackupStatusReplicating = "Replicating"
InstanceAutomatedBackupStatusRetained = "Retained"
)

// @SDKResource("aws_db_instance_automated_backups_replication")
Expand All @@ -33,8 +37,8 @@ func ResourceInstanceAutomatedBackupsReplication() *schema.Resource {
DeleteWithoutTimeout: resourceInstanceAutomatedBackupsReplicationDelete,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(InstanceAutomatedBackupsReplicationCreateTimeout),
Delete: schema.DefaultTimeout(InstanceAutomatedBackupsReplicationDeleteTimeout),
Create: schema.DefaultTimeout(75 * time.Minute),
Delete: schema.DefaultTimeout(75 * time.Minute),
},

Importer: &schema.ResourceImporter{
Expand Down Expand Up @@ -87,7 +91,6 @@ func resourceInstanceAutomatedBackupsReplicationCreate(ctx context.Context, d *s
input.PreSignedUrl = aws.String(v.(string))
}

log.Printf("[DEBUG] Starting RDS instance automated backups replication: %s", input)
output, err := conn.StartDBInstanceAutomatedBackupsReplicationWithContext(ctx, input)
if err != nil {
return sdkdiag.AppendErrorf(diags, "starting RDS instance automated backups replication: %s", err)
Expand Down Expand Up @@ -137,13 +140,13 @@ func resourceInstanceAutomatedBackupsReplicationDelete(ctx context.Context, d *s
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting RDS DB Instance Automated Backup (%s): %s", d.Id(), err)
return sdkdiag.AppendErrorf(diags, "reading RDS instance automated backup (%s): %s", d.Id(), err)
}

dbInstanceID := aws.StringValue(backup.DBInstanceIdentifier)
sourceDatabaseARN, err := arn.Parse(aws.StringValue(backup.DBInstanceArn))
if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting RDS DB Instance Automated Backup (%s): %s", d.Id(), err)
return sdkdiag.AppendFromErr(diags, err)
}

log.Printf("[DEBUG] Stopping RDS Instance Automated Backups Replication: %s", d.Id())
Expand All @@ -155,6 +158,10 @@ func resourceInstanceAutomatedBackupsReplicationDelete(ctx context.Context, d *s
return diags
}

if tfawserr.ErrMessageContains(err, rds.ErrCodeInvalidDBInstanceStateFault, "not replicating to the current region") {
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting RDS DB Instance Automated Backup (%s): %s", d.Id(), err)
}
Expand All @@ -166,8 +173,153 @@ func resourceInstanceAutomatedBackupsReplicationDelete(ctx context.Context, d *s
}

if _, err := waitDBInstanceAutomatedBackupDeleted(ctx, sourceDatabaseConn, dbInstanceID, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return sdkdiag.AppendErrorf(diags, "deleting RDS DB Instance Automated Backup (%s): waiting for completion: %s", d.Id(), err)
return sdkdiag.AppendErrorf(diags, "waiting for DB instance automated backup (%s) delete: %s", d.Id(), err)
}

return diags
}

func FindDBInstanceAutomatedBackupByARN(ctx context.Context, conn *rds.RDS, arn string) (*rds.DBInstanceAutomatedBackup, error) {
input := &rds.DescribeDBInstanceAutomatedBackupsInput{
DBInstanceAutomatedBackupsArn: aws.String(arn),
}
output, err := findDBInstanceAutomatedBackup(ctx, conn, input)

if err != nil {
return nil, err
}

if status := aws.StringValue(output.Status); status == InstanceAutomatedBackupStatusRetained {
// If the automated backup is retained, the replication is stopped.
return nil, &retry.NotFoundError{
Message: status,
LastRequest: input,
}
}

// Eventual consistency check.
if aws.StringValue(output.DBInstanceAutomatedBackupsArn) != arn {
return nil, &retry.NotFoundError{
LastRequest: input,
}
}

return output, nil
}

func findDBInstanceAutomatedBackup(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBInstanceAutomatedBackupsInput) (*rds.DBInstanceAutomatedBackup, error) {
output, err := findDBInstanceAutomatedBackups(ctx, conn, input, tfslices.PredicateTrue[*rds.DBInstanceAutomatedBackup]())

if err != nil {
return nil, err
}

return tfresource.AssertSinglePtrResult(output)
}

func findDBInstanceAutomatedBackups(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBInstanceAutomatedBackupsInput, filter tfslices.Predicate[*rds.DBInstanceAutomatedBackup]) ([]*rds.DBInstanceAutomatedBackup, error) {
var output []*rds.DBInstanceAutomatedBackup

err := conn.DescribeDBInstanceAutomatedBackupsPagesWithContext(ctx, input, func(page *rds.DescribeDBInstanceAutomatedBackupsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, v := range page.DBInstanceAutomatedBackups {
if v != nil && filter(v) {
output = append(output, v)
}
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBInstanceAutomatedBackupNotFoundFault) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

return output, nil
}

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

if tfresource.NotFound(err) {
return nil, "", nil
}

if err != nil {
return nil, "", err
}

return output, aws.StringValue(output.Status), nil
}
}

func waitDBInstanceAutomatedBackupCreated(ctx context.Context, conn *rds.RDS, arn string, timeout time.Duration) (*rds.DBInstanceAutomatedBackup, error) {
stateConf := &retry.StateChangeConf{
Pending: []string{InstanceAutomatedBackupStatusPending},
Target: []string{InstanceAutomatedBackupStatusReplicating},
Refresh: statusDBInstanceAutomatedBackup(ctx, conn, arn),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

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

return nil, err
}

// statusDBInstanceHasAutomatedBackup returns whether or not a database instance has a specified automated backup.
// The connection must be valid for the database instance's Region.
func statusDBInstanceHasAutomatedBackup(ctx context.Context, conn *rds.RDS, dbInstanceID, dbInstanceAutomatedBackupsARN string) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := findDBInstanceByIDSDKv1(ctx, conn, dbInstanceID)

if tfresource.NotFound(err) {
return nil, "", nil
}

if err != nil {
return nil, "", err
}

for _, v := range output.DBInstanceAutomatedBackupsReplications {
if aws.StringValue(v.DBInstanceAutomatedBackupsArn) == dbInstanceAutomatedBackupsARN {
return output, strconv.FormatBool(true), nil
}
}

return output, strconv.FormatBool(false), nil
}
}

// waitDBInstanceAutomatedBackupDeleted waits for a specified automated backup to be deleted from a database instance.
// The connection must be valid for the database instance's Region.
func waitDBInstanceAutomatedBackupDeleted(ctx context.Context, conn *rds.RDS, dbInstanceID, dbInstanceAutomatedBackupsARN string, timeout time.Duration) (*rds.DBInstance, error) {
stateConf := &retry.StateChangeConf{
Pending: []string{strconv.FormatBool(true)},
Target: []string{strconv.FormatBool(false)},
Refresh: statusDBInstanceHasAutomatedBackup(ctx, conn, dbInstanceID, dbInstanceAutomatedBackupsARN),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

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

return nil, err
}
Loading