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 instance- support for UpgradeStorageConfig #36904

Merged
merged 14 commits into from
Aug 1, 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
3 changes: 3 additions & 0 deletions .changelog/36904.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_db_instance: Add `upgrade_storage_config` argument
```
2 changes: 1 addition & 1 deletion internal/service/rds/cluster_role_association.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func resourceClusterRoleAssociationRead(ctx context.Context, d *schema.ResourceD
output, err := findDBClusterRoleByTwoPartKey(ctx, conn, dbClusterID, roleARN)

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] RDS DB Cluster (%s) IAM Role (%s) Association not found, removing from state", dbClusterID, roleARN)
log.Printf("[WARN] RDS Cluster (%s) IAM Role (%s) Association not found, removing from state", dbClusterID, roleARN)
d.SetId("")
return diags
}
Expand Down
8 changes: 4 additions & 4 deletions internal/service/rds/cluster_role_association_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) {
})
}

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

conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx)
Expand Down Expand Up @@ -166,7 +166,7 @@ func testAccCheckClusterRoleAssociationDestroy(ctx context.Context) resource.Tes
return err
}

return fmt.Errorf("RDS DB Cluster IAM Role Association %s still exists", rs.Primary.ID)
return fmt.Errorf("RDS Cluster IAM Role Association %s still exists", rs.Primary.ID)
}

return nil
Expand Down
4 changes: 4 additions & 0 deletions internal/service/rds/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ var (
ResourceCustomDBEngineVersion = resourceCustomDBEngineVersion
ResourceEventSubscription = resourceEventSubscription
ResourceInstanceAutomatedBackupsReplication = resourceInstanceAutomatedBackupsReplication
ResourceInstanceRoleAssociation = resourceInstanceRoleAssociation
ResourceOptionGroup = resourceOptionGroup
ResourceParameterGroup = resourceParameterGroup
ResourceProxy = resourceProxy
ResourceProxyDefaultTargetGroup = resourceProxyDefaultTargetGroup
Expand All @@ -30,6 +32,7 @@ var (
FindDBClusterSnapshotByID = findDBClusterSnapshotByID
FindDBInstanceAutomatedBackupByARN = findDBInstanceAutomatedBackupByARN
FindDBInstanceByID = findDBInstanceByIDSDKv1
FindDBInstanceRoleByTwoPartKey = findDBInstanceRoleByTwoPartKey
FindDBParameterGroupByName = findDBParameterGroupByName
FindDBProxyByName = findDBProxyByName
FindDBProxyEndpointByTwoPartKey = findDBProxyEndpointByTwoPartKey
Expand All @@ -39,6 +42,7 @@ var (
FindDefaultCertificate = findDefaultCertificate
FindDefaultDBProxyTargetGroupByDBProxyName = findDefaultDBProxyTargetGroupByDBProxyName
FindEventSubscriptionByID = findEventSubscriptionByID
FindOptionGroupByName = findOptionGroupByName
ListTags = listTags
NewBlueGreenOrchestrator = newBlueGreenOrchestrator
ParameterGroupModifyChunk = parameterGroupModifyChunk
Expand Down
8 changes: 8 additions & 0 deletions internal/service/rds/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,10 @@ func ResourceInstance() *schema.Resource {
"s3_import",
},
},
"upgrade_storage_config": {
Type: schema.TypeBool,
Optional: true,
},
names.AttrUsername: {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -868,6 +872,10 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, meta in
input.StorageType = aws.String(v.(string))
}

if v, ok := d.GetOk("upgrade_storage_config"); ok {
input.UpgradeStorageConfig = aws.Bool(v.(bool))
}

if v, ok := d.GetOk(names.AttrVPCSecurityGroupIDs); ok && v.(*schema.Set).Len() > 0 {
input.VpcSecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set))
}
Expand Down
188 changes: 92 additions & 96 deletions internal/service/rds/instance_role_association.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import (
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"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/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"
"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"
"github.com/hashicorp/terraform-provider-aws/names"
Expand All @@ -29,13 +31,8 @@ const (
dbInstanceRoleStatusPending = "PENDING"
)

const (
dbInstanceRoleAssociationCreatedTimeout = 10 * time.Minute
dbInstanceRoleAssociationDeletedTimeout = 10 * time.Minute
)

// @SDKResource("aws_db_instance_role_association")
func ResourceInstanceRoleAssociation() *schema.Resource {
// @SDKResource("aws_db_instance_role_association", name="DB Instance IAM Role Association")
func resourceInstanceRoleAssociation() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceInstanceRoleAssociationCreate,
ReadWithoutTimeout: resourceInstanceRoleAssociationRead,
Expand Down Expand Up @@ -68,63 +65,56 @@ func ResourceInstanceRoleAssociation() *schema.Resource {

func resourceInstanceRoleAssociationCreate(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)

dbInstanceIdentifier := d.Get("db_instance_identifier").(string)
roleArn := d.Get(names.AttrRoleARN).(string)

roleARN := d.Get(names.AttrRoleARN).(string)
id := instanceRoleAssociationCreateResourceID(dbInstanceIdentifier, roleARN)
input := &rds.AddRoleToDBInstanceInput{
DBInstanceIdentifier: aws.String(dbInstanceIdentifier),
FeatureName: aws.String(d.Get("feature_name").(string)),
RoleArn: aws.String(roleArn),
RoleArn: aws.String(roleARN),
}

err := retry.RetryContext(ctx, propagationTimeout, func() *retry.RetryError {
var err error
_, err = conn.AddRoleToDBInstanceWithContext(ctx, input)
if err != nil {
if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "IAM role ARN value is invalid or does not include the required permissions") {
return retry.RetryableError(err)
}
return retry.NonRetryableError(err)
}
return nil
})
if tfresource.TimedOut(err) {
_, err = conn.AddRoleToDBInstanceWithContext(ctx, input)
}
_, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, func() (interface{}, error) {
return conn.AddRoleToDBInstance(ctx, input)
}, errCodeInvalidParameterValue, "IAM role ARN value is invalid or does not include the required permissions")

if err != nil {
return sdkdiag.AppendErrorf(diags, "associating RDS DB Instance (%s) IAM Role (%s): %s", dbInstanceIdentifier, roleArn, err)
return sdkdiag.AppendErrorf(diags, "creating RDS DB Instance IAM Role Association (%s): %s", id, err)
}

d.SetId(fmt.Sprintf("%s,%s", dbInstanceIdentifier, roleArn))
d.SetId(id)

if err := waitForDBInstanceRoleAssociation(ctx, conn, dbInstanceIdentifier, roleArn); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Instance (%s) IAM Role (%s) association: %s", dbInstanceIdentifier, roleArn, err)
const (
timeout = 10 * time.Minute
)
if _, err := waitDBInstanceRoleAssociationCreated(ctx, conn, dbInstanceIdentifier, roleARN, timeout); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Instance IAM Role Association (%s) create: %s", d.Id(), err)
}

return append(diags, resourceInstanceRoleAssociationRead(ctx, d, meta)...)
}

func resourceInstanceRoleAssociationRead(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)

dbInstanceIdentifier, roleArn, err := InstanceRoleAssociationDecodeID(d.Id())
dbInstanceIdentifier, roleARN, err := instanceRoleAssociationParseResourceID(d.Id())
if err != nil {
return sdkdiag.AppendErrorf(diags, "reading resource ID: %s", err)
return sdkdiag.AppendFromErr(diags, err)
}

dbInstanceRole, err := DescribeDBInstanceRole(ctx, conn, dbInstanceIdentifier, roleArn)
dbInstanceRole, err := findDBInstanceRoleByTwoPartKey(ctx, conn, dbInstanceIdentifier, roleARN)

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] RDS DB Instance (%s) not found, removing from state", dbInstanceIdentifier)
log.Printf("[WARN] RDS DB Instance (%s) IAM Role (%s) Association not found, removing from state", dbInstanceIdentifier, roleARN)
d.SetId("")
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "reading RDS DB Instance (%s) IAM Role (%s) association: %s", dbInstanceIdentifier, roleArn, err)
return sdkdiag.AppendErrorf(diags, "reading RDS DB Instance IAM Role Association (%s): %s", d.Id(), err)
}

d.Set("db_instance_identifier", dbInstanceIdentifier)
Expand All @@ -136,109 +126,115 @@ func resourceInstanceRoleAssociationRead(ctx context.Context, d *schema.Resource

func resourceInstanceRoleAssociationDelete(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)

dbInstanceIdentifier, roleArn, err := InstanceRoleAssociationDecodeID(d.Id())
dbInstanceIdentifier, roleARN, err := instanceRoleAssociationParseResourceID(d.Id())
if err != nil {
return sdkdiag.AppendErrorf(diags, "reading resource ID: %s", err)
return sdkdiag.AppendFromErr(diags, err)
}

input := &rds.RemoveRoleFromDBInstanceInput{
log.Printf("[DEBUG] Deleting RDS DB Instance IAM Role Association: %s", d.Id())
_, err = conn.RemoveRoleFromDBInstance(ctx, &rds.RemoveRoleFromDBInstanceInput{
DBInstanceIdentifier: aws.String(dbInstanceIdentifier),
FeatureName: aws.String(d.Get("feature_name").(string)),
RoleArn: aws.String(roleArn),
}

log.Printf("[DEBUG] RDS DB Instance (%s) IAM Role disassociating: %s", dbInstanceIdentifier, roleArn)
_, err = conn.RemoveRoleFromDBInstanceWithContext(ctx, input)

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBInstanceNotFoundFault) {
return diags
}
RoleArn: aws.String(roleARN),
})

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBInstanceRoleNotFoundFault) {
if errs.IsA[*types.DBInstanceNotFoundFault](err) || errs.IsA[*types.DBInstanceRoleNotFoundFault](err) {
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "disassociating RDS DB Instance (%s) IAM Role (%s): %s", dbInstanceIdentifier, roleArn, err)
return sdkdiag.AppendErrorf(diags, "deleting RDS DB Instance IAM Role Association (%s): %s", d.Id(), err)
}

if err := WaitForDBInstanceRoleDisassociation(ctx, conn, dbInstanceIdentifier, roleArn); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Instance (%s) IAM Role (%s) disassociation: %s", dbInstanceIdentifier, roleArn, err)
const (
timeout = 10 * time.Minute
)
if _, err := waitDBInstanceRoleAssociationDeleted(ctx, conn, dbInstanceIdentifier, roleARN, timeout); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Instance IAM Role Association (%s) delete: %s", d.Id(), err)
}

return diags
}

func InstanceRoleAssociationDecodeID(id string) (string, string, error) {
parts := strings.SplitN(id, ",", 2)
const instanceRoleAssociationResourceIDSeparator = ","

func instanceRoleAssociationCreateResourceID(dbInstanceID, roleARN string) string {
parts := []string{dbInstanceID, roleARN}
id := strings.Join(parts, instanceRoleAssociationResourceIDSeparator)

return id
}

func instanceRoleAssociationParseResourceID(id string) (string, string, error) {
parts := strings.SplitN(id, instanceRoleAssociationResourceIDSeparator, 2)

if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return "", "", fmt.Errorf("unexpected format of ID (%s), expected DB-INSTANCE-ID,ROLE-ARN", id)
return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected DB-INSTANCE-ID%[2]sROLE-ARN", id, instanceRoleAssociationResourceIDSeparator)
}

return parts[0], parts[1], nil
}

func DescribeDBInstanceRole(ctx context.Context, conn *rds.RDS, dbInstanceIdentifier, roleArn string) (*rds.DBInstanceRole, error) {
dbInstance, err := findDBInstanceByIDSDKv1(ctx, conn, dbInstanceIdentifier)
func findDBInstanceRoleByTwoPartKey(ctx context.Context, conn *rds.Client, dbInstanceIdentifier, roleARN string) (*types.DBInstanceRole, error) {
dbInstance, err := findDBInstanceByIDSDKv2(ctx, conn, dbInstanceIdentifier)

if err != nil {
return nil, err
}

for _, associatedRole := range dbInstance.AssociatedRoles {
if aws.StringValue(associatedRole.RoleArn) == roleArn {
return associatedRole, nil
return tfresource.AssertSingleValueResult(tfslices.Filter(dbInstance.AssociatedRoles, func(v types.DBInstanceRole) bool {
return aws.ToString(v.RoleArn) == roleARN
}))
}

func statusDBInstanceRoleAssociation(ctx context.Context, conn *rds.Client, dbInstanceIdentifier, roleARN string) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := findDBInstanceRoleByTwoPartKey(ctx, conn, dbInstanceIdentifier, roleARN)

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

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

return nil, &tfresource.EmptyResultError{}
return output, aws.ToString(output.Status), nil
}
}

func waitForDBInstanceRoleAssociation(ctx context.Context, conn *rds.RDS, dbInstanceIdentifier, roleArn string) error {
func waitDBInstanceRoleAssociationCreated(ctx context.Context, conn *rds.Client, dbInstanceIdentifier, roleARN string, timeout time.Duration) (*types.DBInstanceRole, error) {
stateConf := &retry.StateChangeConf{
Pending: []string{dbInstanceRoleStatusPending},
Target: []string{dbInstanceRoleStatusActive},
Refresh: statusDBInstanceRoleAssociation(ctx, conn, dbInstanceIdentifier, roleArn),
Timeout: dbInstanceRoleAssociationCreatedTimeout,
Refresh: statusDBInstanceRoleAssociation(ctx, conn, dbInstanceIdentifier, roleARN),
Timeout: timeout,
}

log.Printf("[DEBUG] Waiting for RDS DB Instance (%s) IAM Role association: %s", dbInstanceIdentifier, roleArn)
_, err := stateConf.WaitForStateContext(ctx)
outputRaw, err := stateConf.WaitForStateContext(ctx)

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

return err
return nil, err
}

func WaitForDBInstanceRoleDisassociation(ctx context.Context, conn *rds.RDS, dbInstanceIdentifier, roleArn string) error {
func waitDBInstanceRoleAssociationDeleted(ctx context.Context, conn *rds.Client, dbInstanceIdentifier, roleARN string, timeout time.Duration) (*types.DBInstanceRole, error) {
stateConf := &retry.StateChangeConf{
Pending: []string{
dbInstanceRoleStatusActive,
dbInstanceRoleStatusPending,
},
Pending: []string{dbInstanceRoleStatusActive, dbInstanceRoleStatusPending},
Target: []string{},
Refresh: statusDBInstanceRoleAssociation(ctx, conn, dbInstanceIdentifier, roleArn),
Timeout: dbInstanceRoleAssociationDeletedTimeout,
Refresh: statusDBInstanceRoleAssociation(ctx, conn, dbInstanceIdentifier, roleARN),
Timeout: timeout,
}

log.Printf("[DEBUG] Waiting for RDS DB Instance (%s) IAM Role disassociation: %s", dbInstanceIdentifier, roleArn)
_, err := stateConf.WaitForStateContext(ctx)

return err
}

func statusDBInstanceRoleAssociation(ctx context.Context, conn *rds.RDS, dbInstanceIdentifier, roleArn string) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
dbInstanceRole, err := DescribeDBInstanceRole(ctx, conn, dbInstanceIdentifier, roleArn)

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

if err != nil {
return nil, "", err
}
outputRaw, err := stateConf.WaitForStateContext(ctx)

return dbInstanceRole, aws.StringValue(dbInstanceRole.Status), nil
if output, ok := outputRaw.(*types.DBInstanceRole); ok {
return output, err
}

return nil, err
}
Loading
Loading