From a081a274f954b5176aab875b649b400e7725f8c0 Mon Sep 17 00:00:00 2001 From: Sam Kaessner <1779550+OpenGLShaders@users.noreply.github.com> Date: Sat, 9 Dec 2023 16:03:04 -0700 Subject: [PATCH 01/30] Add shared_accounts parameter to db_snapshot_copy --- .changelog/31212.txt | 3 ++ internal/service/rds/snapshot_copy.go | 46 ++++++++++++++++++- website/docs/r/db_snapshot_copy.html.markdown | 1 + 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 .changelog/31212.txt diff --git a/.changelog/31212.txt b/.changelog/31212.txt new file mode 100644 index 00000000000..cd7293d6017 --- /dev/null +++ b/.changelog/31212.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/db_snapshot_copy: Add `shared_accounts` argument +``` \ No newline at end of file diff --git a/internal/service/rds/snapshot_copy.go b/internal/service/rds/snapshot_copy.go index 0d5c055b7c2..97f9524b6f1 100644 --- a/internal/service/rds/snapshot_copy.go +++ b/internal/service/rds/snapshot_copy.go @@ -103,6 +103,11 @@ func ResourceSnapshotCopy() *schema.Resource { Optional: true, ForceNew: true, }, + "shared_accounts": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "source_db_snapshot_identifier": { Type: schema.TypeString, Required: true, @@ -187,6 +192,20 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met if err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Snapshot Copy (%s) create: %s", d.Id(), err) } + + if v, ok := d.GetOk("shared_accounts"); ok && v.(*schema.Set).Len() > 0 { + input := &rds.ModifyDBSnapshotAttributeInput{ + AttributeName: aws.String("restore"), + DBSnapshotIdentifier: aws.String(d.Id()), + ValuesToAdd: flex.ExpandStringSet(v.(*schema.Set)), + } + + _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err) + } + } return append(diags, resourceSnapshotCopyRead(ctx, d, meta)...) } @@ -225,12 +244,37 @@ func resourceSnapshotCopyRead(ctx context.Context, d *schema.ResourceData, meta d.Set("storage_type", snapshot.StorageType) d.Set("target_db_snapshot_identifier", snapshot.DBSnapshotIdentifier) d.Set("vpc_id", snapshot.VpcId) + d.Set("shared_accounts", flex.FlattenStringSet(output.DBSnapshotAttributesResult.DBSnapshotAttributes[0].AttributeValues)) return diags } func resourceSnapshotCopyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - // Tags only. + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).RDSConn(ctx) + + if d.HasChange("shared_accounts") { + o, n := d.GetChange("shared_accounts") + os := o.(*schema.Set) + ns := n.(*schema.Set) + + additionList := ns.Difference(os) + removalList := os.Difference(ns) + + input := &rds.ModifyDBSnapshotAttributeInput{ + AttributeName: aws.String("restore"), + DBSnapshotIdentifier: aws.String(d.Id()), + ValuesToAdd: flex.ExpandStringSet(additionList), + ValuesToRemove: flex.ExpandStringSet(removalList), + } + + _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attributes: %s", d.Id(), err) + } + } + return resourceSnapshotCopyRead(ctx, d, meta) } diff --git a/website/docs/r/db_snapshot_copy.html.markdown b/website/docs/r/db_snapshot_copy.html.markdown index d42e57928f7..c0ee3efb5bc 100644 --- a/website/docs/r/db_snapshot_copy.html.markdown +++ b/website/docs/r/db_snapshot_copy.html.markdown @@ -67,6 +67,7 @@ This resource exports the following attributes in addition to the arguments abov * `kms_key_id` - The ARN for the KMS encryption key. * `license_model` - License model information for the restored DB instance. * `option_group_name` - Provides the option group name for the DB snapshot. +* `shared_accounts` - (Optional) List of AWS Account ids to share snapshot with, use `all` to make snaphot public. * `source_db_snapshot_identifier` - The DB snapshot Arn that the DB snapshot was copied from. It only has value in case of cross customer or cross region copy. * `source_region` - The region that the DB snapshot was created in or copied from. * `storage_type` - Specifies the storage type associated with DB snapshot. From d72a0825514c593dc82416fbd497e9844206e25c Mon Sep 17 00:00:00 2001 From: Sam Kaessner <1779550+OpenGLShaders@users.noreply.github.com> Date: Sun, 10 Dec 2023 09:11:35 -0700 Subject: [PATCH 02/30] formatting --- internal/service/rds/snapshot_copy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/rds/snapshot_copy.go b/internal/service/rds/snapshot_copy.go index 97f9524b6f1..53ddd137efe 100644 --- a/internal/service/rds/snapshot_copy.go +++ b/internal/service/rds/snapshot_copy.go @@ -192,7 +192,7 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met if err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Snapshot Copy (%s) create: %s", d.Id(), err) } - + if v, ok := d.GetOk("shared_accounts"); ok && v.(*schema.Set).Len() > 0 { input := &rds.ModifyDBSnapshotAttributeInput{ AttributeName: aws.String("restore"), From 624fb6144a04f0898f297db8b9ee52dea455c15b Mon Sep 17 00:00:00 2001 From: Sam Kaessner <1779550+OpenGLShaders@users.noreply.github.com> Date: Sun, 10 Dec 2023 10:07:14 -0700 Subject: [PATCH 03/30] make it compile --- internal/service/rds/snapshot_copy.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/internal/service/rds/snapshot_copy.go b/internal/service/rds/snapshot_copy.go index 53ddd137efe..be60c12fb82 100644 --- a/internal/service/rds/snapshot_copy.go +++ b/internal/service/rds/snapshot_copy.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -244,6 +245,17 @@ func resourceSnapshotCopyRead(ctx context.Context, d *schema.ResourceData, meta d.Set("storage_type", snapshot.StorageType) d.Set("target_db_snapshot_identifier", snapshot.DBSnapshotIdentifier) d.Set("vpc_id", snapshot.VpcId) + + input := &rds.DescribeDBSnapshotAttributesInput{ + DBSnapshotIdentifier: aws.String(d.Id()), + } + + output, err := conn.DescribeDBSnapshotAttributesWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s) attributes: %s", d.Id(), err) + } + d.Set("shared_accounts", flex.FlattenStringSet(output.DBSnapshotAttributesResult.DBSnapshotAttributes[0].AttributeValues)) return diags From 0dac5cabe5dad1099b8c44b77aee10b49ac05c6b Mon Sep 17 00:00:00 2001 From: Sam Kaessner <1779550+OpenGLShaders@users.noreply.github.com> Date: Sun, 10 Dec 2023 11:23:38 -0700 Subject: [PATCH 04/30] adding tests --- internal/service/rds/snapshot_copy_test.go | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/internal/service/rds/snapshot_copy_test.go b/internal/service/rds/snapshot_copy_test.go index aebffac6c50..e4f8ea5e5d2 100644 --- a/internal/service/rds/snapshot_copy_test.go +++ b/internal/service/rds/snapshot_copy_test.go @@ -38,6 +38,8 @@ func TestAccRDSSnapshotCopy_basic(t *testing.T) { Config: testAccSnapshotCopyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSnapshotCopyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -49,6 +51,46 @@ func TestAccRDSSnapshotCopy_basic(t *testing.T) { }) } +func TestAccRDSSnapshotCopy_share(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var v rds.DBSnapshot + resourceName := "aws_db_snapshot_copy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSnapshotCopyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSnapshotCopyConfig_share(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSnapshotCopyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "shared_accounts.*", "all"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccSnapshotCopyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSnapshotCopyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", "0"), + ), + }, + }, + }) +} + func TestAccRDSSnapshotCopy_tags(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -242,3 +284,13 @@ resource "aws_db_snapshot_copy" "test" { } }`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } + +func testAccSnapshotCopyConfig_share(rName string) string { + return acctest.ConfigCompose(testAccSnapshotCopyConfig_base(rName), fmt.Sprintf(` +resource "aws_db_snapshot_copy" "test" { + source_db_snapshot_identifier = aws_db_snapshot.test.db_snapshot_arn + target_db_snapshot_identifier = "%[1]s-target" + shared_accounts = ["all"] +} +`, rName)) +} From 540935a378d3ae62b81d033ca79569daab825dad Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 23 Jul 2024 15:53:03 -0400 Subject: [PATCH 05/30] Run 'make fix-constants PKG=rds'. --- internal/service/rds/snapshot_copy_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/rds/snapshot_copy_test.go b/internal/service/rds/snapshot_copy_test.go index 45130c7fb0b..c0d96186d28 100644 --- a/internal/service/rds/snapshot_copy_test.go +++ b/internal/service/rds/snapshot_copy_test.go @@ -39,8 +39,8 @@ func TestAccRDSSnapshotCopy_basic(t *testing.T) { Config: testAccSnapshotCopyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSnapshotCopyExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", "0"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), ), }, { @@ -72,7 +72,7 @@ func TestAccRDSSnapshotCopy_share(t *testing.T) { Config: testAccSnapshotCopyConfig_share(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSnapshotCopyExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", "1"), + resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", acctest.Ct1), resource.TestCheckTypeSetElemAttr(resourceName, "shared_accounts.*", "all"), ), }, @@ -85,7 +85,7 @@ func TestAccRDSSnapshotCopy_share(t *testing.T) { Config: testAccSnapshotCopyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSnapshotCopyExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", "0"), + resource.TestCheckResourceAttr(resourceName, "shared_accounts.#", acctest.Ct0), ), }, }, From bfb2c0f490077c5273bea20247630840725f3383 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 23 Jul 2024 15:54:03 -0400 Subject: [PATCH 06/30] Correct CHANGELOG entry. --- .changelog/31212.txt | 3 --- .changelog/34843.txt | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 .changelog/31212.txt create mode 100644 .changelog/34843.txt diff --git a/.changelog/31212.txt b/.changelog/31212.txt deleted file mode 100644 index cd7293d6017..00000000000 --- a/.changelog/31212.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:enhancement -resource/db_snapshot_copy: Add `shared_accounts` argument -``` \ No newline at end of file diff --git a/.changelog/34843.txt b/.changelog/34843.txt new file mode 100644 index 00000000000..8120fc4fb11 --- /dev/null +++ b/.changelog/34843.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_db_snapshot_copy: Add `shared_accounts` argument +``` \ No newline at end of file From 550b6a53e0dc7a0cc482af80e63374f02f11bbc6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 23 Jul 2024 16:11:59 -0400 Subject: [PATCH 07/30] Cosmetics. --- internal/service/rds/consts.go | 8 +- internal/service/rds/exports_test.go | 3 + internal/service/rds/service_package_gen.go | 4 +- internal/service/rds/snapshot.go | 117 ++++++++++++++++---- internal/service/rds/snapshot_copy.go | 38 +++---- internal/service/rds/snapshot_copy_test.go | 2 +- internal/service/rds/status.go | 16 --- internal/service/rds/sweep.go | 2 +- internal/service/rds/wait.go | 15 --- 9 files changed, 123 insertions(+), 82 deletions(-) diff --git a/internal/service/rds/consts.go b/internal/service/rds/consts.go index ec4b746a099..34927cc6d9b 100644 --- a/internal/service/rds/consts.go +++ b/internal/service/rds/consts.go @@ -134,8 +134,12 @@ const ( ) const ( - DBSnapshotAvailable = "available" - DBSnapshotCreating = "creating" + dbSnapshotAvailable = "available" + dbSnapshotCreating = "creating" +) + +const ( + dbSnapshotAttributeNameRestore = "restore" ) const ( diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index ceead998a56..2e36dc350ca 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -13,6 +13,8 @@ var ( ResourceProxyDefaultTargetGroup = resourceProxyDefaultTargetGroup ResourceProxyEndpoint = resourceProxyEndpoint ResourceProxyTarget = resourceProxyTarget + ResourceSnapshot = resourceSnapshot + ResourceSnapshotCopy = resourceSnapshotCopy ResourceSubnetGroup = resourceSubnetGroup FindDBClusterSnapshotByID = findDBClusterSnapshotByID @@ -20,6 +22,7 @@ var ( FindDBProxyByName = findDBProxyByName FindDBProxyEndpointByTwoPartKey = findDBProxyEndpointByTwoPartKey FindDBProxyTargetByFourPartKey = findDBProxyTargetByFourPartKey + FindDBSnapshotByID = findDBSnapshotByID FindDBSubnetGroupByName = findDBSubnetGroupByName FindDefaultCertificate = findDefaultCertificate FindDefaultDBProxyTargetGroupByDBProxyName = findDefaultDBProxyTargetGroupByDBProxyName diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index f007684df4d..b5800987f45 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -176,7 +176,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Name: "DB Proxy Target", }, { - Factory: ResourceSnapshot, + Factory: resourceSnapshot, TypeName: "aws_db_snapshot", Name: "DB Snapshot", Tags: &types.ServicePackageResourceTags{ @@ -184,7 +184,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceSnapshotCopy, + Factory: resourceSnapshotCopy, TypeName: "aws_db_snapshot_copy", Name: "DB Snapshot", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/rds/snapshot.go b/internal/service/rds/snapshot.go index 5076bd17736..73d87213f31 100644 --- a/internal/service/rds/snapshot.go +++ b/internal/service/rds/snapshot.go @@ -27,7 +27,7 @@ import ( // @SDKResource("aws_db_snapshot", name="DB Snapshot") // @Tags(identifierAttribute="db_snapshot_arn") // @Testing(tagsTest=false) -func ResourceSnapshot() *schema.Resource { +func resourceSnapshot() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceSnapshotCreate, ReadWithoutTimeout: resourceSnapshotRead, @@ -146,13 +146,14 @@ func resourceSnapshotCreate(ctx context.Context, d *schema.ResourceData, meta in } output, err := conn.CreateDBSnapshotWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS DB Snapshot (%s): %s", dbSnapshotID, err) } d.SetId(aws.StringValue(output.DBSnapshot.DBSnapshotIdentifier)) - if err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + if _, err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Snapshot (%s) create: %s", d.Id(), err) } @@ -164,6 +165,7 @@ func resourceSnapshotCreate(ctx context.Context, d *schema.ResourceData, meta in } _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err) } @@ -176,7 +178,7 @@ func resourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSConn(ctx) - snapshot, err := FindDBSnapshotByID(ctx, conn, d.Id()) + snapshot, err := findDBSnapshotByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS DB Snapshot (%s) not found, removing from state", d.Id()) @@ -208,18 +210,15 @@ func resourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta inte d.Set(names.AttrStatus, snapshot.Status) d.Set(names.AttrVPCID, snapshot.VpcId) - input := &rds.DescribeDBSnapshotAttributesInput{ - DBSnapshotIdentifier: aws.String(d.Id()), - } - - output, err := conn.DescribeDBSnapshotAttributesWithContext(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s) attributes: %s", d.Id(), err) + attribute, err := findDBSnapshotAttributeByTwoPartKey(ctx, conn, d.Id(), dbSnapshotAttributeNameRestore) + switch { + case err == nil: + d.Set("shared_accounts", flex.FlattenStringSet(attribute.AttributeValues)) + case tfresource.NotFound(err): + default: + return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s) attribute: %s", d.Id(), err) } - d.Set("shared_accounts", flex.FlattenStringSet(output.DBSnapshotAttributesResult.DBSnapshotAttributes[0].AttributeValues)) - setTagsOut(ctx, snapshot.TagList) return diags @@ -231,23 +230,19 @@ func resourceSnapshotUpdate(ctx context.Context, d *schema.ResourceData, meta in if d.HasChange("shared_accounts") { o, n := d.GetChange("shared_accounts") - os := o.(*schema.Set) - ns := n.(*schema.Set) - - additionList := ns.Difference(os) - removalList := os.Difference(ns) - + os, ns := o.(*schema.Set), n.(*schema.Set) + add, del := ns.Difference(os), os.Difference(ns) input := &rds.ModifyDBSnapshotAttributeInput{ AttributeName: aws.String("restore"), DBSnapshotIdentifier: aws.String(d.Id()), - ValuesToAdd: flex.ExpandStringSet(additionList), - ValuesToRemove: flex.ExpandStringSet(removalList), + ValuesToAdd: flex.ExpandStringSet(add), + ValuesToRemove: flex.ExpandStringSet(del), } _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attributes: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err) } } @@ -274,7 +269,7 @@ func resourceSnapshotDelete(ctx context.Context, d *schema.ResourceData, meta in return diags } -func FindDBSnapshotByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBSnapshot, error) { +func findDBSnapshotByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBSnapshot, error) { input := &rds.DescribeDBSnapshotsInput{ DBSnapshotIdentifier: aws.String(id), } @@ -334,3 +329,79 @@ func findDBSnapshots(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBSn return output, nil } + +func statusDBSnapshot(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findDBSnapshotByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Status), nil + } +} + +func waitDBSnapshotCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBSnapshot, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{dbSnapshotCreating}, + Target: []string{dbSnapshotAvailable}, + Refresh: statusDBSnapshot(ctx, conn, id), + Timeout: timeout, + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*rds.DBSnapshot); ok { + return output, err + } + + return nil, err +} + +func findDBSnapshotAttributeByTwoPartKey(ctx context.Context, conn *rds.RDS, id, attributeName string) (*rds.DBSnapshotAttribute, error) { + input := &rds.DescribeDBSnapshotAttributesInput{ + DBSnapshotIdentifier: aws.String(id), + } + + return findDBSnapshotAttribute(ctx, conn, input, func(v *rds.DBSnapshotAttribute) bool { + return aws.StringValue(v.AttributeName) == attributeName + }) +} + +func findDBSnapshotAttribute(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBSnapshotAttributesInput, filter tfslices.Predicate[*rds.DBSnapshotAttribute]) (*rds.DBSnapshotAttribute, error) { + output, err := findDBSnapshotAttributes(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSinglePtrResult(output) +} + +func findDBSnapshotAttributes(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBSnapshotAttributesInput, filter tfslices.Predicate[*rds.DBSnapshotAttribute]) ([]*rds.DBSnapshotAttribute, error) { + output, err := conn.DescribeDBSnapshotAttributesWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.DBSnapshotAttributesResult == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return tfslices.Filter(output.DBSnapshotAttributesResult.DBSnapshotAttributes, filter), nil +} diff --git a/internal/service/rds/snapshot_copy.go b/internal/service/rds/snapshot_copy.go index 9dacd95f6b0..e1b4ab3f1de 100644 --- a/internal/service/rds/snapshot_copy.go +++ b/internal/service/rds/snapshot_copy.go @@ -27,7 +27,7 @@ import ( // @SDKResource("aws_db_snapshot_copy", name="DB Snapshot") // @Tags(identifierAttribute="db_snapshot_arn") // @Testing(tagsTest=false) -func ResourceSnapshotCopy() *schema.Resource { +func resourceSnapshotCopy() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceSnapshotCopyCreate, ReadWithoutTimeout: resourceSnapshotCopyRead, @@ -185,13 +185,14 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met } output, err := conn.CopyDBSnapshotWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS DB Snapshot Copy (%s): %s", targetDBSnapshotID, err) } d.SetId(aws.StringValue(output.DBSnapshot.DBSnapshotIdentifier)) - if err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + if _, err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Snapshot Copy (%s) create: %s", d.Id(), err) } @@ -216,7 +217,7 @@ func resourceSnapshotCopyRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSConn(ctx) - snapshot, err := FindDBSnapshotByID(ctx, conn, d.Id()) + snapshot, err := findDBSnapshotByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS DB Snapshot (%s) not found, removing from state", d.Id()) @@ -247,18 +248,15 @@ func resourceSnapshotCopyRead(ctx context.Context, d *schema.ResourceData, meta d.Set("target_db_snapshot_identifier", snapshot.DBSnapshotIdentifier) d.Set(names.AttrVPCID, snapshot.VpcId) - input := &rds.DescribeDBSnapshotAttributesInput{ - DBSnapshotIdentifier: aws.String(d.Id()), - } - - output, err := conn.DescribeDBSnapshotAttributesWithContext(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s) attributes: %s", d.Id(), err) + attribute, err := findDBSnapshotAttributeByTwoPartKey(ctx, conn, d.Id(), dbSnapshotAttributeNameRestore) + switch { + case err == nil: + d.Set("shared_accounts", flex.FlattenStringSet(attribute.AttributeValues)) + case tfresource.NotFound(err): + default: + return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s) attribute: %s", d.Id(), err) } - d.Set("shared_accounts", flex.FlattenStringSet(output.DBSnapshotAttributesResult.DBSnapshotAttributes[0].AttributeValues)) - return diags } @@ -268,23 +266,19 @@ func resourceSnapshotCopyUpdate(ctx context.Context, d *schema.ResourceData, met if d.HasChange("shared_accounts") { o, n := d.GetChange("shared_accounts") - os := o.(*schema.Set) - ns := n.(*schema.Set) - - additionList := ns.Difference(os) - removalList := os.Difference(ns) - + os, ns := o.(*schema.Set), n.(*schema.Set) + add, del := ns.Difference(os), os.Difference(ns) input := &rds.ModifyDBSnapshotAttributeInput{ AttributeName: aws.String("restore"), DBSnapshotIdentifier: aws.String(d.Id()), - ValuesToAdd: flex.ExpandStringSet(additionList), - ValuesToRemove: flex.ExpandStringSet(removalList), + ValuesToAdd: flex.ExpandStringSet(add), + ValuesToRemove: flex.ExpandStringSet(del), } _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attributes: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err) } } diff --git a/internal/service/rds/snapshot_copy_test.go b/internal/service/rds/snapshot_copy_test.go index c0d96186d28..1c27e41f8a8 100644 --- a/internal/service/rds/snapshot_copy_test.go +++ b/internal/service/rds/snapshot_copy_test.go @@ -291,7 +291,7 @@ func testAccSnapshotCopyConfig_share(rName string) string { resource "aws_db_snapshot_copy" "test" { source_db_snapshot_identifier = aws_db_snapshot.test.db_snapshot_arn target_db_snapshot_identifier = "%[1]s-target" - shared_accounts = ["all"] + shared_accounts = ["all"] } `, rName)) } diff --git a/internal/service/rds/status.go b/internal/service/rds/status.go index be79678f8b1..dd46e8e442c 100644 --- a/internal/service/rds/status.go +++ b/internal/service/rds/status.go @@ -43,19 +43,3 @@ func statusReservedInstance(ctx context.Context, conn *rds.RDS, id string) retry return output, aws.StringValue(output.State), nil } } - -func statusDBSnapshot(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindDBSnapshotByID(ctx, conn, id) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.StringValue(output.Status), nil - } -} diff --git a/internal/service/rds/sweep.go b/internal/service/rds/sweep.go index dd1abe5cad0..bd73266e1ee 100644 --- a/internal/service/rds/sweep.go +++ b/internal/service/rds/sweep.go @@ -578,7 +578,7 @@ func sweepSnapshots(region string) error { continue } - r := ResourceSnapshot() + r := resourceSnapshot() d := r.Data(nil) d.SetId(id) diff --git a/internal/service/rds/wait.go b/internal/service/rds/wait.go index 1932090e361..a639d5d064f 100644 --- a/internal/service/rds/wait.go +++ b/internal/service/rds/wait.go @@ -156,18 +156,3 @@ func waitReservedInstanceCreated(ctx context.Context, conn *rds.RDS, id string, return err } - -func waitDBSnapshotCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) error { - stateConf := &retry.StateChangeConf{ - Pending: []string{DBSnapshotCreating}, - Target: []string{DBSnapshotAvailable}, - Refresh: statusDBSnapshot(ctx, conn, id), - Timeout: timeout, - MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, - } - - _, err := stateConf.WaitForStateContext(ctx) - - return err -} From 369eb17798d97285ed46c78a9bcb41666662ab88 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 23 Jul 2024 17:03:11 -0400 Subject: [PATCH 08/30] Acceptance test output: % make testacc TESTARGS='-run=TestAccRDSSnapshot_\|TestAccRDSSnapshotCopy_' PKG=rds ACCTEST_PARALLELISM=3 make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/rds/... -v -count 1 -parallel 3 -run=TestAccRDSSnapshot_\|TestAccRDSSnapshotCopy_ -timeout 360m === RUN TestAccRDSSnapshotCopy_basic === PAUSE TestAccRDSSnapshotCopy_basic === RUN TestAccRDSSnapshotCopy_share === PAUSE TestAccRDSSnapshotCopy_share === RUN TestAccRDSSnapshotCopy_tags === PAUSE TestAccRDSSnapshotCopy_tags === RUN TestAccRDSSnapshotCopy_disappears === PAUSE TestAccRDSSnapshotCopy_disappears === RUN TestAccRDSSnapshot_basic === PAUSE TestAccRDSSnapshot_basic === RUN TestAccRDSSnapshot_share === PAUSE TestAccRDSSnapshot_share === RUN TestAccRDSSnapshot_tags === PAUSE TestAccRDSSnapshot_tags === RUN TestAccRDSSnapshot_disappears === PAUSE TestAccRDSSnapshot_disappears === CONT TestAccRDSSnapshotCopy_basic === CONT TestAccRDSSnapshot_basic === CONT TestAccRDSSnapshotCopy_tags --- PASS: TestAccRDSSnapshot_basic (672.32s) === CONT TestAccRDSSnapshotCopy_disappears --- PASS: TestAccRDSSnapshotCopy_basic (722.32s) === CONT TestAccRDSSnapshot_tags --- PASS: TestAccRDSSnapshotCopy_tags (750.45s) === CONT TestAccRDSSnapshot_disappears --- PASS: TestAccRDSSnapshotCopy_disappears (749.22s) === CONT TestAccRDSSnapshot_share --- PASS: TestAccRDSSnapshot_tags (780.17s) === CONT TestAccRDSSnapshotCopy_share --- PASS: TestAccRDSSnapshot_disappears (880.85s) --- PASS: TestAccRDSSnapshot_share (745.66s) --- PASS: TestAccRDSSnapshotCopy_share (920.40s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/rds 2427.826s From 2bd3fd395201cabfc5327aaa7723e64a563c5049 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 23 Jul 2024 17:11:43 -0400 Subject: [PATCH 09/30] Add 'tfslices.PredicateValue'. --- internal/service/rds/cluster_snapshot.go | 6 +----- internal/slices/predicates.go | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/service/rds/cluster_snapshot.go b/internal/service/rds/cluster_snapshot.go index 751e08e5397..7523909ad0d 100644 --- a/internal/service/rds/cluster_snapshot.go +++ b/internal/service/rds/cluster_snapshot.go @@ -396,9 +396,5 @@ func findDBClusterSnapshotAttributes(ctx context.Context, conn *rds.Client, inpu return nil, tfresource.NewEmptyResultError(input) } - f := func(v types.DBClusterSnapshotAttribute) bool { - return filter(&v) - } - - return tfslices.Filter(output.DBClusterSnapshotAttributesResult.DBClusterSnapshotAttributes, f), nil + return tfslices.Filter(output.DBClusterSnapshotAttributesResult.DBClusterSnapshotAttributes, tfslices.PredicateValue(filter)), nil } diff --git a/internal/slices/predicates.go b/internal/slices/predicates.go index 962388c7a26..cfac1eac942 100644 --- a/internal/slices/predicates.go +++ b/internal/slices/predicates.go @@ -42,3 +42,9 @@ func PredicateTrue[T any]() Predicate[T] { return true } } + +func PredicateValue[T any](predicate Predicate[*T]) Predicate[T] { + return func(v T) bool { + return predicate(&v) + } +} From e386006c39f2648ebe8ad1c12268de9b8fec77a1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 23 Jul 2024 17:19:51 -0400 Subject: [PATCH 10/30] r/aws_db_snapshot: Migrate to AWS SDK for Go v2. --- internal/service/rds/instance_test.go | 80 +++++++++--------- internal/service/rds/snapshot.go | 112 +++++++++++++------------- internal/service/rds/snapshot_test.go | 20 ++--- 3 files changed, 104 insertions(+), 108 deletions(-) diff --git a/internal/service/rds/instance_test.go b/internal/service/rds/instance_test.go index e41a8e8f2b7..e16ef0626ab 100644 --- a/internal/service/rds/instance_test.go +++ b/internal/service/rds/instance_test.go @@ -14,8 +14,8 @@ import ( "github.com/YakDriver/regexache" rds_sdkv2 "github.com/aws/aws-sdk-go-v2/service/rds" + "github.com/aws/aws-sdk-go-v2/service/rds/types" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/service/rds" tfawserr_sdkv2 "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" @@ -349,7 +349,7 @@ func TestAccRDSInstance_customIAMInstanceProfile(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionNot(t, endpoints.AwsUsGovPartitionID) + acctest.PreCheckPartitionNot(t, names.USGovCloudPartitionID) }, ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -2387,7 +2387,7 @@ func TestAccRDSInstance_SnapshotIdentifier_basic(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2432,7 +2432,7 @@ func TestAccRDSInstance_SnapshotIdentifier_ManageMasterPasswordKMSKey(t *testing } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2599,7 +2599,7 @@ func TestAccRDSInstance_SnapshotIdentifier_allocatedStorage(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2632,7 +2632,7 @@ func TestAccRDSInstance_SnapshotIdentifier_io1Storage(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2665,7 +2665,7 @@ func TestAccRDSInstance_SnapshotIdentifier_io2Storage(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2698,7 +2698,7 @@ func TestAccRDSInstance_SnapshotIdentifier_allowMajorVersionUpgrade(t *testing.T } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2731,7 +2731,7 @@ func TestAccRDSInstance_SnapshotIdentifier_autoMinorVersionUpgrade(t *testing.T) } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2764,7 +2764,7 @@ func TestAccRDSInstance_SnapshotIdentifier_availabilityZone(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2796,7 +2796,7 @@ func TestAccRDSInstance_SnapshotIdentifier_backupRetentionPeriodOverride(t *test } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2829,7 +2829,7 @@ func TestAccRDSInstance_SnapshotIdentifier_backupRetentionPeriodUnset(t *testing } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2862,7 +2862,7 @@ func TestAccRDSInstance_SnapshotIdentifier_backupWindow(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -2895,7 +2895,7 @@ func TestAccRDSInstance_SnapshotIdentifier_dbSubnetGroupName(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dbSubnetGroupResourceName := "aws_db_subnet_group.test" sourceDbResourceName := "aws_db_instance.source" @@ -2928,7 +2928,7 @@ func TestAccRDSInstance_SnapshotIdentifier_dbSubnetGroupNameRAMShared(t *testing } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dbSubnetGroupResourceName := "aws_db_subnet_group.test" sourceDbResourceName := "aws_db_instance.source" @@ -2965,7 +2965,7 @@ func TestAccRDSInstance_SnapshotIdentifier_dbSubnetGroupNameVPCSecurityGroupIDs( } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dbSubnetGroupResourceName := "aws_db_subnet_group.test" sourceDbResourceName := "aws_db_instance.source" @@ -2998,7 +2998,7 @@ func TestAccRDSInstance_SnapshotIdentifier_deletionProtection(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3041,7 +3041,7 @@ func TestAccRDSInstance_SnapshotIdentifier_iamDatabaseAuthenticationEnabled(t *t } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3074,7 +3074,7 @@ func TestAccRDSInstance_SnapshotIdentifier_maintenanceWindow(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3107,7 +3107,7 @@ func TestAccRDSInstance_SnapshotIdentifier_maxAllocatedStorage(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3140,7 +3140,7 @@ func TestAccRDSInstance_SnapshotIdentifier_monitoring(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3173,7 +3173,7 @@ func TestAccRDSInstance_SnapshotIdentifier_multiAZ(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3206,7 +3206,7 @@ func TestAccRDSInstance_SnapshotIdentifier_multiAZSQLServer(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3240,7 +3240,7 @@ func TestAccRDSInstance_SnapshotIdentifier_parameterGroupName(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3274,7 +3274,7 @@ func TestAccRDSInstance_SnapshotIdentifier_port(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3307,7 +3307,7 @@ func TestAccRDSInstance_SnapshotIdentifier_tags(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3351,7 +3351,7 @@ func TestAccRDSInstance_SnapshotIdentifier_tagsRemove(t *testing.T) { ctx := acctest.Context(t) var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3393,7 +3393,7 @@ func TestAccRDSInstance_SnapshotIdentifier_vpcSecurityGroupIDs(t *testing.T) { } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -3429,7 +3429,7 @@ func TestAccRDSInstance_SnapshotIdentifier_vpcSecurityGroupIDsTags(t *testing.T) } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.source" @@ -4643,7 +4643,7 @@ func TestAccRDSInstance_SnapshotIdentifier_performanceInsightsEnabled(t *testing } var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) kmsKeyResourceName := "aws_kms_key.test" @@ -5093,7 +5093,7 @@ func TestAccRDSInstance_Outposts_coIPRestoreToPointInTime(t *testing.T) { func TestAccRDSInstance_Outposts_coIPSnapshotIdentifier(t *testing.T) { ctx := acctest.Context(t) var dbInstance, sourceDbInstance rds.DBInstance - var dbSnapshot rds.DBSnapshot + var dbSnapshot types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceDbResourceName := "aws_db_instance.test" @@ -6453,7 +6453,8 @@ func testAccCheckInstanceReplicaAttributes(source, replica *rds.DBInstance) reso // The snapshot is deleted. func testAccCheckInstanceDestroyWithFinalSnapshot(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn1 := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn2 := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_db_instance" { @@ -6461,12 +6462,12 @@ func testAccCheckInstanceDestroyWithFinalSnapshot(ctx context.Context) resource. } finalSnapshotID := rs.Primary.Attributes[names.AttrFinalSnapshotIdentifier] - output, err := tfrds.FindDBSnapshotByID(ctx, conn, finalSnapshotID) + output, err := tfrds.FindDBSnapshotByID(ctx, conn2, finalSnapshotID) if err != nil { return err } - tags, err := tfrds.ListTags(ctx, conn, aws.StringValue(output.DBSnapshotArn)) + tags, err := tfrds.ListTags(ctx, conn1, aws.StringValue(output.DBSnapshotArn)) if err != nil { return err } @@ -6475,7 +6476,7 @@ func testAccCheckInstanceDestroyWithFinalSnapshot(ctx context.Context) resource. return fmt.Errorf("Name tag not found") } - _, err = conn.DeleteDBSnapshotWithContext(ctx, &rds.DeleteDBSnapshotInput{ + _, err = conn2.DeleteDBSnapshot(ctx, &rds_sdkv2.DeleteDBSnapshotInput{ DBSnapshotIdentifier: aws.String(finalSnapshotID), }) @@ -6483,7 +6484,7 @@ func testAccCheckInstanceDestroyWithFinalSnapshot(ctx context.Context) resource. return err } - _, err = tfrds.FindDBInstanceByID(ctx, conn, rs.Primary.Attributes[names.AttrIdentifier]) + _, err = tfrds.FindDBInstanceByID(ctx, conn1, rs.Primary.Attributes[names.AttrIdentifier]) if tfresource.NotFound(err) { continue @@ -6505,7 +6506,8 @@ func testAccCheckInstanceDestroyWithFinalSnapshot(ctx context.Context) resource. // - No DBSnapshot has been produced func testAccCheckInstanceDestroyWithoutFinalSnapshot(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn1 := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn2 := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_db_instance" { @@ -6513,7 +6515,7 @@ func testAccCheckInstanceDestroyWithoutFinalSnapshot(ctx context.Context) resour } finalSnapshotID := rs.Primary.Attributes[names.AttrFinalSnapshotIdentifier] - _, err := tfrds.FindDBSnapshotByID(ctx, conn, finalSnapshotID) + _, err := tfrds.FindDBSnapshotByID(ctx, conn2, finalSnapshotID) if err != nil { if !tfresource.NotFound(err) { @@ -6523,7 +6525,7 @@ func testAccCheckInstanceDestroyWithoutFinalSnapshot(ctx context.Context) resour return fmt.Errorf("RDS DB Snapshot %s exists", finalSnapshotID) } - _, err = tfrds.FindDBInstanceByID(ctx, conn, rs.Primary.Attributes[names.AttrIdentifier]) + _, err = tfrds.FindDBInstanceByID(ctx, conn1, rs.Primary.Attributes[names.AttrIdentifier]) if tfresource.NotFound(err) { continue diff --git a/internal/service/rds/snapshot.go b/internal/service/rds/snapshot.go index 73d87213f31..e725e6b3e5d 100644 --- a/internal/service/rds/snapshot.go +++ b/internal/service/rds/snapshot.go @@ -8,13 +8,14 @@ import ( "log" "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" "github.com/hashicorp/terraform-provider-aws/internal/flex" tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" @@ -136,22 +137,22 @@ func resourceSnapshot() *schema.Resource { func resourceSnapshotCreate(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) dbSnapshotID := d.Get("db_snapshot_identifier").(string) input := &rds.CreateDBSnapshotInput{ DBInstanceIdentifier: aws.String(d.Get("db_instance_identifier").(string)), DBSnapshotIdentifier: aws.String(dbSnapshotID), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), } - output, err := conn.CreateDBSnapshotWithContext(ctx, input) + output, err := conn.CreateDBSnapshot(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS DB Snapshot (%s): %s", dbSnapshotID, err) } - d.SetId(aws.StringValue(output.DBSnapshot.DBSnapshotIdentifier)) + d.SetId(aws.ToString(output.DBSnapshot.DBSnapshotIdentifier)) if _, err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Snapshot (%s) create: %s", d.Id(), err) @@ -161,10 +162,10 @@ func resourceSnapshotCreate(ctx context.Context, d *schema.ResourceData, meta in input := &rds.ModifyDBSnapshotAttributeInput{ AttributeName: aws.String("restore"), DBSnapshotIdentifier: aws.String(d.Id()), - ValuesToAdd: flex.ExpandStringSet(v.(*schema.Set)), + ValuesToAdd: flex.ExpandStringValueSet(v.(*schema.Set)), } - _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) + _, err := conn.ModifyDBSnapshotAttribute(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err) @@ -176,7 +177,7 @@ func resourceSnapshotCreate(ctx context.Context, d *schema.ResourceData, meta in func resourceSnapshotRead(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) snapshot, err := findDBSnapshotByID(ctx, conn, d.Id()) @@ -190,7 +191,7 @@ func resourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta inte return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s): %s", d.Id(), err) } - arn := aws.StringValue(snapshot.DBSnapshotArn) + arn := aws.ToString(snapshot.DBSnapshotArn) d.Set(names.AttrAllocatedStorage, snapshot.AllocatedStorage) d.Set(names.AttrAvailabilityZone, snapshot.AvailabilityZone) d.Set("db_instance_identifier", snapshot.DBInstanceIdentifier) @@ -213,20 +214,20 @@ func resourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta inte attribute, err := findDBSnapshotAttributeByTwoPartKey(ctx, conn, d.Id(), dbSnapshotAttributeNameRestore) switch { case err == nil: - d.Set("shared_accounts", flex.FlattenStringSet(attribute.AttributeValues)) + d.Set("shared_accounts", attribute.AttributeValues) case tfresource.NotFound(err): default: return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s) attribute: %s", d.Id(), err) } - setTagsOut(ctx, snapshot.TagList) + setTagsOutV2(ctx, snapshot.TagList) return diags } func resourceSnapshotUpdate(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) if d.HasChange("shared_accounts") { o, n := d.GetChange("shared_accounts") @@ -235,11 +236,11 @@ func resourceSnapshotUpdate(ctx context.Context, d *schema.ResourceData, meta in input := &rds.ModifyDBSnapshotAttributeInput{ AttributeName: aws.String("restore"), DBSnapshotIdentifier: aws.String(d.Id()), - ValuesToAdd: flex.ExpandStringSet(add), - ValuesToRemove: flex.ExpandStringSet(del), + ValuesToAdd: flex.ExpandStringValueSet(add), + ValuesToRemove: flex.ExpandStringValueSet(del), } - _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) + _, err := conn.ModifyDBSnapshotAttribute(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err) @@ -251,14 +252,14 @@ func resourceSnapshotUpdate(ctx context.Context, d *schema.ResourceData, meta in func resourceSnapshotDelete(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 DB Snapshot: %s", d.Id()) - _, err := conn.DeleteDBSnapshotWithContext(ctx, &rds.DeleteDBSnapshotInput{ + _, err := conn.DeleteDBSnapshot(ctx, &rds.DeleteDBSnapshotInput{ DBSnapshotIdentifier: aws.String(d.Id()), }) - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) { + if errs.IsA[*types.DBSnapshotNotFoundFault](err) { return diags } @@ -269,18 +270,18 @@ func resourceSnapshotDelete(ctx context.Context, d *schema.ResourceData, meta in return diags } -func findDBSnapshotByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBSnapshot, error) { +func findDBSnapshotByID(ctx context.Context, conn *rds.Client, id string) (*types.DBSnapshot, error) { input := &rds.DescribeDBSnapshotsInput{ DBSnapshotIdentifier: aws.String(id), } - output, err := findDBSnapshot(ctx, conn, input, tfslices.PredicateTrue[*rds.DBSnapshot]()) + output, err := findDBSnapshot(ctx, conn, input, tfslices.PredicateTrue[*types.DBSnapshot]()) if err != nil { return nil, err } // Eventual consistency check. - if aws.StringValue(output.DBSnapshotIdentifier) != id { + if aws.ToString(output.DBSnapshotIdentifier) != id { return nil, &retry.NotFoundError{ LastRequest: input, } @@ -289,48 +290,45 @@ func findDBSnapshotByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBS return output, nil } -func findDBSnapshot(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBSnapshotsInput, filter tfslices.Predicate[*rds.DBSnapshot]) (*rds.DBSnapshot, error) { +func findDBSnapshot(ctx context.Context, conn *rds.Client, input *rds.DescribeDBSnapshotsInput, filter tfslices.Predicate[*types.DBSnapshot]) (*types.DBSnapshot, error) { output, err := findDBSnapshots(ctx, conn, input, filter) if err != nil { return nil, err } - return tfresource.AssertSinglePtrResult(output) + return tfresource.AssertSingleValueResult(output) } -func findDBSnapshots(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBSnapshotsInput, filter tfslices.Predicate[*rds.DBSnapshot]) ([]*rds.DBSnapshot, error) { - var output []*rds.DBSnapshot +func findDBSnapshots(ctx context.Context, conn *rds.Client, input *rds.DescribeDBSnapshotsInput, filter tfslices.Predicate[*types.DBSnapshot]) ([]types.DBSnapshot, error) { + var output []types.DBSnapshot - err := conn.DescribeDBSnapshotsPagesWithContext(ctx, input, func(page *rds.DescribeDBSnapshotsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + pages := rds.NewDescribeDBSnapshotsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - for _, v := range page.DBSnapshots { - if v != nil && filter(v) { - output = append(output, v) + if errs.IsA[*types.DBSnapshotNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, } } - return !lastPage - }) - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, + if err != nil { + return nil, err } - } - if err != nil { - return nil, err + for _, v := range page.DBSnapshots { + if filter(&v) { + output = append(output, v) + } + } } return output, nil } -func statusDBSnapshot(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc { +func statusDBSnapshot(ctx context.Context, conn *rds.Client, id string) retry.StateRefreshFunc { return func() (interface{}, string, error) { output, err := findDBSnapshotByID(ctx, conn, id) @@ -342,11 +340,11 @@ func statusDBSnapshot(ctx context.Context, conn *rds.RDS, id string) retry.State return nil, "", err } - return output, aws.StringValue(output.Status), nil + return output, aws.ToString(output.Status), nil } } -func waitDBSnapshotCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBSnapshot, error) { +func waitDBSnapshotCreated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.DBSnapshot, error) { stateConf := &retry.StateChangeConf{ Pending: []string{dbSnapshotCreating}, Target: []string{dbSnapshotAvailable}, @@ -358,37 +356,37 @@ func waitDBSnapshotCreated(ctx context.Context, conn *rds.RDS, id string, timeou outputRaw, err := stateConf.WaitForStateContext(ctx) - if output, ok := outputRaw.(*rds.DBSnapshot); ok { + if output, ok := outputRaw.(*types.DBSnapshot); ok { return output, err } return nil, err } -func findDBSnapshotAttributeByTwoPartKey(ctx context.Context, conn *rds.RDS, id, attributeName string) (*rds.DBSnapshotAttribute, error) { +func findDBSnapshotAttributeByTwoPartKey(ctx context.Context, conn *rds.Client, id, attributeName string) (*types.DBSnapshotAttribute, error) { input := &rds.DescribeDBSnapshotAttributesInput{ DBSnapshotIdentifier: aws.String(id), } - return findDBSnapshotAttribute(ctx, conn, input, func(v *rds.DBSnapshotAttribute) bool { - return aws.StringValue(v.AttributeName) == attributeName + return findDBSnapshotAttribute(ctx, conn, input, func(v *types.DBSnapshotAttribute) bool { + return aws.ToString(v.AttributeName) == attributeName }) } -func findDBSnapshotAttribute(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBSnapshotAttributesInput, filter tfslices.Predicate[*rds.DBSnapshotAttribute]) (*rds.DBSnapshotAttribute, error) { +func findDBSnapshotAttribute(ctx context.Context, conn *rds.Client, input *rds.DescribeDBSnapshotAttributesInput, filter tfslices.Predicate[*types.DBSnapshotAttribute]) (*types.DBSnapshotAttribute, error) { output, err := findDBSnapshotAttributes(ctx, conn, input, filter) if err != nil { return nil, err } - return tfresource.AssertSinglePtrResult(output) + return tfresource.AssertSingleValueResult(output) } -func findDBSnapshotAttributes(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBSnapshotAttributesInput, filter tfslices.Predicate[*rds.DBSnapshotAttribute]) ([]*rds.DBSnapshotAttribute, error) { - output, err := conn.DescribeDBSnapshotAttributesWithContext(ctx, input) +func findDBSnapshotAttributes(ctx context.Context, conn *rds.Client, input *rds.DescribeDBSnapshotAttributesInput, filter tfslices.Predicate[*types.DBSnapshotAttribute]) ([]types.DBSnapshotAttribute, error) { + output, err := conn.DescribeDBSnapshotAttributes(ctx, input) - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) { + if errs.IsA[*types.DBSnapshotNotFoundFault](err) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -403,5 +401,5 @@ func findDBSnapshotAttributes(ctx context.Context, conn *rds.RDS, input *rds.Des return nil, tfresource.NewEmptyResultError(input) } - return tfslices.Filter(output.DBSnapshotAttributesResult.DBSnapshotAttributes, filter), nil + return tfslices.Filter(output.DBSnapshotAttributesResult.DBSnapshotAttributes, tfslices.PredicateValue(filter)), nil } diff --git a/internal/service/rds/snapshot_test.go b/internal/service/rds/snapshot_test.go index 3286111bdf5..a262307bc2f 100644 --- a/internal/service/rds/snapshot_test.go +++ b/internal/service/rds/snapshot_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/YakDriver/regexache" - "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" @@ -26,7 +26,7 @@ func TestAccRDSSnapshot_basic(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var v rds.DBSnapshot + var v types.DBSnapshot resourceName := "aws_db_snapshot.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -60,7 +60,7 @@ func TestAccRDSSnapshot_share(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var v rds.DBSnapshot + var v types.DBSnapshot resourceName := "aws_db_snapshot.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -100,7 +100,7 @@ func TestAccRDSSnapshot_tags(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var v rds.DBSnapshot + var v types.DBSnapshot resourceName := "aws_db_snapshot.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -150,7 +150,7 @@ func TestAccRDSSnapshot_disappears(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var v rds.DBSnapshot + var v types.DBSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_db_snapshot.test" @@ -174,7 +174,7 @@ func TestAccRDSSnapshot_disappears(t *testing.T) { func testAccCheckDBSnapshotDestroy(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_db_snapshot" { @@ -198,18 +198,14 @@ func testAccCheckDBSnapshotDestroy(ctx context.Context) resource.TestCheckFunc { } } -func testAccCheckDBSnapshotExists(ctx context.Context, n string, v *rds.DBSnapshot) resource.TestCheckFunc { +func testAccCheckDBSnapshotExists(ctx context.Context, n string, v *types.DBSnapshot) 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("No RDS DB Snapshot ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindDBSnapshotByID(ctx, conn, rs.Primary.ID) if err != nil { From b0c847e54fbca7e8aa208beeb934bbbf26eff743 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 23 Jul 2024 17:25:54 -0400 Subject: [PATCH 11/30] d/aws_db_snapshot: Migrate to AWS SDK for Go v2. --- internal/service/rds/service_package_gen.go | 2 +- internal/service/rds/snapshot_data_source.go | 33 ++++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index b5800987f45..c2d7e0b18d9 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -62,7 +62,7 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac Name: "DB Proxy", }, { - Factory: DataSourceSnapshot, + Factory: dataSourceSnapshot, TypeName: "aws_db_snapshot", Name: "DB Snapshot", Tags: &types.ServicePackageResourceTags{}, diff --git a/internal/service/rds/snapshot_data_source.go b/internal/service/rds/snapshot_data_source.go index 168eba32280..40e0820367a 100644 --- a/internal/service/rds/snapshot_data_source.go +++ b/internal/service/rds/snapshot_data_source.go @@ -8,8 +8,9 @@ import ( "sort" "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/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -22,7 +23,7 @@ import ( // @SDKDataSource("aws_db_snapshot", name="DB Snapshot") // @Tags // @Testing(tagsTest=false) -func DataSourceSnapshot() *schema.Resource { +func dataSourceSnapshot() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceSnapshotRead, @@ -133,7 +134,7 @@ func DataSourceSnapshot() *schema.Resource { func dataSourceSnapshotRead(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) input := &rds.DescribeDBSnapshotsInput{ IncludePublic: aws.Bool(d.Get("include_public").(bool)), @@ -152,10 +153,10 @@ func dataSourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta in input.SnapshotType = aws.String(v.(string)) } - f := tfslices.PredicateTrue[*rds.DBSnapshot]() - if tags := getTagsIn(ctx); len(tags) > 0 { - f = func(v *rds.DBSnapshot) bool { - return KeyValueTags(ctx, v.TagList).ContainsAll(KeyValueTags(ctx, tags)) + f := tfslices.PredicateTrue[*types.DBSnapshot]() + if tags := getTagsInV2(ctx); len(tags) > 0 { + f = func(v *types.DBSnapshot) bool { + return keyValueTagsV2(ctx, v.TagList).ContainsAll(keyValueTagsV2(ctx, tags)) } } @@ -169,7 +170,7 @@ func dataSourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta in return sdkdiag.AppendErrorf(diags, "Your query returned no results. Please change your search criteria and try again.") } - var snapshot *rds.DBSnapshot + var snapshot *types.DBSnapshot if len(snapshots) > 1 { if d.Get(names.AttrMostRecent).(bool) { snapshot = mostRecentDBSnapshot(snapshots) @@ -177,10 +178,10 @@ func dataSourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta in return sdkdiag.AppendErrorf(diags, "Your query returned more than one result. Please try a more specific search criteria.") } } else { - snapshot = snapshots[0] + snapshot = &snapshots[0] } - d.SetId(aws.StringValue(snapshot.DBSnapshotIdentifier)) + d.SetId(aws.ToString(snapshot.DBSnapshotIdentifier)) d.Set(names.AttrAllocatedStorage, snapshot.AllocatedStorage) d.Set(names.AttrAvailabilityZone, snapshot.AvailabilityZone) d.Set("db_instance_identifier", snapshot.DBInstanceIdentifier) @@ -207,12 +208,12 @@ func dataSourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta in d.Set(names.AttrStorageType, snapshot.StorageType) d.Set(names.AttrVPCID, snapshot.VpcId) - setTagsOut(ctx, snapshot.TagList) + setTagsOutV2(ctx, snapshot.TagList) return diags } -type rdsSnapshotSort []*rds.DBSnapshot +type rdsSnapshotSort []types.DBSnapshot func (a rdsSnapshotSort) Len() int { return len(a) } func (a rdsSnapshotSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] } @@ -225,11 +226,11 @@ func (a rdsSnapshotSort) Less(i, j int) bool { return false } - return (*a[i].SnapshotCreateTime).Before(*a[j].SnapshotCreateTime) + return (aws.ToTime(a[i].SnapshotCreateTime)).Before(aws.ToTime(a[j].SnapshotCreateTime)) } -func mostRecentDBSnapshot(snapshots []*rds.DBSnapshot) *rds.DBSnapshot { +func mostRecentDBSnapshot(snapshots []types.DBSnapshot) *types.DBSnapshot { sortedSnapshots := snapshots sort.Sort(rdsSnapshotSort(sortedSnapshots)) - return sortedSnapshots[len(sortedSnapshots)-1] + return &sortedSnapshots[len(sortedSnapshots)-1] } From 6451df93d474c8e25e064150eddc51e3ab4fbc9c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 23 Jul 2024 17:30:56 -0400 Subject: [PATCH 12/30] r/aws_db_snapshot_copy: Migrate to AWS SDK for Go v2. --- internal/service/rds/snapshot_copy.go | 48 +++++++++++----------- internal/service/rds/snapshot_copy_test.go | 22 ++++------ 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/internal/service/rds/snapshot_copy.go b/internal/service/rds/snapshot_copy.go index e1b4ab3f1de..405acbc0dc2 100644 --- a/internal/service/rds/snapshot_copy.go +++ b/internal/service/rds/snapshot_copy.go @@ -9,13 +9,14 @@ import ( "time" "github.com/YakDriver/regexache" - "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/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/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -24,7 +25,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_db_snapshot_copy", name="DB Snapshot") +// @SDKResource("aws_db_snapshot_copy", name="DB Snapshot Copy") // @Tags(identifierAttribute="db_snapshot_arn") // @Testing(tagsTest=false) func resourceSnapshotCopy() *schema.Resource { @@ -155,12 +156,12 @@ func resourceSnapshotCopy() *schema.Resource { func resourceSnapshotCopyCreate(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) targetDBSnapshotID := d.Get("target_db_snapshot_identifier").(string) input := &rds.CopyDBSnapshotInput{ SourceDBSnapshotIdentifier: aws.String(d.Get("source_db_snapshot_identifier").(string)), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), TargetDBSnapshotIdentifier: aws.String(targetDBSnapshotID), } @@ -168,9 +169,10 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met input.CopyTags = aws.Bool(v.(bool)) } - if v, ok := d.GetOk("destination_region"); ok { - input.DestinationRegion = aws.String(v.(string)) - } + // TODO + // if v, ok := d.GetOk("destination_region"); ok { + // input.DestinationRegion = aws.String(v.(string)) + // } if v, ok := d.GetOk(names.AttrKMSKeyID); ok { input.KmsKeyId = aws.String(v.(string)) @@ -184,13 +186,13 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met input.PreSignedUrl = aws.String(v.(string)) } - output, err := conn.CopyDBSnapshotWithContext(ctx, input) + output, err := conn.CopyDBSnapshot(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS DB Snapshot Copy (%s): %s", targetDBSnapshotID, err) } - d.SetId(aws.StringValue(output.DBSnapshot.DBSnapshotIdentifier)) + d.SetId(aws.ToString(output.DBSnapshot.DBSnapshotIdentifier)) if _, err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Snapshot Copy (%s) create: %s", d.Id(), err) @@ -200,10 +202,10 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met input := &rds.ModifyDBSnapshotAttributeInput{ AttributeName: aws.String("restore"), DBSnapshotIdentifier: aws.String(d.Id()), - ValuesToAdd: flex.ExpandStringSet(v.(*schema.Set)), + ValuesToAdd: flex.ExpandStringValueSet(v.(*schema.Set)), } - _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) + _, err := conn.ModifyDBSnapshotAttribute(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err) @@ -215,7 +217,7 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met func resourceSnapshotCopyRead(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) snapshot, err := findDBSnapshotByID(ctx, conn, d.Id()) @@ -229,7 +231,7 @@ func resourceSnapshotCopyRead(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot Copy (%s): %s", d.Id(), err) } - arn := aws.StringValue(snapshot.DBSnapshotArn) + arn := aws.ToString(snapshot.DBSnapshotArn) d.Set(names.AttrAllocatedStorage, snapshot.AllocatedStorage) d.Set(names.AttrAvailabilityZone, snapshot.AvailabilityZone) d.Set("db_snapshot_arn", arn) @@ -251,7 +253,7 @@ func resourceSnapshotCopyRead(ctx context.Context, d *schema.ResourceData, meta attribute, err := findDBSnapshotAttributeByTwoPartKey(ctx, conn, d.Id(), dbSnapshotAttributeNameRestore) switch { case err == nil: - d.Set("shared_accounts", flex.FlattenStringSet(attribute.AttributeValues)) + d.Set("shared_accounts", attribute.AttributeValues) case tfresource.NotFound(err): default: return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s) attribute: %s", d.Id(), err) @@ -262,7 +264,7 @@ func resourceSnapshotCopyRead(ctx context.Context, d *schema.ResourceData, meta func resourceSnapshotCopyUpdate(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) if d.HasChange("shared_accounts") { o, n := d.GetChange("shared_accounts") @@ -271,11 +273,11 @@ func resourceSnapshotCopyUpdate(ctx context.Context, d *schema.ResourceData, met input := &rds.ModifyDBSnapshotAttributeInput{ AttributeName: aws.String("restore"), DBSnapshotIdentifier: aws.String(d.Id()), - ValuesToAdd: flex.ExpandStringSet(add), - ValuesToRemove: flex.ExpandStringSet(del), + ValuesToAdd: flex.ExpandStringValueSet(add), + ValuesToRemove: flex.ExpandStringValueSet(del), } - _, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input) + _, err := conn.ModifyDBSnapshotAttribute(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err) @@ -287,14 +289,14 @@ func resourceSnapshotCopyUpdate(ctx context.Context, d *schema.ResourceData, met func resourceSnapshotCopyDelete(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 DB Snapshot Copy: %s", d.Id()) - _, err := conn.DeleteDBSnapshotWithContext(ctx, &rds.DeleteDBSnapshotInput{ + _, err := conn.DeleteDBSnapshot(ctx, &rds.DeleteDBSnapshotInput{ DBSnapshotIdentifier: aws.String(d.Id()), }) - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) { + if errs.IsA[*types.DBSnapshotNotFoundFault](err) { return diags } diff --git a/internal/service/rds/snapshot_copy_test.go b/internal/service/rds/snapshot_copy_test.go index 1c27e41f8a8..a5903fb5a9f 100644 --- a/internal/service/rds/snapshot_copy_test.go +++ b/internal/service/rds/snapshot_copy_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - "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" @@ -25,7 +25,7 @@ func TestAccRDSSnapshotCopy_basic(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var v rds.DBSnapshot + var v types.DBSnapshot resourceName := "aws_db_snapshot_copy.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -58,13 +58,13 @@ func TestAccRDSSnapshotCopy_share(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var v rds.DBSnapshot + var v types.DBSnapshot resourceName := "aws_db_snapshot_copy.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckSnapshotCopyDestroy(ctx), Steps: []resource.TestStep{ @@ -98,7 +98,7 @@ func TestAccRDSSnapshotCopy_tags(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var v rds.DBSnapshot + var v types.DBSnapshot resourceName := "aws_db_snapshot_copy.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -148,7 +148,7 @@ func TestAccRDSSnapshotCopy_disappears(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var v rds.DBSnapshot + var v types.DBSnapshot resourceName := "aws_db_snapshot_copy.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -172,7 +172,7 @@ func TestAccRDSSnapshotCopy_disappears(t *testing.T) { func testAccCheckSnapshotCopyDestroy(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_db_snapshot_copy" { @@ -196,18 +196,14 @@ func testAccCheckSnapshotCopyDestroy(ctx context.Context) resource.TestCheckFunc } } -func testAccCheckSnapshotCopyExists(ctx context.Context, n string, v *rds.DBSnapshot) resource.TestCheckFunc { +func testAccCheckSnapshotCopyExists(ctx context.Context, n string, v *types.DBSnapshot) 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("No RDS DB Snapshot Copy ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindDBSnapshotByID(ctx, conn, rs.Primary.ID) if err != nil { From f061e670c991fca25b27d90bf59c04863d212ff2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 24 Jul 2024 07:52:40 -0400 Subject: [PATCH 13/30] Acceptance test output: % make testacc TESTARGS='-run=TestAccRDSSnapshot_\|TestAccRDSSnapshotCopy_' PKG=rds ACCTEST_PARALLELISM=3 make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/rds/... -v -count 1 -parallel 3 -run=TestAccRDSSnapshot_\|TestAccRDSSnapshotCopy_ -timeout 360m === RUN TestAccRDSSnapshotCopy_basic === PAUSE TestAccRDSSnapshotCopy_basic === RUN TestAccRDSSnapshotCopy_share === PAUSE TestAccRDSSnapshotCopy_share === RUN TestAccRDSSnapshotCopy_tags === PAUSE TestAccRDSSnapshotCopy_tags === RUN TestAccRDSSnapshotCopy_disappears === PAUSE TestAccRDSSnapshotCopy_disappears === RUN TestAccRDSSnapshot_basic === PAUSE TestAccRDSSnapshot_basic === RUN TestAccRDSSnapshot_share === PAUSE TestAccRDSSnapshot_share === RUN TestAccRDSSnapshot_tags === PAUSE TestAccRDSSnapshot_tags === RUN TestAccRDSSnapshot_disappears === PAUSE TestAccRDSSnapshot_disappears === CONT TestAccRDSSnapshotCopy_basic === CONT TestAccRDSSnapshot_basic === CONT TestAccRDSSnapshotCopy_tags --- PASS: TestAccRDSSnapshot_basic (663.28s) === CONT TestAccRDSSnapshotCopy_share --- PASS: TestAccRDSSnapshotCopy_tags (665.45s) === CONT TestAccRDSSnapshotCopy_disappears --- PASS: TestAccRDSSnapshotCopy_basic (857.90s) === CONT TestAccRDSSnapshot_tags --- PASS: TestAccRDSSnapshotCopy_disappears (485.91s) === CONT TestAccRDSSnapshot_disappears --- PASS: TestAccRDSSnapshotCopy_share (507.17s) === CONT TestAccRDSSnapshot_share --- PASS: TestAccRDSSnapshot_tags (468.80s) --- PASS: TestAccRDSSnapshot_disappears (231.80s) --- PASS: TestAccRDSSnapshot_share (228.36s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/rds 1403.830s From 4c72379f18602d5cbc0b0134b17c2b2486daf002 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 24 Jul 2024 09:21:03 -0400 Subject: [PATCH 14/30] Add 'TestAccRDSSnapshotCopy_destinationRegion'. --- internal/service/rds/snapshot_copy_test.go | 55 ++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/internal/service/rds/snapshot_copy_test.go b/internal/service/rds/snapshot_copy_test.go index a5903fb5a9f..1119d40198a 100644 --- a/internal/service/rds/snapshot_copy_test.go +++ b/internal/service/rds/snapshot_copy_test.go @@ -170,6 +170,52 @@ func TestAccRDSSnapshotCopy_disappears(t *testing.T) { }) } +func TestAccRDSSnapshotCopy_destinationRegion(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var v types.DBSnapshot + resourceName := "aws_db_snapshot_copy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5FactoriesAlternate(ctx, t), + CheckDestroy: testAccCheckSnapshotCopyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSnapshotCopyConfig_destinationRegion(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckSnapshotCopyExists(ctx, resourceName, &v), + resource.TestCheckResourceAttrSet(resourceName, names.AttrAllocatedStorage), + resource.TestCheckResourceAttrSet(resourceName, names.AttrAvailabilityZone), + resource.TestCheckResourceAttr(resourceName, "destination_region", acctest.AlternateRegion()), + resource.TestCheckResourceAttr(resourceName, names.AttrEncrypted, acctest.CtFalse), + resource.TestCheckResourceAttrSet(resourceName, names.AttrEngine), + resource.TestCheckResourceAttrSet(resourceName, names.AttrEngineVersion), + resource.TestCheckResourceAttrSet(resourceName, names.AttrIOPS), + resource.TestCheckResourceAttr(resourceName, names.AttrKMSKeyID, ""), + resource.TestCheckResourceAttrSet(resourceName, "license_model"), + resource.TestCheckResourceAttrSet(resourceName, "option_group_name"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrPort), + resource.TestCheckResourceAttrSet(resourceName, "snapshot_type"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrStorageType), + resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_region"}, + }, + }, + }) +} + func testAccCheckSnapshotCopyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) @@ -291,3 +337,12 @@ resource "aws_db_snapshot_copy" "test" { } `, rName)) } + +func testAccSnapshotCopyConfig_destinationRegion(rName string) string { + return acctest.ConfigCompose(testAccSnapshotCopyConfig_base(rName), fmt.Sprintf(` +resource "aws_db_snapshot_copy" "test" { + source_db_snapshot_identifier = aws_db_snapshot.test.db_snapshot_arn + target_db_snapshot_identifier = "%[1]s-target" + destination_region = %[2]q +}`, rName, acctest.AlternateRegion())) +} From 09e90c5c9884299727120125b82cddfa3b12d32e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 24 Jul 2024 09:47:19 -0400 Subject: [PATCH 15/30] Acceptance test output: % make testacc TESTARGS='-run=TestAccRDSSnapshotCopy_destinationRegion' PKG=rds make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.5 test ./internal/service/rds/... -v -count 1 -parallel 20 -run=TestAccRDSSnapshotCopy_destinationRegion -timeout 360m === RUN TestAccRDSSnapshotCopy_destinationRegion === PAUSE TestAccRDSSnapshotCopy_destinationRegion === CONT TestAccRDSSnapshotCopy_destinationRegion --- PASS: TestAccRDSSnapshotCopy_destinationRegion (768.14s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/rds 772.855s From 917a7e9a4417ec93b74f745b35e061be8953452a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 24 Jul 2024 15:33:32 -0400 Subject: [PATCH 16/30] r/aws_db_snapshot: Add '% progress' to 'waitDBSnapshotCreated'. --- internal/service/rds/snapshot.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/service/rds/snapshot.go b/internal/service/rds/snapshot.go index e725e6b3e5d..8bc3e39561a 100644 --- a/internal/service/rds/snapshot.go +++ b/internal/service/rds/snapshot.go @@ -5,6 +5,7 @@ package rds import ( "context" + "fmt" "log" "time" @@ -357,6 +358,7 @@ func waitDBSnapshotCreated(ctx context.Context, conn *rds.Client, id string, tim outputRaw, err := stateConf.WaitForStateContext(ctx) if output, ok := outputRaw.(*types.DBSnapshot); ok { + tfresource.SetLastError(err, fmt.Errorf("%d%% progress", aws.ToInt32(output.PercentProgress))) return output, err } From d5a480cc71d8664ced5261993d037bcbc22fea42 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 24 Jul 2024 16:38:36 -0400 Subject: [PATCH 17/30] r/aws_db_snapshot_copy: Migrate 'destination_region' functionality to AWS SDK for Go v2. --- internal/service/rds/snapshot_copy.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/internal/service/rds/snapshot_copy.go b/internal/service/rds/snapshot_copy.go index 405acbc0dc2..b6a62803240 100644 --- a/internal/service/rds/snapshot_copy.go +++ b/internal/service/rds/snapshot_copy.go @@ -169,11 +169,6 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met input.CopyTags = aws.Bool(v.(bool)) } - // TODO - // if v, ok := d.GetOk("destination_region"); ok { - // input.DestinationRegion = aws.String(v.(string)) - // } - if v, ok := d.GetOk(names.AttrKMSKeyID); ok { input.KmsKeyId = aws.String(v.(string)) } @@ -184,6 +179,18 @@ func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, met if v, ok := d.GetOk("presigned_url"); ok { input.PreSignedUrl = aws.String(v.(string)) + } else if v, ok := d.GetOk("destination_region"); ok { + output, err := rds.NewPresignClient(conn, func(o *rds.PresignOptions) { + o.ClientOptions = append(o.ClientOptions, func(o *rds.Options) { + o.Region = v.(string) + }) + }).PresignCopyDBSnapshot(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "presigning RDS DB Snapshot Copy (%s) request: %s", targetDBSnapshotID, err) + } + + input.PreSignedUrl = aws.String(output.URL) } output, err := conn.CopyDBSnapshot(ctx, input) From 86dd6f8159c86e62ed147744462a81888e0c702e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 24 Jul 2024 17:07:39 -0400 Subject: [PATCH 18/30] d/aws_rds_reserved_instance_offering: Migrate to AWS SDK for Go v2. --- .../reserved_instance_offering_data_source.go | 75 +++++++++++++------ ...rved_instance_offering_data_source_test.go | 1 - internal/service/rds/service_package_gen.go | 4 +- 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/internal/service/rds/reserved_instance_offering_data_source.go b/internal/service/rds/reserved_instance_offering_data_source.go index b7eafc625d6..f5c4ce8f9df 100644 --- a/internal/service/rds/reserved_instance_offering_data_source.go +++ b/internal/service/rds/reserved_instance_offering_data_source.go @@ -8,24 +8,25 @@ import ( "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/rds" + "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-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" + "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/names" ) -const ( - ResNameReservedInstanceOffering = "Reserved Instance Offering" -) - // @SDKDataSource("aws_rds_reserved_instance_offering") -func DataSourceReservedOffering() *schema.Resource { +func dataSourceReservedOffering() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceReservedOfferingRead, + Schema: map[string]*schema.Schema{ "currency_code": { Type: schema.TypeString, @@ -70,7 +71,7 @@ func DataSourceReservedOffering() *schema.Resource { func dataSourceReservedOfferingRead(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) input := &rds.DescribeReservedDBInstancesOfferingsInput{ DBInstanceClass: aws.String(d.Get("db_instance_class").(string)), @@ -80,30 +81,60 @@ func dataSourceReservedOfferingRead(ctx context.Context, d *schema.ResourceData, ProductDescription: aws.String(d.Get("product_description").(string)), } - resp, err := conn.DescribeReservedDBInstancesOfferingsWithContext(ctx, input) - if err != nil { - return create.AppendDiagError(diags, names.RDS, create.ErrActionReading, ResNameReservedInstanceOffering, "unknown", err) - } - - if len(resp.ReservedDBInstancesOfferings) == 0 { - return sdkdiag.AppendErrorf(diags, "no %s %s found matching criteria; try different search", names.RDS, ResNameReservedInstanceOffering) - } + offering, err := findReservedDBInstancesOffering(ctx, conn, input, tfslices.PredicateTrue[*types.ReservedDBInstancesOffering]()) - if len(resp.ReservedDBInstancesOfferings) > 1 { - return sdkdiag.AppendErrorf(diags, "More than one %s %s found matching criteria; try different search", names.RDS, ResNameReservedInstanceOffering) + if err != nil { + return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("RDS Reserved Instance Offering", err)) } - offering := resp.ReservedDBInstancesOfferings[0] - - d.SetId(aws.ToString(offering.ReservedDBInstancesOfferingId)) + offeringID := aws.ToString(offering.ReservedDBInstancesOfferingId) + d.SetId(offeringID) d.Set("currency_code", offering.CurrencyCode) d.Set("db_instance_class", offering.DBInstanceClass) d.Set(names.AttrDuration, offering.Duration) d.Set("fixed_price", offering.FixedPrice) d.Set("multi_az", offering.MultiAZ) + d.Set("offering_id", offeringID) d.Set("offering_type", offering.OfferingType) d.Set("product_description", offering.ProductDescription) - d.Set("offering_id", offering.ReservedDBInstancesOfferingId) return diags } + +func findReservedDBInstancesOffering(ctx context.Context, conn *rds.Client, input *rds.DescribeReservedDBInstancesOfferingsInput, filter tfslices.Predicate[*types.ReservedDBInstancesOffering]) (*types.ReservedDBInstancesOffering, error) { + output, err := findReservedDBInstancesOfferings(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findReservedDBInstancesOfferings(ctx context.Context, conn *rds.Client, input *rds.DescribeReservedDBInstancesOfferingsInput, filter tfslices.Predicate[*types.ReservedDBInstancesOffering]) ([]types.ReservedDBInstancesOffering, error) { + var output []types.ReservedDBInstancesOffering + + pages := rds.NewDescribeReservedDBInstancesOfferingsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*types.ReservedDBInstancesOfferingNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + for _, v := range page.ReservedDBInstancesOfferings { + if filter(&v) { + output = append(output, v) + } + } + } + + return output, nil +} diff --git a/internal/service/rds/reserved_instance_offering_data_source_test.go b/internal/service/rds/reserved_instance_offering_data_source_test.go index 423deb5b6bc..2eb343f8f9a 100644 --- a/internal/service/rds/reserved_instance_offering_data_source_test.go +++ b/internal/service/rds/reserved_instance_offering_data_source_test.go @@ -18,7 +18,6 @@ func TestAccRDSInstanceOffering_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: nil, ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), Steps: []resource.TestStep{ { diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index c2d7e0b18d9..bc54a5fcd23 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -93,7 +93,7 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac TypeName: "aws_rds_orderable_db_instance", }, { - Factory: DataSourceReservedOffering, + Factory: dataSourceReservedOffering, TypeName: "aws_rds_reserved_instance_offering", }, } @@ -186,7 +186,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: resourceSnapshotCopy, TypeName: "aws_db_snapshot_copy", - Name: "DB Snapshot", + Name: "DB Snapshot Copy", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: "db_snapshot_arn", }, From 4a62b96131b6ca3b83f06c84a89f8bf98f9edf5c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 10:40:28 -0400 Subject: [PATCH 19/30] r/aws_db_parameter_group: Migrate to AWS SDK for Go v2. --- internal/service/rds/consts.go | 6 + internal/service/rds/exports_test.go | 3 + internal/service/rds/parameter_group.go | 300 +++++++++++------- internal/service/rds/parameter_group_test.go | 162 +++++----- .../reserved_instance_offering_data_source.go | 2 +- internal/service/rds/service_package_gen.go | 3 +- internal/service/rds/sweep.go | 2 +- 7 files changed, 287 insertions(+), 191 deletions(-) diff --git a/internal/service/rds/consts.go b/internal/service/rds/consts.go index 34927cc6d9b..a4809384b0e 100644 --- a/internal/service/rds/consts.go +++ b/internal/service/rds/consts.go @@ -318,3 +318,9 @@ const ( ReservedInstanceStateRetired = "retired" ReservedInstanceStatePaymentPending = "payment-pending" ) + +const ( + parameterSourceEngineDefault = "engine-default" + parameterSourceSystem = "system" + parameterSourceUser = "user" +) diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index 2e36dc350ca..e900d8ff380 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -9,6 +9,7 @@ var ( ResourceCluster = resourceCluster ResourceClusterSnapshot = resourceClusterSnapshot ResourceEventSubscription = resourceEventSubscription + ResourceParameterGroup = resourceParameterGroup ResourceProxy = resourceProxy ResourceProxyDefaultTargetGroup = resourceProxyDefaultTargetGroup ResourceProxyEndpoint = resourceProxyEndpoint @@ -19,6 +20,7 @@ var ( FindDBClusterSnapshotByID = findDBClusterSnapshotByID FindDBInstanceByID = findDBInstanceByIDSDKv1 + FindDBParameterGroupByName = findDBParameterGroupByName FindDBProxyByName = findDBProxyByName FindDBProxyEndpointByTwoPartKey = findDBProxyEndpointByTwoPartKey FindDBProxyTargetByFourPartKey = findDBProxyTargetByFourPartKey @@ -29,6 +31,7 @@ var ( FindEventSubscriptionByID = findEventSubscriptionByID ListTags = listTags NewBlueGreenOrchestrator = newBlueGreenOrchestrator + ParameterGroupModifyChunk = parameterGroupModifyChunk ParseDBInstanceARN = parseDBInstanceARN ProxyTargetParseResourceID = proxyTargetParseResourceID WaitBlueGreenDeploymentDeleted = waitBlueGreenDeploymentDeleted diff --git a/internal/service/rds/parameter_group.go b/internal/service/rds/parameter_group.go index b870ffe2bc6..5a59d287711 100644 --- a/internal/service/rds/parameter_group.go +++ b/internal/service/rds/parameter_group.go @@ -11,18 +11,19 @@ import ( "strings" "time" - rds_sdkv2 "github.com/aws/aws-sdk-go-v2/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/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/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/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfmaps "github.com/hashicorp/terraform-provider-aws/internal/maps" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -32,7 +33,7 @@ import ( // @SDKResource("aws_db_parameter_group", name="DB Parameter Group") // @Tags(identifierAttribute="arn") // @Testing(tagsTest=false) -func ResourceParameterGroup() *schema.Resource { +func resourceParameterGroup() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceParameterGroupCreate, ReadWithoutTimeout: resourceParameterGroupRead, @@ -81,9 +82,10 @@ func ResourceParameterGroup() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "apply_method": { - Type: schema.TypeString, - Optional: true, - Default: "immediate", + Type: schema.TypeString, + Optional: true, + Default: types.ApplyMethodImmediate, + ValidateDiagFunc: enum.ValidateIgnoreCase[types.ApplyMethod](), }, names.AttrName: { Type: schema.TypeString, @@ -107,24 +109,25 @@ func ResourceParameterGroup() *schema.Resource { func resourceParameterGroupCreate(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) name := create.Name(d.Get(names.AttrName).(string), d.Get(names.AttrNamePrefix).(string)) input := &rds.CreateDBParameterGroupInput{ DBParameterGroupFamily: aws.String(d.Get(names.AttrFamily).(string)), DBParameterGroupName: aws.String(name), Description: aws.String(d.Get(names.AttrDescription).(string)), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), } - output, err := conn.CreateDBParameterGroupWithContext(ctx, input) + output, err := conn.CreateDBParameterGroup(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creatingDB Parameter Group (%s): %s", name, err) + return sdkdiag.AppendErrorf(diags, "creating RDS DB Parameter Group (%s): %s", name, err) } - d.SetId(aws.StringValue(output.DBParameterGroup.DBParameterGroupName)) + d.SetId(aws.ToString(output.DBParameterGroup.DBParameterGroupName)) - // Set for update + // Set for update. d.Set(names.AttrARN, output.DBParameterGroup.DBParameterGroupArn) return append(diags, resourceParameterGroupUpdate(ctx, d, meta)...) @@ -132,9 +135,9 @@ func resourceParameterGroupCreate(ctx context.Context, d *schema.ResourceData, m func resourceParameterGroupRead(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) - dbParameterGroup, err := FindDBParameterGroupByName(ctx, conn, d.Id()) + dbParameterGroup, err := findDBParameterGroupByName(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS DB Parameter Group (%s) not found, removing from state", d.Id()) @@ -146,8 +149,7 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met return sdkdiag.AppendErrorf(diags, "reading RDS DB Parameter Group (%s): %s", d.Id(), err) } - arn := aws.StringValue(dbParameterGroup.DBParameterGroupArn) - d.Set(names.AttrARN, arn) + d.Set(names.AttrARN, dbParameterGroup.DBParameterGroupArn) d.Set(names.AttrDescription, dbParameterGroup.Description) d.Set(names.AttrFamily, dbParameterGroup.DBParameterGroupFamily) d.Set(names.AttrName, dbParameterGroup.DBParameterGroupName) @@ -158,7 +160,7 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met configParams := d.Get(names.AttrParameter).(*schema.Set) if configParams.Len() < 1 { - // if we don't have any params in the ResourceData already, two possibilities + // If we don't have any params in the ResourceData already, two possibilities // first, we don't have a config available to us. Second, we do, but it has // no parameters. We're going to assume the first, to be safe. In this case, // we're only going to ask for the user-modified values, because any defaults @@ -167,26 +169,22 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met // an empty list anyways, so we just make some unnecessary requests. But in // the more common case (I assume) of an import, this will make fewer requests // and "do the right thing". - input.Source = aws.String("user") + input.Source = aws.String(parameterSourceUser) } - var parameters []*rds.Parameter - err = conn.DescribeDBParametersPagesWithContext(ctx, input, func(page *rds.DescribeDBParametersOutput, lastPage bool) bool { - parameters = append(parameters, page.Parameters...) - return !lastPage - }) + parameters, err := findParameters(ctx, conn, input, tfslices.PredicateTrue[*types.Parameter]()) if err != nil { return sdkdiag.AppendErrorf(diags, "reading RDS DB Parameter Group (%s) parameters: %s", d.Id(), err) } - var userParams []*rds.Parameter + var userParams []types.Parameter if configParams.Len() < 1 { - // if we have no config/no parameters in config, we've already asked for only + // If we have no config/no parameters in config, we've already asked for only // user-modified values, so we can just use the entire response. userParams = parameters } else { - // if we have a config available to us, we have two possible classes of value + // If we have a config available to us, we have two possible classes of value // in the config. On the one hand, the user could have specified a parameter // that _actually_ changed things, in which case its Source would be set to // user. On the other, they may have specified a parameter that coincides with @@ -195,32 +193,35 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met // _and_ the "system"/"engine-default" Source parameters _that appear in the // config_ in the state, or the user gets a perpetual diff. See // terraform-providers/terraform-provider-aws#593 for more context and details. - confParams := expandParameters(configParams.List()) - for _, param := range parameters { - if param.Source == nil || param.ParameterName == nil { + for _, parameter := range parameters { + if parameter.Source == nil || parameter.ParameterName == nil { continue } - if aws.StringValue(param.Source) == "user" { - userParams = append(userParams, param) + + if aws.ToString(parameter.Source) == parameterSourceUser { + userParams = append(userParams, parameter) continue } + var paramFound bool - for _, cp := range confParams { + for _, cp := range expandParameters2(configParams.List()) { if cp.ParameterName == nil { continue } - if aws.StringValue(cp.ParameterName) == aws.StringValue(param.ParameterName) { - userParams = append(userParams, param) + + if aws.ToString(cp.ParameterName) == aws.ToString(parameter.ParameterName) { + userParams = append(userParams, parameter) + paramFound = true break } } if !paramFound { - log.Printf("[DEBUG] Not persisting %s to state, as its source is %q and it isn't in the config", aws.StringValue(param.ParameterName), aws.StringValue(param.Source)) + log.Printf("[DEBUG] Not persisting %s to state, as its source is %q and it isn't in the config", aws.ToString(parameter.ParameterName), aws.ToString(parameter.Source)) } } } - if err := d.Set(names.AttrParameter, flattenParameters(userParams)); err != nil { + if err := d.Set(names.AttrParameter, flattenParameters2(userParams)); err != nil { return sdkdiag.AppendErrorf(diags, "setting parameter: %s", err) } @@ -232,65 +233,50 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m maxParamModifyChunk = 20 ) var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) if d.HasChange(names.AttrParameter) { o, n := d.GetChange(names.AttrParameter) - if o == nil { - o = new(schema.Set) - } - if n == nil { - n = new(schema.Set) - } - - os := o.(*schema.Set) - ns := n.(*schema.Set) + os, ns := o.(*schema.Set), n.(*schema.Set) - // Expand the "parameter" set to aws-sdk-go compat []rds.Parameter - parameters := expandParameters(ns.Difference(os).List()) - - if len(parameters) > 0 { + if parameters := expandParameters2(ns.Difference(os).List()); len(parameters) > 0 { // We can only modify 20 parameters at a time, so walk them until // we've got them all. - for parameters != nil { - var paramsToModify []*rds.Parameter - paramsToModify, parameters = ResourceParameterModifyChunk(parameters, maxParamModifyChunk) + var paramsToModify []types.Parameter + paramsToModify, parameters = parameterGroupModifyChunk(parameters, maxParamModifyChunk) input := &rds.ModifyDBParameterGroupInput{ DBParameterGroupName: aws.String(d.Id()), Parameters: paramsToModify, } - _, err := conn.ModifyDBParameterGroupWithContext(ctx, input) + _, err := conn.ModifyDBParameterGroup(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "modifying DB Parameter Group (%s): %s", d.Id(), err) } } } - toRemove := map[string]*rds.Parameter{} + toRemove := map[string]types.Parameter{} - for _, p := range expandParameters(os.List()) { + for _, p := range expandParameters2(os.List()) { if p.ParameterName != nil { - toRemove[*p.ParameterName] = p + toRemove[aws.ToString(p.ParameterName)] = p } } - for _, p := range expandParameters(ns.List()) { + for _, p := range expandParameters2(ns.List()) { if p.ParameterName != nil { - delete(toRemove, *p.ParameterName) + delete(toRemove, aws.ToString(p.ParameterName)) } } - // Reset parameters that have been removed - var resetParameters []*rds.Parameter - for _, v := range toRemove { - resetParameters = append(resetParameters, v) - } - if len(resetParameters) > 0 { + // Reset parameters that have been removed. + if resetParameters := tfmaps.Values(toRemove); len(resetParameters) > 0 { for resetParameters != nil { - var paramsToReset []*rds.Parameter + var paramsToReset []types.Parameter if len(resetParameters) <= maxParamModifyChunk { paramsToReset, resetParameters = resetParameters[:], nil } else { @@ -303,7 +289,8 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m ResetAllParameters: aws.Bool(false), } - _, err := conn.ResetDBParameterGroupWithContext(ctx, input) + _, err := conn.ResetDBParameterGroup(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "resetting DB Parameter Group (%s): %s", d.Id(), err) } @@ -314,70 +301,115 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m return append(diags, resourceParameterGroupRead(ctx, d, meta)...) } -func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { +func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSClient(ctx) - input := &rds_sdkv2.DeleteDBParameterGroupInput{ - DBParameterGroupName: aws.String(d.Id()), - } log.Printf("[DEBUG] Deleting RDS DB Parameter Group: %s", d.Id()) - err := retry.RetryContext(ctx, 3*time.Minute, func() *retry.RetryError { - _, err := conn.DeleteDBParameterGroup(ctx, input) - if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { - return nil - } else if errs.IsA[*types.InvalidDBParameterGroupStateFault](err) { - return retry.RetryableError(err) - } - if err != nil { - return retry.NonRetryableError(err) - } - return nil + const ( + timeout = 3 * time.Minute + ) + _, err := tfresource.RetryWhenIsA[*types.InvalidDBParameterGroupStateFault](ctx, timeout, func() (interface{}, error) { + return conn.DeleteDBParameterGroup(ctx, &rds.DeleteDBParameterGroupInput{ + DBParameterGroupName: aws.String(d.Id()), + }) }) - if tfresource.TimedOut(err) { - _, err = conn.DeleteDBParameterGroup(ctx, input) + + if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { + return diags } + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting RDS DB Parameter Group (%s): %s", d.Id(), err) } + return diags } -func FindDBParameterGroupByName(ctx context.Context, conn *rds.RDS, name string) (*rds.DBParameterGroup, error) { +func findDBParameterGroupByName(ctx context.Context, conn *rds.Client, name string) (*types.DBParameterGroup, error) { input := &rds.DescribeDBParameterGroupsInput{ DBParameterGroupName: aws.String(name), } + output, err := findDBParameterGroup(ctx, conn, input, tfslices.PredicateTrue[*types.DBParameterGroup]()) - output, err := conn.DescribeDBParameterGroupsWithContext(ctx, input) + if err != nil { + return nil, err + } - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { + // Eventual consistency check. + if aws.ToString(output.DBParameterGroupName) != name { return nil, &retry.NotFoundError{ - LastError: err, LastRequest: input, } } + return output, nil +} + +func findDBParameterGroup(ctx context.Context, conn *rds.Client, input *rds.DescribeDBParameterGroupsInput, filter tfslices.Predicate[*types.DBParameterGroup]) (*types.DBParameterGroup, error) { + output, err := findDBParameterGroups(ctx, conn, input, filter) + if err != nil { return nil, err } - if output == nil || len(output.DBParameterGroups) == 0 || output.DBParameterGroups[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } + return tfresource.AssertSingleValueResult(output) +} + +func findDBParameterGroups(ctx context.Context, conn *rds.Client, input *rds.DescribeDBParameterGroupsInput, filter tfslices.Predicate[*types.DBParameterGroup]) ([]types.DBParameterGroup, error) { + var output []types.DBParameterGroup + + pages := rds.NewDescribeDBParameterGroupsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } - if count := len(output.DBParameterGroups); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) + for _, v := range page.DBParameterGroups { + if filter(&v) { + output = append(output, v) + } + } } - dbParameterGroup := output.DBParameterGroups[0] + return output, nil +} - // Eventual consistency check. - if aws.StringValue(dbParameterGroup.DBParameterGroupName) != name { - return nil, &retry.NotFoundError{ - LastRequest: input, +func findParameters(ctx context.Context, conn *rds.Client, input *rds.DescribeDBParametersInput, filter tfslices.Predicate[*types.Parameter]) ([]types.Parameter, error) { + var output []types.Parameter + + pages := rds.NewDescribeDBParametersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + for _, v := range page.Parameters { + if filter(&v) { + output = append(output, v) + } } } - return dbParameterGroup, nil + return output, nil } func resourceParameterHash(v interface{}) int { @@ -393,15 +425,15 @@ func resourceParameterHash(v interface{}) int { return create.StringHashcode(buf.String()) } -func ResourceParameterModifyChunk(all []*rds.Parameter, maxChunkSize int) ([]*rds.Parameter, []*rds.Parameter) { +func parameterGroupModifyChunk(all []types.Parameter, maxChunkSize int) ([]types.Parameter, []types.Parameter) { // Since the hash randomly affect the set "order," this attempts to prioritize important - // parameters to go in the first chunk (i.e., charset) + // parameters to go in the first chunk (i.e., charset). if len(all) <= maxChunkSize { return all[:], nil } - var modifyChunk, remainder []*rds.Parameter + var modifyChunk, remainder []types.Parameter // pass 1 for i, p := range all { @@ -410,7 +442,7 @@ func ResourceParameterModifyChunk(all []*rds.Parameter, maxChunkSize int) ([]*rd return modifyChunk, remainder } - if strings.Contains(aws.StringValue(p.ParameterName), "character_set") && aws.StringValue(p.ApplyMethod) != "pending-reboot" { + if strings.Contains(aws.ToString(p.ParameterName), "character_set") && p.ApplyMethod != types.ApplyMethodPendingReboot { modifyChunk = append(modifyChunk, p) continue } @@ -428,7 +460,7 @@ func ResourceParameterModifyChunk(all []*rds.Parameter, maxChunkSize int) ([]*rd return modifyChunk, remainder } - if aws.StringValue(p.ApplyMethod) != "pending-reboot" { + if p.ApplyMethod != types.ApplyMethodPendingReboot { modifyChunk = append(modifyChunk, p) continue } @@ -451,3 +483,59 @@ func ResourceParameterModifyChunk(all []*rds.Parameter, maxChunkSize int) ([]*rd return modifyChunk, remainder } + +// TODO Rename once aws_rds_cluster_parameter_group has been migrated. +func expandParameters2(tfList []interface{}) []types.Parameter { + var apiObjects []types.Parameter + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } + + if tfMap[names.AttrName].(string) == "" { + continue + } + + apiObject := types.Parameter{ + ParameterName: aws.String(strings.ToLower(tfMap[names.AttrName].(string))), + ParameterValue: aws.String(tfMap[names.AttrValue].(string)), + } + + if v, ok := tfMap["apply_method"].(string); ok && v != "" { + apiObject.ApplyMethod = types.ApplyMethod(strings.ToLower(v)) + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects +} + +// TODO Rename once aws_rds_cluster_parameter_group has been migrated. +func flattenParameters2(apiObject []types.Parameter) []interface{} { + apiObjects := make([]interface{}, 0) + + for _, apiObject := range apiObject { + if apiObject.ParameterName == nil { + continue + } + + tfMap := make(map[string]interface{}) + + tfMap["apply_method"] = strings.ToLower(string(apiObject.ApplyMethod)) + tfMap[names.AttrName] = strings.ToLower(aws.ToString(apiObject.ParameterName)) + + // Default empty string, guard against nil parameter values. + tfMap[names.AttrValue] = "" + if apiObject.ParameterValue != nil { + tfMap[names.AttrValue] = aws.ToString(apiObject.ParameterValue) + } + + apiObjects = append(apiObjects, tfMap) + + } + + return apiObjects +} diff --git a/internal/service/rds/parameter_group_test.go b/internal/service/rds/parameter_group_test.go index f89d2e39c4b..47c2cda0bea 100644 --- a/internal/service/rds/parameter_group_test.go +++ b/internal/service/rds/parameter_group_test.go @@ -10,8 +10,9 @@ import ( "testing" "github.com/YakDriver/regexache" - "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" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -24,7 +25,7 @@ import ( func TestAccRDSParameterGroup_basic(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -119,7 +120,7 @@ func TestAccRDSParameterGroup_basic(t *testing.T) { func TestAccRDSParameterGroup_disappears(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -143,7 +144,7 @@ func TestAccRDSParameterGroup_disappears(t *testing.T) { func TestAccRDSParameterGroup_tags(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -207,7 +208,7 @@ func TestAccRDSParameterGroup_caseWithMixedParameters(t *testing.T) { func TestAccRDSParameterGroup_limit(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -576,7 +577,7 @@ func TestAccRDSParameterGroup_limit(t *testing.T) { func TestAccRDSParameterGroup_namePrefix(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -597,7 +598,7 @@ func TestAccRDSParameterGroup_namePrefix(t *testing.T) { func TestAccRDSParameterGroup_generatedName(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -617,7 +618,7 @@ func TestAccRDSParameterGroup_generatedName(t *testing.T) { func TestAccRDSParameterGroup_withApplyMethod(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -658,7 +659,7 @@ func TestAccRDSParameterGroup_withApplyMethod(t *testing.T) { func TestAccRDSParameterGroup_only(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -688,7 +689,7 @@ func TestAccRDSParameterGroup_only(t *testing.T) { func TestAccRDSParameterGroup_matchDefault(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -718,7 +719,7 @@ func TestAccRDSParameterGroup_matchDefault(t *testing.T) { func TestAccRDSParameterGroup_updateParameters(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -779,7 +780,7 @@ func TestAccRDSParameterGroup_updateParameters(t *testing.T) { func TestAccRDSParameterGroup_caseParameters(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBParameterGroup + var v types.DBParameterGroup resourceName := "aws_db_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -814,15 +815,15 @@ func TestAccRDSParameterGroup_caseParameters(t *testing.T) { }) } -func TestDBParameterModifyChunk(t *testing.T) { +func TestParameterGroupModifyChunk(t *testing.T) { t.Parallel() cases := []struct { Name string ChunkSize int - Parameters []*rds.Parameter - ExpectedModify []*rds.Parameter - ExpectedRemainder []*rds.Parameter + Parameters []types.Parameter + ExpectedModify []types.Parameter + ExpectedRemainder []types.Parameter }{ { Name: "Empty", @@ -834,26 +835,26 @@ func TestDBParameterModifyChunk(t *testing.T) { { Name: "A couple", ChunkSize: 20, - Parameters: []*rds.Parameter{ + Parameters: []types.Parameter{ { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("tx_isolation"), ParameterValue: aws.String("repeatable-read"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("binlog_cache_size"), ParameterValue: aws.String("131072"), }, }, - ExpectedModify: []*rds.Parameter{ + ExpectedModify: []types.Parameter{ { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("tx_isolation"), ParameterValue: aws.String("repeatable-read"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("binlog_cache_size"), ParameterValue: aws.String("131072"), }, @@ -863,68 +864,68 @@ func TestDBParameterModifyChunk(t *testing.T) { { Name: "Over 3 max, 6 in", ChunkSize: 3, - Parameters: []*rds.Parameter{ + Parameters: []types.Parameter{ { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("tx_isolation"), ParameterValue: aws.String("repeatable-read"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("binlog_cache_size"), ParameterValue: aws.String("131072"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("innodb_read_io_threads"), ParameterValue: aws.String("64"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("character_set_server"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("innodb_flush_log_at_trx_commit"), ParameterValue: aws.String(acctest.Ct0), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("character_set_filesystem"), ParameterValue: aws.String("utf8"), }, }, - ExpectedModify: []*rds.Parameter{ + ExpectedModify: []types.Parameter{ { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("character_set_server"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("character_set_filesystem"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("tx_isolation"), ParameterValue: aws.String("repeatable-read"), }, }, - ExpectedRemainder: []*rds.Parameter{ + ExpectedRemainder: []types.Parameter{ { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("binlog_cache_size"), ParameterValue: aws.String("131072"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("innodb_read_io_threads"), ParameterValue: aws.String("64"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("innodb_flush_log_at_trx_commit"), ParameterValue: aws.String(acctest.Ct0), }, @@ -933,98 +934,98 @@ func TestDBParameterModifyChunk(t *testing.T) { { Name: "Over 3 max, 9 in", ChunkSize: 3, - Parameters: []*rds.Parameter{ + Parameters: []types.Parameter{ { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("tx_isolation"), ParameterValue: aws.String("repeatable-read"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("binlog_cache_size"), ParameterValue: aws.String("131072"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("innodb_read_io_threads"), ParameterValue: aws.String("64"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("character_set_server"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("innodb_flush_log_at_trx_commit"), ParameterValue: aws.String(acctest.Ct0), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("character_set_filesystem"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("innodb_max_dirty_pages_pct"), ParameterValue: aws.String("90"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("character_set_connection"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("key_buffer_size"), ParameterValue: aws.String("67108864"), }, }, - ExpectedModify: []*rds.Parameter{ + ExpectedModify: []types.Parameter{ { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("character_set_filesystem"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("character_set_connection"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("innodb_flush_log_at_trx_commit"), ParameterValue: aws.String(acctest.Ct0), }, }, - ExpectedRemainder: []*rds.Parameter{ + ExpectedRemainder: []types.Parameter{ { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("tx_isolation"), ParameterValue: aws.String("repeatable-read"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("binlog_cache_size"), ParameterValue: aws.String("131072"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("innodb_read_io_threads"), ParameterValue: aws.String("64"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("character_set_server"), ParameterValue: aws.String("utf8"), }, { - ApplyMethod: aws.String("pending-reboot"), + ApplyMethod: types.ApplyMethodPendingReboot, ParameterName: aws.String("innodb_max_dirty_pages_pct"), ParameterValue: aws.String("90"), }, { - ApplyMethod: aws.String("immediate"), + ApplyMethod: types.ApplyMethodImmediate, ParameterName: aws.String("key_buffer_size"), ParameterValue: aws.String("67108864"), }, @@ -1033,7 +1034,7 @@ func TestDBParameterModifyChunk(t *testing.T) { } for _, tc := range cases { - mod, rem := tfrds.ResourceParameterModifyChunk(tc.Parameters, tc.ChunkSize) + mod, rem := tfrds.ParameterGroupModifyChunk(tc.Parameters, tc.ChunkSize) if !reflect.DeepEqual(mod, tc.ExpectedModify) { t.Errorf("Case %q: Modify did not match\n%#v\n\nGot:\n%#v", tc.Name, tc.ExpectedModify, mod) } @@ -1045,7 +1046,7 @@ func TestDBParameterModifyChunk(t *testing.T) { func testAccCheckParameterGroupDestroy(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_db_parameter_group" { @@ -1069,33 +1070,29 @@ func testAccCheckParameterGroupDestroy(ctx context.Context) resource.TestCheckFu } } -func testAccCheckParameterGroupAttributes(v *rds.DBParameterGroup, name string) resource.TestCheckFunc { +func testAccCheckParameterGroupAttributes(v *types.DBParameterGroup, name string) resource.TestCheckFunc { return func(s *terraform.State) error { if *v.DBParameterGroupName != name { return fmt.Errorf("Bad Parameter Group name, expected (%s), got (%s)", name, *v.DBParameterGroupName) } family := "mysql5.6" - if aws.StringValue(v.DBParameterGroupFamily) != family { - return fmt.Errorf("bad family, got: %s, expecting: %s", aws.StringValue(v.DBParameterGroupFamily), family) + if aws.ToString(v.DBParameterGroupFamily) != family { + return fmt.Errorf("bad family, got: %s, expecting: %s", aws.ToString(v.DBParameterGroupFamily), family) } return nil } } -func testAccCheckParameterGroupExists(ctx context.Context, n string, v *rds.DBParameterGroup) resource.TestCheckFunc { +func testAccCheckParameterGroupExists(ctx context.Context, n string, v *types.DBParameterGroup) 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("No RDS DB Parameter Group ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindDBParameterGroupByName(ctx, conn, rs.Primary.ID) if err != nil { @@ -1115,33 +1112,34 @@ func testAccCheckParameterNotUserDefined(ctx context.Context, rName, paramName s return fmt.Errorf("Not found: %s", rName) } - if rs.Primary.ID == "" { - return fmt.Errorf("No DB Parameter Group ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) - opts := rds.DescribeDBParametersInput{ + input := &rds.DescribeDBParametersInput{ DBParameterGroupName: aws.String(rs.Primary.ID), Source: aws.String("user"), } userDefined := false - err := conn.DescribeDBParametersPagesWithContext(ctx, &opts, func(page *rds.DescribeDBParametersOutput, lastPage bool) bool { + pages := rds.NewDescribeDBParametersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if err != nil { + return err + } + for _, param := range page.Parameters { - if *param.ParameterName == paramName { + if aws.ToString(param.ParameterName) == paramName { userDefined = true - return false } } - return true - }) + } if userDefined { return fmt.Errorf("DB Parameter is user defined") } - return err + return nil } } diff --git a/internal/service/rds/reserved_instance_offering_data_source.go b/internal/service/rds/reserved_instance_offering_data_source.go index f5c4ce8f9df..d9b9b5eb01f 100644 --- a/internal/service/rds/reserved_instance_offering_data_source.go +++ b/internal/service/rds/reserved_instance_offering_data_source.go @@ -22,7 +22,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_rds_reserved_instance_offering") +// @SDKDataSource("aws_rds_reserved_instance_offering", name="Reserved Instance Offering") func dataSourceReservedOffering() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceReservedOfferingRead, diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index bc54a5fcd23..af633584d6d 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -95,6 +95,7 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac { Factory: dataSourceReservedOffering, TypeName: "aws_rds_reserved_instance_offering", + Name: "Reserved Instance Offering", }, } } @@ -142,7 +143,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceParameterGroup, + Factory: resourceParameterGroup, TypeName: "aws_db_parameter_group", Name: "DB Parameter Group", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/rds/sweep.go b/internal/service/rds/sweep.go index bd73266e1ee..726f5bdf7c2 100644 --- a/internal/service/rds/sweep.go +++ b/internal/service/rds/sweep.go @@ -484,7 +484,7 @@ func sweepParameterGroups(region string) error { continue } - r := ResourceParameterGroup() + r := resourceParameterGroup() d := r.Data(nil) d.SetId(name) From 24a397bd213fbf60d622093b65513d024794409e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 11:02:40 -0400 Subject: [PATCH 20/30] Fix 'TestAccRDSParameterGroup_caseParameters'. --- internal/service/rds/parameter_group_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/service/rds/parameter_group_test.go b/internal/service/rds/parameter_group_test.go index 47c2cda0bea..8807f972f77 100644 --- a/internal/service/rds/parameter_group_test.go +++ b/internal/service/rds/parameter_group_test.go @@ -804,9 +804,10 @@ func TestAccRDSParameterGroup_caseParameters(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"parameter.0.name"}, }, { Config: testAccParameterGroupConfig_upperCase(rName, "max_connections"), From 18f2da310003b31ff8f60993b286c77bc37279fb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 11:31:53 -0400 Subject: [PATCH 21/30] r/aws_rds_cluster_parameter_group: Migrate to AWS SDK for Go v2. --- .../service/rds/cluster_parameter_group.go | 246 ++++++++++-------- .../rds/cluster_parameter_group_test.go | 76 +++--- internal/service/rds/exports_test.go | 2 + internal/service/rds/parameter_group.go | 8 +- internal/service/rds/parameter_group_test.go | 1 + internal/service/rds/service_package_gen.go | 2 +- internal/service/rds/sweep.go | 2 +- 7 files changed, 179 insertions(+), 158 deletions(-) diff --git a/internal/service/rds/cluster_parameter_group.go b/internal/service/rds/cluster_parameter_group.go index 257bbb50eaa..734caa81b15 100644 --- a/internal/service/rds/cluster_parameter_group.go +++ b/internal/service/rds/cluster_parameter_group.go @@ -6,18 +6,18 @@ package rds import ( "context" "log" + "slices" "time" - rds_sdkv2 "github.com/aws/aws-sdk-go-v2/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/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/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/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/maps" @@ -31,7 +31,7 @@ import ( // @SDKResource("aws_rds_cluster_parameter_group", name="Cluster Parameter Group") // @Tags(identifierAttribute="arn") // @Testing(tagsTest=false) -func ResourceClusterParameterGroup() *schema.Resource { +func resourceClusterParameterGroup() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceClusterParameterGroupCreate, ReadWithoutTimeout: resourceClusterParameterGroupRead, @@ -80,9 +80,10 @@ func ResourceClusterParameterGroup() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "apply_method": { - Type: schema.TypeString, - Optional: true, - Default: "immediate", + Type: schema.TypeString, + Optional: true, + Default: types.ApplyMethodImmediate, + ValidateDiagFunc: enum.ValidateIgnoreCase[types.ApplyMethod](), }, names.AttrName: { Type: schema.TypeString, @@ -106,24 +107,25 @@ func ResourceClusterParameterGroup() *schema.Resource { func resourceClusterParameterGroupCreate(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) - groupName := create.Name(d.Get(names.AttrName).(string), d.Get(names.AttrNamePrefix).(string)) + name := create.Name(d.Get(names.AttrName).(string), d.Get(names.AttrNamePrefix).(string)) input := &rds.CreateDBClusterParameterGroupInput{ - DBClusterParameterGroupName: aws.String(groupName), + DBClusterParameterGroupName: aws.String(name), DBParameterGroupFamily: aws.String(d.Get(names.AttrFamily).(string)), Description: aws.String(d.Get(names.AttrDescription).(string)), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), } - output, err := conn.CreateDBClusterParameterGroupWithContext(ctx, input) + output, err := conn.CreateDBClusterParameterGroup(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating DB Cluster Parameter Group (%s): %s", groupName, err) + return sdkdiag.AppendErrorf(diags, "creating RDS Cluster Parameter Group (%s): %s", name, err) } - d.SetId(groupName) + d.SetId(name) - // Set for update + // Set for update. d.Set(names.AttrARN, output.DBClusterParameterGroup.DBClusterParameterGroupArn) return append(diags, resourceClusterParameterGroupUpdate(ctx, d, meta)...) @@ -131,86 +133,64 @@ func resourceClusterParameterGroupCreate(ctx context.Context, d *schema.Resource func resourceClusterParameterGroupRead(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) - dbClusterParameterGroup, err := FindDBClusterParameterGroupByName(ctx, conn, d.Id()) + dbClusterParameterGroup, err := findDBClusterParameterGroupByName(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] RDS DB Cluster Parameter Group (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] RDS Cluster Parameter Group (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS DB Cluster Parameter Group (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading RDS Cluster Parameter Group (%s): %s", d.Id(), err) } - arn := aws.StringValue(dbClusterParameterGroup.DBClusterParameterGroupArn) - d.Set(names.AttrARN, arn) + d.Set(names.AttrARN, dbClusterParameterGroup.DBClusterParameterGroupArn) d.Set(names.AttrDescription, dbClusterParameterGroup.Description) d.Set(names.AttrFamily, dbClusterParameterGroup.DBParameterGroupFamily) d.Set(names.AttrName, dbClusterParameterGroup.DBClusterParameterGroupName) - d.Set(names.AttrNamePrefix, create.NamePrefixFromName(aws.StringValue(dbClusterParameterGroup.DBClusterParameterGroupName))) + d.Set(names.AttrNamePrefix, create.NamePrefixFromName(aws.ToString(dbClusterParameterGroup.DBClusterParameterGroupName))) - // Only include user customized parameters as there's hundreds of system/default ones + // Only include user customized parameters as there's hundreds of system/default ones. input := &rds.DescribeDBClusterParametersInput{ DBClusterParameterGroupName: aws.String(d.Id()), - Source: aws.String("user"), + Source: aws.String(parameterSourceUser), } - var parameters []*rds.Parameter - - err = conn.DescribeDBClusterParametersPagesWithContext(ctx, input, func(page *rds.DescribeDBClusterParametersOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - for _, v := range page.Parameters { - if v != nil { - parameters = append(parameters, v) - } - } - return !lastPage - }) + parameters, err := findDBClusterParameters(ctx, conn, input, tfslices.PredicateTrue[*types.Parameter]()) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS Cluster Parameter Group (%s) parameters: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading RDS Cluster Parameter Group (%s) user parameters: %s", d.Id(), err) } - // add only system parameters that are set in the config + // Add only system parameters that are set in the config. p := d.Get(names.AttrParameter) if p == nil { p = new(schema.Set) } s := p.(*schema.Set) - configParameters := expandParameters(s.List()) + configParameters := expandParameters2(s.List()) input = &rds.DescribeDBClusterParametersInput{ DBClusterParameterGroupName: aws.String(d.Id()), - Source: aws.String("system"), + Source: aws.String(parameterSourceSystem), } - err = conn.DescribeDBClusterParametersPagesWithContext(ctx, input, func(page *rds.DescribeDBClusterParametersOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - for _, v := range page.Parameters { - for _, p := range configParameters { - if aws.StringValue(v.ParameterName) == aws.StringValue(p.ParameterName) { - parameters = append(parameters, v) - } - } - } - - return !lastPage + systemParameters, err := findDBClusterParameters(ctx, conn, input, func(v *types.Parameter) bool { + return slices.ContainsFunc(configParameters, func(p types.Parameter) bool { + return aws.ToString(p.ParameterName) == aws.ToString(v.ParameterName) + }) }) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS Cluster Parameter Group (%s) parameters: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading RDS Cluster Parameter Group (%s) system parameters: %s", d.Id(), err) } - if err := d.Set(names.AttrParameter, flattenParameters(parameters)); err != nil { + parameters = append(parameters, systemParameters...) + + if err := d.Set(names.AttrParameter, flattenParameters2(parameters)); err != nil { return sdkdiag.AppendErrorf(diags, "setting parameter: %s", err) } @@ -222,44 +202,36 @@ func resourceClusterParameterGroupUpdate(ctx context.Context, d *schema.Resource maxParamModifyChunk = 20 ) var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) if d.HasChange(names.AttrParameter) { o, n := d.GetChange(names.AttrParameter) - if o == nil { - o = new(schema.Set) - } - if n == nil { - n = new(schema.Set) - } - - os := o.(*schema.Set) - ns := n.(*schema.Set) + os, ns := o.(*schema.Set), n.(*schema.Set) - // Expand the "parameter" set to aws-sdk-go compat []rds.Parameter. - for _, chunk := range tfslices.Chunks(expandParameters(ns.Difference(os).List()), maxParamModifyChunk) { + for _, chunk := range tfslices.Chunks(expandParameters2(ns.Difference(os).List()), maxParamModifyChunk) { input := &rds.ModifyDBClusterParameterGroupInput{ DBClusterParameterGroupName: aws.String(d.Id()), Parameters: chunk, } - _, err := conn.ModifyDBClusterParameterGroupWithContext(ctx, input) + _, err := conn.ModifyDBClusterParameterGroup(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "modifying DB Cluster Parameter Group (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "modifying RDS Cluster Parameter Group (%s): %s", d.Id(), err) } } - toRemove := map[string]*rds.Parameter{} + toRemove := map[string]types.Parameter{} - for _, p := range expandParameters(os.List()) { + for _, p := range expandParameters2(os.List()) { if p.ParameterName != nil { - toRemove[*p.ParameterName] = p + toRemove[aws.ToString(p.ParameterName)] = p } } - for _, p := range expandParameters(ns.List()) { + for _, p := range expandParameters2(ns.List()) { if p.ParameterName != nil { - delete(toRemove, *p.ParameterName) + delete(toRemove, aws.ToString(p.ParameterName)) } } @@ -271,12 +243,15 @@ func resourceClusterParameterGroupUpdate(ctx context.Context, d *schema.Resource ResetAllParameters: aws.Bool(false), } - _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, 3*time.Minute, func() (interface{}, error) { - return conn.ResetDBClusterParameterGroupWithContext(ctx, input) - }, rds.ErrCodeInvalidDBParameterGroupStateFault, "has pending changes") + const ( + timeout = 3 * time.Minute + ) + _, err := tfresource.RetryWhenIsAErrorMessageContains[*types.InvalidDBParameterGroupStateFault](ctx, timeout, func() (interface{}, error) { + return conn.ResetDBClusterParameterGroup(ctx, input) + }, "has pending changes") if err != nil { - return sdkdiag.AppendErrorf(diags, "resetting DB Cluster Parameter Group (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "resetting RDS Cluster Parameter Group (%s): %s", d.Id(), err) } } } @@ -288,25 +263,18 @@ func resourceClusterParameterGroupDelete(ctx context.Context, d *schema.Resource var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSClient(ctx) - input := &rds_sdkv2.DeleteDBClusterParameterGroupInput{ - DBClusterParameterGroupName: aws.String(d.Id()), - } - - log.Printf("[DEBUG] Deleting RDS DB Cluster Parameter Group: %s", d.Id()) - err := retry.RetryContext(ctx, 3*time.Minute, func() *retry.RetryError { - _, err := conn.DeleteDBClusterParameterGroup(ctx, input) - if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { - return nil - } else if errs.IsA[*types.InvalidDBParameterGroupStateFault](err) { - return retry.RetryableError(err) - } - if err != nil { - return retry.NonRetryableError(err) - } - return nil + log.Printf("[DEBUG] Deleting RDS Cluster Parameter Group: %s", d.Id()) + const ( + timeout = 3 * time.Minute + ) + _, err := tfresource.RetryWhenIsA[*types.InvalidDBParameterGroupStateFault](ctx, timeout, func() (interface{}, error) { + return conn.DeleteDBClusterParameterGroup(ctx, &rds.DeleteDBClusterParameterGroupInput{ + DBClusterParameterGroupName: aws.String(d.Id()), + }) }) - if tfresource.TimedOut(err) { - _, err = conn.DeleteDBClusterParameterGroup(ctx, input) + + if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { + return diags } if err != nil { @@ -316,40 +284,88 @@ func resourceClusterParameterGroupDelete(ctx context.Context, d *schema.Resource return diags } -func FindDBClusterParameterGroupByName(ctx context.Context, conn *rds.RDS, name string) (*rds.DBClusterParameterGroup, error) { +func findDBClusterParameterGroupByName(ctx context.Context, conn *rds.Client, name string) (*types.DBClusterParameterGroup, error) { input := &rds.DescribeDBClusterParameterGroupsInput{ DBClusterParameterGroupName: aws.String(name), } + output, err := findDBClusterParameterGroup(ctx, conn, input, tfslices.PredicateTrue[*types.DBClusterParameterGroup]()) - output, err := conn.DescribeDBClusterParameterGroupsWithContext(ctx, input) + if err != nil { + return nil, err + } - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { + // Eventual consistency check. + if aws.ToString(output.DBClusterParameterGroupName) != name { return nil, &retry.NotFoundError{ - LastError: err, LastRequest: input, } } + return output, nil +} + +func findDBClusterParameterGroup(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClusterParameterGroupsInput, filter tfslices.Predicate[*types.DBClusterParameterGroup]) (*types.DBClusterParameterGroup, error) { + output, err := findDBClusterParameterGroups(ctx, conn, input, filter) + if err != nil { return nil, err } - if output == nil || len(output.DBClusterParameterGroups) == 0 || output.DBClusterParameterGroups[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } + return tfresource.AssertSingleValueResult(output) +} + +func findDBClusterParameterGroups(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClusterParameterGroupsInput, filter tfslices.Predicate[*types.DBClusterParameterGroup]) ([]types.DBClusterParameterGroup, error) { + var output []types.DBClusterParameterGroup + + pages := rds.NewDescribeDBClusterParameterGroupsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } - if count := len(output.DBClusterParameterGroups); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) + for _, v := range page.DBClusterParameterGroups { + if filter(&v) { + output = append(output, v) + } + } } - dbClusterParameterGroup := output.DBClusterParameterGroups[0] + return output, nil +} - // Eventual consistency check. - if aws.StringValue(dbClusterParameterGroup.DBClusterParameterGroupName) != name { - return nil, &retry.NotFoundError{ - LastRequest: input, +func findDBClusterParameters(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClusterParametersInput, filter tfslices.Predicate[*types.Parameter]) ([]types.Parameter, error) { + var output []types.Parameter + + pages := rds.NewDescribeDBClusterParametersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + for _, v := range page.Parameters { + if filter(&v) { + output = append(output, v) + } } } - return dbClusterParameterGroup, nil + return output, nil } diff --git a/internal/service/rds/cluster_parameter_group_test.go b/internal/service/rds/cluster_parameter_group_test.go index 7f5c19e66a5..c64ab96c52b 100644 --- a/internal/service/rds/cluster_parameter_group_test.go +++ b/internal/service/rds/cluster_parameter_group_test.go @@ -5,12 +5,12 @@ package rds_test import ( "context" - "errors" "fmt" "testing" - "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/terraform-plugin-sdk/v2/helper/id" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -24,7 +24,7 @@ import ( func TestAccRDSClusterParameterGroup_basic(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -123,7 +123,7 @@ func TestAccRDSClusterParameterGroup_basic(t *testing.T) { func TestAccRDSClusterParameterGroup_disappears(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -147,7 +147,7 @@ func TestAccRDSClusterParameterGroup_disappears(t *testing.T) { func TestAccRDSClusterParameterGroup_tags(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -193,7 +193,7 @@ func TestAccRDSClusterParameterGroup_tags(t *testing.T) { func TestAccRDSClusterParameterGroup_withApplyMethod(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster_parameter_group.test" @@ -235,7 +235,7 @@ func TestAccRDSClusterParameterGroup_withApplyMethod(t *testing.T) { func TestAccRDSClusterParameterGroup_namePrefix(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -263,7 +263,7 @@ func TestAccRDSClusterParameterGroup_namePrefix(t *testing.T) { func TestAccRDSClusterParameterGroup_NamePrefix_parameter(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -291,7 +291,7 @@ func TestAccRDSClusterParameterGroup_NamePrefix_parameter(t *testing.T) { func TestAccRDSClusterParameterGroup_generatedName(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -319,7 +319,7 @@ func TestAccRDSClusterParameterGroup_generatedName(t *testing.T) { func TestAccRDSClusterParameterGroup_GeneratedName_parameter(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -347,7 +347,7 @@ func TestAccRDSClusterParameterGroup_GeneratedName_parameter(t *testing.T) { func TestAccRDSClusterParameterGroup_only(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -378,7 +378,7 @@ func TestAccRDSClusterParameterGroup_only(t *testing.T) { func TestAccRDSClusterParameterGroup_updateParameters(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -439,7 +439,7 @@ func TestAccRDSClusterParameterGroup_updateParameters(t *testing.T) { func TestAccRDSClusterParameterGroup_caseParameters(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -476,7 +476,7 @@ func TestAccRDSClusterParameterGroup_caseParameters(t *testing.T) { func TestAccRDSClusterParameterGroup_dynamicDiffs(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBClusterParameterGroup + var v types.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -515,7 +515,7 @@ func TestAccRDSClusterParameterGroup_dynamicDiffs(t *testing.T) { func testAccCheckClusterParameterGroupDestroy(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_parameter_group" { @@ -532,7 +532,7 @@ func testAccCheckClusterParameterGroupDestroy(ctx context.Context) resource.Test return err } - return fmt.Errorf("RDS DB Cluster Parameter Group %s still exists", rs.Primary.ID) + return fmt.Errorf("RDS Cluster Parameter Group %s still exists", rs.Primary.ID) } return nil @@ -546,33 +546,38 @@ func testAccCheckClusterParameterNotUserDefined(ctx context.Context, n, paramNam return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No DB Parameter Group ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) - opts := rds.DescribeDBClusterParametersInput{ + input := &rds.DescribeDBClusterParametersInput{ DBClusterParameterGroupName: aws.String(rs.Primary.ID), } userDefined := false - out, err := conn.DescribeDBClusterParametersWithContext(ctx, &opts) - for _, param := range out.Parameters { - if *param.ParameterName == paramName && aws.StringValue(param.ParameterValue) != "" { - // Some of these resets leave the parameter name present but with a nil value - userDefined = true + pages := rds.NewDescribeDBClusterParametersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if err != nil { + return err + } + + for _, param := range page.Parameters { + if aws.ToString(param.ParameterName) == paramName && aws.ToString(param.ParameterValue) != "" { + // Some of these resets leave the parameter name present but with a nil value. + userDefined = true + } } } if userDefined { - return fmt.Errorf("DB Parameter %s is user defined", paramName) + return fmt.Errorf("Cluster Parameter %s is user defined", paramName) } - return err + + return nil } } -func testAccCheckClusterParameterGroupAttributes(v *rds.DBClusterParameterGroup, name string) resource.TestCheckFunc { +func testAccCheckClusterParameterGroupAttributes(v *types.DBClusterParameterGroup, name string) resource.TestCheckFunc { return func(s *terraform.State) error { if *v.DBClusterParameterGroupName != name { return fmt.Errorf("bad name: %#v expected: %v", *v.DBClusterParameterGroupName, name) @@ -586,20 +591,17 @@ func testAccCheckClusterParameterGroupAttributes(v *rds.DBClusterParameterGroup, } } -func testAccCheckClusterParameterGroupExists(ctx context.Context, n string, v *rds.DBClusterParameterGroup) resource.TestCheckFunc { +func testAccCheckClusterParameterGroupExists(ctx context.Context, n string, v *types.DBClusterParameterGroup) 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 errors.New("No RDS DB Cluster Parameter Group ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindDBClusterParameterGroupByName(ctx, conn, rs.Primary.ID) + if err != nil { return err } diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index e900d8ff380..c05e98103c7 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -7,6 +7,7 @@ package rds var ( ResourceCertificate = resourceCertificate ResourceCluster = resourceCluster + ResourceClusterParameterGroup = resourceClusterParameterGroup ResourceClusterSnapshot = resourceClusterSnapshot ResourceEventSubscription = resourceEventSubscription ResourceParameterGroup = resourceParameterGroup @@ -18,6 +19,7 @@ var ( ResourceSnapshotCopy = resourceSnapshotCopy ResourceSubnetGroup = resourceSubnetGroup + FindDBClusterParameterGroupByName = findDBClusterParameterGroupByName FindDBClusterSnapshotByID = findDBClusterSnapshotByID FindDBInstanceByID = findDBInstanceByIDSDKv1 FindDBParameterGroupByName = findDBParameterGroupByName diff --git a/internal/service/rds/parameter_group.go b/internal/service/rds/parameter_group.go index 5a59d287711..2e2de6de8b7 100644 --- a/internal/service/rds/parameter_group.go +++ b/internal/service/rds/parameter_group.go @@ -172,7 +172,7 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met input.Source = aws.String(parameterSourceUser) } - parameters, err := findParameters(ctx, conn, input, tfslices.PredicateTrue[*types.Parameter]()) + parameters, err := findDBParameters(ctx, conn, input, tfslices.PredicateTrue[*types.Parameter]()) if err != nil { return sdkdiag.AppendErrorf(diags, "reading RDS DB Parameter Group (%s) parameters: %s", d.Id(), err) @@ -254,7 +254,7 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m _, err := conn.ModifyDBParameterGroup(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "modifying DB Parameter Group (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "modifying RDS DB Parameter Group (%s): %s", d.Id(), err) } } } @@ -292,7 +292,7 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m _, err := conn.ResetDBParameterGroup(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "resetting DB Parameter Group (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "resetting RDS DB Parameter Group (%s): %s", d.Id(), err) } } } @@ -384,7 +384,7 @@ func findDBParameterGroups(ctx context.Context, conn *rds.Client, input *rds.Des return output, nil } -func findParameters(ctx context.Context, conn *rds.Client, input *rds.DescribeDBParametersInput, filter tfslices.Predicate[*types.Parameter]) ([]types.Parameter, error) { +func findDBParameters(ctx context.Context, conn *rds.Client, input *rds.DescribeDBParametersInput, filter tfslices.Predicate[*types.Parameter]) ([]types.Parameter, error) { var output []types.Parameter pages := rds.NewDescribeDBParametersPaginator(conn, input) diff --git a/internal/service/rds/parameter_group_test.go b/internal/service/rds/parameter_group_test.go index 8807f972f77..c4af565f1f9 100644 --- a/internal/service/rds/parameter_group_test.go +++ b/internal/service/rds/parameter_group_test.go @@ -1096,6 +1096,7 @@ func testAccCheckParameterGroupExists(ctx context.Context, n string, v *types.DB conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindDBParameterGroupByName(ctx, conn, rs.Primary.ID) + if err != nil { return err } diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index af633584d6d..78910dadd0d 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -234,7 +234,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceClusterParameterGroup, + Factory: resourceClusterParameterGroup, TypeName: "aws_rds_cluster_parameter_group", Name: "Cluster Parameter Group", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/rds/sweep.go b/internal/service/rds/sweep.go index 726f5bdf7c2..24d3109d55a 100644 --- a/internal/service/rds/sweep.go +++ b/internal/service/rds/sweep.go @@ -131,7 +131,7 @@ func sweepClusterParameterGroups(region string) error { continue } - r := ResourceClusterParameterGroup() + r := resourceClusterParameterGroup() d := r.Data(nil) d.SetId(name) From 682661264a4d152a93c6e82c0ae7fafd7197f9b5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 11:43:05 -0400 Subject: [PATCH 22/30] rds: Move flatteners and expanders around. --- internal/service/rds/cluster.go | 128 +++++++ .../service/rds/cluster_parameter_group.go | 10 +- internal/service/rds/flex.go | 351 ++---------------- internal/service/rds/flex_test.go | 86 ----- internal/service/rds/option_group.go | 163 ++++++++ internal/service/rds/parameter_group.go | 66 +--- 6 files changed, 331 insertions(+), 473 deletions(-) delete mode 100644 internal/service/rds/flex_test.go diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index d80f9302918..152cb715b53 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -1901,3 +1901,131 @@ func waitDBClusterDeleted(ctx context.Context, conn *rds.RDS, id string, timeout return nil, err } + +func expandScalingConfiguration(tfMap map[string]interface{}) *rds.ScalingConfiguration { + if tfMap == nil { + return nil + } + + apiObject := &rds.ScalingConfiguration{} + + if v, ok := tfMap["auto_pause"].(bool); ok { + apiObject.AutoPause = aws.Bool(v) + } + + if v, ok := tfMap[names.AttrMaxCapacity].(int); ok { + apiObject.MaxCapacity = aws.Int64(int64(v)) + } + + if v, ok := tfMap["min_capacity"].(int); ok { + apiObject.MinCapacity = aws.Int64(int64(v)) + } + + if v, ok := tfMap["seconds_before_timeout"].(int); ok { + apiObject.SecondsBeforeTimeout = aws.Int64(int64(v)) + } + + if v, ok := tfMap["seconds_until_auto_pause"].(int); ok { + apiObject.SecondsUntilAutoPause = aws.Int64(int64(v)) + } + + if v, ok := tfMap["timeout_action"].(string); ok && v != "" { + apiObject.TimeoutAction = aws.String(v) + } + + return apiObject +} + +func flattenScalingConfigurationInfo(apiObject *rds.ScalingConfigurationInfo) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.AutoPause; v != nil { + tfMap["auto_pause"] = aws.BoolValue(v) + } + + if v := apiObject.MaxCapacity; v != nil { + tfMap[names.AttrMaxCapacity] = aws.Int64Value(v) + } + + if v := apiObject.MaxCapacity; v != nil { + tfMap[names.AttrMaxCapacity] = aws.Int64Value(v) + } + + if v := apiObject.MinCapacity; v != nil { + tfMap["min_capacity"] = aws.Int64Value(v) + } + + if v := apiObject.SecondsBeforeTimeout; v != nil { + tfMap["seconds_before_timeout"] = aws.Int64Value(v) + } + + if v := apiObject.SecondsUntilAutoPause; v != nil { + tfMap["seconds_until_auto_pause"] = aws.Int64Value(v) + } + + if v := apiObject.TimeoutAction; v != nil { + tfMap["timeout_action"] = aws.StringValue(v) + } + + return tfMap +} + +func expandServerlessV2ScalingConfiguration(tfMap map[string]interface{}) *rds.ServerlessV2ScalingConfiguration { + if tfMap == nil { + return nil + } + + apiObject := &rds.ServerlessV2ScalingConfiguration{} + + if v, ok := tfMap[names.AttrMaxCapacity].(float64); ok && v != 0.0 { + apiObject.MaxCapacity = aws.Float64(v) + } + + if v, ok := tfMap["min_capacity"].(float64); ok && v != 0.0 { + apiObject.MinCapacity = aws.Float64(v) + } + + return apiObject +} + +func flattenServerlessV2ScalingConfigurationInfo(apiObject *rds.ServerlessV2ScalingConfigurationInfo) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.MaxCapacity; v != nil { + tfMap[names.AttrMaxCapacity] = aws.Float64Value(v) + } + + if v := apiObject.MinCapacity; v != nil { + tfMap["min_capacity"] = aws.Float64Value(v) + } + + return tfMap +} + +// TODO Move back to 'flex.go' once migrate to AWS SDK for Go v2. +func flattenManagedMasterUserSecret(apiObject *rds.MasterUserSecret) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + if v := apiObject.KmsKeyId; v != nil { + tfMap[names.AttrKMSKeyID] = aws.StringValue(v) + } + if v := apiObject.SecretArn; v != nil { + tfMap["secret_arn"] = aws.StringValue(v) + } + if v := apiObject.SecretStatus; v != nil { + tfMap["secret_status"] = aws.StringValue(v) + } + + return tfMap +} diff --git a/internal/service/rds/cluster_parameter_group.go b/internal/service/rds/cluster_parameter_group.go index 734caa81b15..4cdc5534b82 100644 --- a/internal/service/rds/cluster_parameter_group.go +++ b/internal/service/rds/cluster_parameter_group.go @@ -171,7 +171,7 @@ func resourceClusterParameterGroupRead(ctx context.Context, d *schema.ResourceDa p = new(schema.Set) } s := p.(*schema.Set) - configParameters := expandParameters2(s.List()) + configParameters := expandParameters(s.List()) input = &rds.DescribeDBClusterParametersInput{ DBClusterParameterGroupName: aws.String(d.Id()), @@ -190,7 +190,7 @@ func resourceClusterParameterGroupRead(ctx context.Context, d *schema.ResourceDa parameters = append(parameters, systemParameters...) - if err := d.Set(names.AttrParameter, flattenParameters2(parameters)); err != nil { + if err := d.Set(names.AttrParameter, flattenParameters(parameters)); err != nil { return sdkdiag.AppendErrorf(diags, "setting parameter: %s", err) } @@ -208,7 +208,7 @@ func resourceClusterParameterGroupUpdate(ctx context.Context, d *schema.Resource o, n := d.GetChange(names.AttrParameter) os, ns := o.(*schema.Set), n.(*schema.Set) - for _, chunk := range tfslices.Chunks(expandParameters2(ns.Difference(os).List()), maxParamModifyChunk) { + for _, chunk := range tfslices.Chunks(expandParameters(ns.Difference(os).List()), maxParamModifyChunk) { input := &rds.ModifyDBClusterParameterGroupInput{ DBClusterParameterGroupName: aws.String(d.Id()), Parameters: chunk, @@ -223,13 +223,13 @@ func resourceClusterParameterGroupUpdate(ctx context.Context, d *schema.Resource toRemove := map[string]types.Parameter{} - for _, p := range expandParameters2(os.List()) { + for _, p := range expandParameters(os.List()) { if p.ParameterName != nil { toRemove[aws.ToString(p.ParameterName)] = p } } - for _, p := range expandParameters2(ns.List()) { + for _, p := range expandParameters(ns.List()) { if p.ParameterName != nil { delete(toRemove, aws.ToString(p.ParameterName)) } diff --git a/internal/service/rds/flex.go b/internal/service/rds/flex.go index 892118e908c..db7d3e9a7dc 100644 --- a/internal/service/rds/flex.go +++ b/internal/service/rds/flex.go @@ -6,352 +6,61 @@ package rds import ( "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds/types" "github.com/hashicorp/terraform-provider-aws/names" ) -func expandScalingConfiguration(tfMap map[string]interface{}) *rds.ScalingConfiguration { - if tfMap == nil { - return nil - } - - apiObject := &rds.ScalingConfiguration{} - - if v, ok := tfMap["auto_pause"].(bool); ok { - apiObject.AutoPause = aws.Bool(v) - } - - if v, ok := tfMap[names.AttrMaxCapacity].(int); ok { - apiObject.MaxCapacity = aws.Int64(int64(v)) - } - - if v, ok := tfMap["min_capacity"].(int); ok { - apiObject.MinCapacity = aws.Int64(int64(v)) - } - - if v, ok := tfMap["seconds_before_timeout"].(int); ok { - apiObject.SecondsBeforeTimeout = aws.Int64(int64(v)) - } - - if v, ok := tfMap["seconds_until_auto_pause"].(int); ok { - apiObject.SecondsUntilAutoPause = aws.Int64(int64(v)) - } - - if v, ok := tfMap["timeout_action"].(string); ok && v != "" { - apiObject.TimeoutAction = aws.String(v) - } - - return apiObject -} - -func flattenManagedMasterUserSecret(apiObject *rds.MasterUserSecret) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - if v := apiObject.KmsKeyId; v != nil { - tfMap[names.AttrKMSKeyID] = aws.StringValue(v) - } - if v := apiObject.SecretArn; v != nil { - tfMap["secret_arn"] = aws.StringValue(v) - } - if v := apiObject.SecretStatus; v != nil { - tfMap["secret_status"] = aws.StringValue(v) - } - - return tfMap -} - -func flattenScalingConfigurationInfo(apiObject *rds.ScalingConfigurationInfo) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.AutoPause; v != nil { - tfMap["auto_pause"] = aws.BoolValue(v) - } - - if v := apiObject.MaxCapacity; v != nil { - tfMap[names.AttrMaxCapacity] = aws.Int64Value(v) - } - - if v := apiObject.MaxCapacity; v != nil { - tfMap[names.AttrMaxCapacity] = aws.Int64Value(v) - } - - if v := apiObject.MinCapacity; v != nil { - tfMap["min_capacity"] = aws.Int64Value(v) - } - - if v := apiObject.SecondsBeforeTimeout; v != nil { - tfMap["seconds_before_timeout"] = aws.Int64Value(v) - } - - if v := apiObject.SecondsUntilAutoPause; v != nil { - tfMap["seconds_until_auto_pause"] = aws.Int64Value(v) - } - - if v := apiObject.TimeoutAction; v != nil { - tfMap["timeout_action"] = aws.StringValue(v) - } - - return tfMap -} - -func expandServerlessV2ScalingConfiguration(tfMap map[string]interface{}) *rds.ServerlessV2ScalingConfiguration { - if tfMap == nil { - return nil - } - - apiObject := &rds.ServerlessV2ScalingConfiguration{} - - if v, ok := tfMap[names.AttrMaxCapacity].(float64); ok && v != 0.0 { - apiObject.MaxCapacity = aws.Float64(v) - } - - if v, ok := tfMap["min_capacity"].(float64); ok && v != 0.0 { - apiObject.MinCapacity = aws.Float64(v) - } - - return apiObject -} - -func flattenServerlessV2ScalingConfigurationInfo(apiObject *rds.ServerlessV2ScalingConfigurationInfo) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.MaxCapacity; v != nil { - tfMap[names.AttrMaxCapacity] = aws.Float64Value(v) - } +func expandParameters(tfList []interface{}) []types.Parameter { + var apiObjects []types.Parameter - if v := apiObject.MinCapacity; v != nil { - tfMap["min_capacity"] = aws.Float64Value(v) - } - - return tfMap -} - -func expandOptionConfiguration(configured []interface{}) []*rds.OptionConfiguration { - var option []*rds.OptionConfiguration - - for _, pRaw := range configured { - data := pRaw.(map[string]interface{}) - - o := &rds.OptionConfiguration{ - OptionName: aws.String(data["option_name"].(string)), - } - - if raw, ok := data[names.AttrPort]; ok { - port := raw.(int) - if port != 0 { - o.Port = aws.Int64(int64(port)) - } - } - - if raw, ok := data["db_security_group_memberships"]; ok { - memberships := flex.ExpandStringSet(raw.(*schema.Set)) - if len(memberships) > 0 { - o.DBSecurityGroupMemberships = memberships - } - } - - if raw, ok := data["vpc_security_group_memberships"]; ok { - memberships := flex.ExpandStringSet(raw.(*schema.Set)) - if len(memberships) > 0 { - o.VpcSecurityGroupMemberships = memberships - } - } - - if raw, ok := data["option_settings"]; ok { - o.OptionSettings = expandOptionSetting(raw.(*schema.Set).List()) - } - - if raw, ok := data[names.AttrVersion]; ok && raw.(string) != "" { - o.OptionVersion = aws.String(raw.(string)) - } - - option = append(option, o) - } - - return option -} - -// Flattens an array of Options into a []map[string]interface{} -func flattenOptions(apiOptions []*rds.Option, optionConfigurations []*rds.OptionConfiguration) []map[string]interface{} { - result := make([]map[string]interface{}, 0) - - for _, apiOption := range apiOptions { - if apiOption == nil || apiOption.OptionName == nil { + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { continue } - var configuredOption *rds.OptionConfiguration - - for _, optionConfiguration := range optionConfigurations { - if aws.StringValue(apiOption.OptionName) == aws.StringValue(optionConfiguration.OptionName) { - configuredOption = optionConfiguration - break - } - } - - dbSecurityGroupMemberships := make([]interface{}, 0) - for _, db := range apiOption.DBSecurityGroupMemberships { - if db != nil { - dbSecurityGroupMemberships = append(dbSecurityGroupMemberships, aws.StringValue(db.DBSecurityGroupName)) - } - } - - optionSettings := make([]interface{}, 0) - for _, apiOptionSetting := range apiOption.OptionSettings { - // The RDS API responds with all settings. Omit settings that match default value, - // but only if unconfigured. This is to prevent operators from continually needing - // to continually update their Terraform configurations to match new option settings - // when added by the API. - var configuredOptionSetting *rds.OptionSetting - - if configuredOption != nil { - for _, configuredOptionOptionSetting := range configuredOption.OptionSettings { - if aws.StringValue(apiOptionSetting.Name) == aws.StringValue(configuredOptionOptionSetting.Name) { - configuredOptionSetting = configuredOptionOptionSetting - break - } - } - } - - if configuredOptionSetting == nil && aws.StringValue(apiOptionSetting.Value) == aws.StringValue(apiOptionSetting.DefaultValue) { - continue - } - - optionSetting := map[string]interface{}{ - names.AttrName: aws.StringValue(apiOptionSetting.Name), - names.AttrValue: aws.StringValue(apiOptionSetting.Value), - } - - // Some values, like passwords, are sent back from the API as ****. - // Set the response to match the configuration to prevent an unexpected difference - if configuredOptionSetting != nil && aws.StringValue(apiOptionSetting.Value) == "****" { - optionSetting[names.AttrValue] = aws.StringValue(configuredOptionSetting.Value) - } - - optionSettings = append(optionSettings, optionSetting) - } - optionSettingsResource := &schema.Resource{ - Schema: map[string]*schema.Schema{ - names.AttrName: { - Type: schema.TypeString, - Required: true, - }, - names.AttrValue: { - Type: schema.TypeString, - Required: true, - }, - }, - } - - vpcSecurityGroupMemberships := make([]interface{}, 0) - for _, vpc := range apiOption.VpcSecurityGroupMemberships { - if vpc != nil { - vpcSecurityGroupMemberships = append(vpcSecurityGroupMemberships, aws.StringValue(vpc.VpcSecurityGroupId)) - } - } - - r := map[string]interface{}{ - "db_security_group_memberships": schema.NewSet(schema.HashString, dbSecurityGroupMemberships), - "option_name": aws.StringValue(apiOption.OptionName), - "option_settings": schema.NewSet(schema.HashResource(optionSettingsResource), optionSettings), - "vpc_security_group_memberships": schema.NewSet(schema.HashString, vpcSecurityGroupMemberships), - } - - if apiOption.OptionVersion != nil && configuredOption != nil && configuredOption.OptionVersion != nil { - r[names.AttrVersion] = aws.StringValue(apiOption.OptionVersion) + if tfMap[names.AttrName].(string) == "" { + continue } - if apiOption.Port != nil && configuredOption != nil && configuredOption.Port != nil { - r[names.AttrPort] = aws.Int64Value(apiOption.Port) + apiObject := types.Parameter{ + ParameterName: aws.String(strings.ToLower(tfMap[names.AttrName].(string))), + ParameterValue: aws.String(tfMap[names.AttrValue].(string)), } - result = append(result, r) - } - - return result -} - -func expandOptionSetting(list []interface{}) []*rds.OptionSetting { - options := make([]*rds.OptionSetting, 0, len(list)) - - for _, oRaw := range list { - data := oRaw.(map[string]interface{}) - - o := &rds.OptionSetting{ - Name: aws.String(data[names.AttrName].(string)), - Value: aws.String(data[names.AttrValue].(string)), + if v, ok := tfMap["apply_method"].(string); ok && v != "" { + apiObject.ApplyMethod = types.ApplyMethod(strings.ToLower(v)) } - options = append(options, o) + apiObjects = append(apiObjects, apiObject) } - return options + return apiObjects } -// Takes the result of flatmap.Expand for an array of parameters and -// returns Parameter API compatible objects -func expandParameters(configured []interface{}) []*rds.Parameter { - var parameters []*rds.Parameter +func flattenParameters(apiObject []types.Parameter) []interface{} { + apiObjects := make([]interface{}, 0) - // Loop over our configured parameters and create - // an array of aws-sdk-go compatible objects - for _, pRaw := range configured { - data := pRaw.(map[string]interface{}) - - if data[names.AttrName].(string) == "" { + for _, apiObject := range apiObject { + if apiObject.ParameterName == nil { continue } - p := &rds.Parameter{ - ParameterName: aws.String(strings.ToLower(data[names.AttrName].(string))), - ParameterValue: aws.String(data[names.AttrValue].(string)), - } - - if data["apply_method"].(string) != "" { - p.ApplyMethod = aws.String(strings.ToLower(data["apply_method"].(string))) - } - - parameters = append(parameters, p) - } + tfMap := make(map[string]interface{}) - return parameters -} + tfMap["apply_method"] = strings.ToLower(string(apiObject.ApplyMethod)) + tfMap[names.AttrName] = strings.ToLower(aws.ToString(apiObject.ParameterName)) -// Flattens an array of Parameters into a []map[string]interface{} -func flattenParameters(list []*rds.Parameter) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(list)) - for _, i := range list { - if i.ParameterName != nil { - r := make(map[string]interface{}) - if i.ApplyMethod != nil { - r["apply_method"] = strings.ToLower(aws.StringValue(i.ApplyMethod)) - } - - r[names.AttrName] = strings.ToLower(aws.StringValue(i.ParameterName)) + // Default empty string, guard against nil parameter values. + tfMap[names.AttrValue] = "" + if apiObject.ParameterValue != nil { + tfMap[names.AttrValue] = aws.ToString(apiObject.ParameterValue) + } - // Default empty string, guard against nil parameter values - r[names.AttrValue] = "" - if i.ParameterValue != nil { - r[names.AttrValue] = aws.StringValue(i.ParameterValue) - } + apiObjects = append(apiObjects, tfMap) - result = append(result, r) - } } - return result + return apiObjects } diff --git a/internal/service/rds/flex_test.go b/internal/service/rds/flex_test.go deleted file mode 100644 index 75492b43d70..00000000000 --- a/internal/service/rds/flex_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package rds - -import ( - "reflect" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/terraform-provider-aws/names" -) - -func TestExpandParameters(t *testing.T) { - t.Parallel() - - expanded := []interface{}{ - map[string]interface{}{ - names.AttrName: "character_set_client", - names.AttrValue: "utf8", - "apply_method": "immediate", - }, - } - parameters := expandParameters(expanded) - - expected := &rds.Parameter{ - ParameterName: aws.String("character_set_client"), - ParameterValue: aws.String("utf8"), - ApplyMethod: aws.String("immediate"), - } - - if !reflect.DeepEqual(parameters[0], expected) { - t.Fatalf( - "Got:\n\n%#v\n\nExpected:\n\n%#v\n", - parameters[0], - expected) - } -} - -func TestFlattenParameters(t *testing.T) { - t.Parallel() - - cases := []struct { - Input []*rds.Parameter - Output []map[string]interface{} - }{ - { - Input: []*rds.Parameter{ - { - ParameterName: aws.String("character_set_client"), - ParameterValue: aws.String("utf8"), - }, - }, - Output: []map[string]interface{}{ - { - names.AttrName: "character_set_client", - names.AttrValue: "utf8", - }, - }, - }, - { - Input: []*rds.Parameter{ - { - ParameterName: aws.String("character_set_client"), - ParameterValue: aws.String("utf8"), - ApplyMethod: aws.String("immediate"), - }, - }, - Output: []map[string]interface{}{ - { - names.AttrName: "character_set_client", - names.AttrValue: "utf8", - "apply_method": "immediate", - }, - }, - }, - } - - for _, tc := range cases { - output := flattenParameters(tc.Input) - if !reflect.DeepEqual(output, tc.Output) { - t.Fatalf("Got:\n\n%#v\n\nExpected:\n\n%#v", output, tc.Output) - } - } -} diff --git a/internal/service/rds/option_group.go b/internal/service/rds/option_group.go index 0eba392459b..fb390899940 100644 --- a/internal/service/rds/option_group.go +++ b/internal/service/rds/option_group.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -344,3 +345,165 @@ func flattenOptionNames(configured []interface{}) []*string { return optionNames } + +func expandOptionConfiguration(configured []interface{}) []*rds.OptionConfiguration { + var option []*rds.OptionConfiguration + + for _, pRaw := range configured { + data := pRaw.(map[string]interface{}) + + o := &rds.OptionConfiguration{ + OptionName: aws.String(data["option_name"].(string)), + } + + if raw, ok := data[names.AttrPort]; ok { + port := raw.(int) + if port != 0 { + o.Port = aws.Int64(int64(port)) + } + } + + if raw, ok := data["db_security_group_memberships"]; ok { + memberships := flex.ExpandStringSet(raw.(*schema.Set)) + if len(memberships) > 0 { + o.DBSecurityGroupMemberships = memberships + } + } + + if raw, ok := data["vpc_security_group_memberships"]; ok { + memberships := flex.ExpandStringSet(raw.(*schema.Set)) + if len(memberships) > 0 { + o.VpcSecurityGroupMemberships = memberships + } + } + + if raw, ok := data["option_settings"]; ok { + o.OptionSettings = expandOptionSetting(raw.(*schema.Set).List()) + } + + if raw, ok := data[names.AttrVersion]; ok && raw.(string) != "" { + o.OptionVersion = aws.String(raw.(string)) + } + + option = append(option, o) + } + + return option +} + +// Flattens an array of Options into a []map[string]interface{} +func flattenOptions(apiOptions []*rds.Option, optionConfigurations []*rds.OptionConfiguration) []map[string]interface{} { + result := make([]map[string]interface{}, 0) + + for _, apiOption := range apiOptions { + if apiOption == nil || apiOption.OptionName == nil { + continue + } + + var configuredOption *rds.OptionConfiguration + + for _, optionConfiguration := range optionConfigurations { + if aws.StringValue(apiOption.OptionName) == aws.StringValue(optionConfiguration.OptionName) { + configuredOption = optionConfiguration + break + } + } + + dbSecurityGroupMemberships := make([]interface{}, 0) + for _, db := range apiOption.DBSecurityGroupMemberships { + if db != nil { + dbSecurityGroupMemberships = append(dbSecurityGroupMemberships, aws.StringValue(db.DBSecurityGroupName)) + } + } + + optionSettings := make([]interface{}, 0) + for _, apiOptionSetting := range apiOption.OptionSettings { + // The RDS API responds with all settings. Omit settings that match default value, + // but only if unconfigured. This is to prevent operators from continually needing + // to continually update their Terraform configurations to match new option settings + // when added by the API. + var configuredOptionSetting *rds.OptionSetting + + if configuredOption != nil { + for _, configuredOptionOptionSetting := range configuredOption.OptionSettings { + if aws.StringValue(apiOptionSetting.Name) == aws.StringValue(configuredOptionOptionSetting.Name) { + configuredOptionSetting = configuredOptionOptionSetting + break + } + } + } + + if configuredOptionSetting == nil && aws.StringValue(apiOptionSetting.Value) == aws.StringValue(apiOptionSetting.DefaultValue) { + continue + } + + optionSetting := map[string]interface{}{ + names.AttrName: aws.StringValue(apiOptionSetting.Name), + names.AttrValue: aws.StringValue(apiOptionSetting.Value), + } + + // Some values, like passwords, are sent back from the API as ****. + // Set the response to match the configuration to prevent an unexpected difference + if configuredOptionSetting != nil && aws.StringValue(apiOptionSetting.Value) == "****" { + optionSetting[names.AttrValue] = aws.StringValue(configuredOptionSetting.Value) + } + + optionSettings = append(optionSettings, optionSetting) + } + optionSettingsResource := &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrName: { + Type: schema.TypeString, + Required: true, + }, + names.AttrValue: { + Type: schema.TypeString, + Required: true, + }, + }, + } + + vpcSecurityGroupMemberships := make([]interface{}, 0) + for _, vpc := range apiOption.VpcSecurityGroupMemberships { + if vpc != nil { + vpcSecurityGroupMemberships = append(vpcSecurityGroupMemberships, aws.StringValue(vpc.VpcSecurityGroupId)) + } + } + + r := map[string]interface{}{ + "db_security_group_memberships": schema.NewSet(schema.HashString, dbSecurityGroupMemberships), + "option_name": aws.StringValue(apiOption.OptionName), + "option_settings": schema.NewSet(schema.HashResource(optionSettingsResource), optionSettings), + "vpc_security_group_memberships": schema.NewSet(schema.HashString, vpcSecurityGroupMemberships), + } + + if apiOption.OptionVersion != nil && configuredOption != nil && configuredOption.OptionVersion != nil { + r[names.AttrVersion] = aws.StringValue(apiOption.OptionVersion) + } + + if apiOption.Port != nil && configuredOption != nil && configuredOption.Port != nil { + r[names.AttrPort] = aws.Int64Value(apiOption.Port) + } + + result = append(result, r) + } + + return result +} + +func expandOptionSetting(list []interface{}) []*rds.OptionSetting { + options := make([]*rds.OptionSetting, 0, len(list)) + + for _, oRaw := range list { + data := oRaw.(map[string]interface{}) + + o := &rds.OptionSetting{ + Name: aws.String(data[names.AttrName].(string)), + Value: aws.String(data[names.AttrValue].(string)), + } + + options = append(options, o) + } + + return options +} diff --git a/internal/service/rds/parameter_group.go b/internal/service/rds/parameter_group.go index 2e2de6de8b7..9dc8d4ed15a 100644 --- a/internal/service/rds/parameter_group.go +++ b/internal/service/rds/parameter_group.go @@ -204,7 +204,7 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met } var paramFound bool - for _, cp := range expandParameters2(configParams.List()) { + for _, cp := range expandParameters(configParams.List()) { if cp.ParameterName == nil { continue } @@ -221,7 +221,7 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met } } - if err := d.Set(names.AttrParameter, flattenParameters2(userParams)); err != nil { + if err := d.Set(names.AttrParameter, flattenParameters(userParams)); err != nil { return sdkdiag.AppendErrorf(diags, "setting parameter: %s", err) } @@ -239,7 +239,7 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m o, n := d.GetChange(names.AttrParameter) os, ns := o.(*schema.Set), n.(*schema.Set) - if parameters := expandParameters2(ns.Difference(os).List()); len(parameters) > 0 { + if parameters := expandParameters(ns.Difference(os).List()); len(parameters) > 0 { // We can only modify 20 parameters at a time, so walk them until // we've got them all. for parameters != nil { @@ -261,13 +261,13 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m toRemove := map[string]types.Parameter{} - for _, p := range expandParameters2(os.List()) { + for _, p := range expandParameters(os.List()) { if p.ParameterName != nil { toRemove[aws.ToString(p.ParameterName)] = p } } - for _, p := range expandParameters2(ns.List()) { + for _, p := range expandParameters(ns.List()) { if p.ParameterName != nil { delete(toRemove, aws.ToString(p.ParameterName)) } @@ -483,59 +483,3 @@ func parameterGroupModifyChunk(all []types.Parameter, maxChunkSize int) ([]types return modifyChunk, remainder } - -// TODO Rename once aws_rds_cluster_parameter_group has been migrated. -func expandParameters2(tfList []interface{}) []types.Parameter { - var apiObjects []types.Parameter - - for _, tfMapRaw := range tfList { - tfMap, ok := tfMapRaw.(map[string]interface{}) - if !ok { - continue - } - - if tfMap[names.AttrName].(string) == "" { - continue - } - - apiObject := types.Parameter{ - ParameterName: aws.String(strings.ToLower(tfMap[names.AttrName].(string))), - ParameterValue: aws.String(tfMap[names.AttrValue].(string)), - } - - if v, ok := tfMap["apply_method"].(string); ok && v != "" { - apiObject.ApplyMethod = types.ApplyMethod(strings.ToLower(v)) - } - - apiObjects = append(apiObjects, apiObject) - } - - return apiObjects -} - -// TODO Rename once aws_rds_cluster_parameter_group has been migrated. -func flattenParameters2(apiObject []types.Parameter) []interface{} { - apiObjects := make([]interface{}, 0) - - for _, apiObject := range apiObject { - if apiObject.ParameterName == nil { - continue - } - - tfMap := make(map[string]interface{}) - - tfMap["apply_method"] = strings.ToLower(string(apiObject.ApplyMethod)) - tfMap[names.AttrName] = strings.ToLower(aws.ToString(apiObject.ParameterName)) - - // Default empty string, guard against nil parameter values. - tfMap[names.AttrValue] = "" - if apiObject.ParameterValue != nil { - tfMap[names.AttrValue] = aws.ToString(apiObject.ParameterValue) - } - - apiObjects = append(apiObjects, tfMap) - - } - - return apiObjects -} From 9606c59ba6f4a53c1557c6bd664913286364c23e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 11:56:43 -0400 Subject: [PATCH 23/30] r/aws_rds_cluster_endpoint: Migrate to AWS SDK for Go v2. --- internal/service/rds/cluster_endpoint.go | 121 +++++++++++------- internal/service/rds/cluster_endpoint_test.go | 35 +++-- internal/service/rds/consts.go | 6 + internal/service/rds/exports_test.go | 2 + internal/service/rds/service_package_gen.go | 2 +- 5 files changed, 98 insertions(+), 68 deletions(-) diff --git a/internal/service/rds/cluster_endpoint.go b/internal/service/rds/cluster_endpoint.go index a0b1703b5a9..205f5a18e17 100644 --- a/internal/service/rds/cluster_endpoint.go +++ b/internal/service/rds/cluster_endpoint.go @@ -8,8 +8,9 @@ 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/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -17,6 +18,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -26,7 +28,7 @@ import ( // @SDKResource("aws_rds_cluster_endpoint", name="Cluster Endpoint") // @Tags(identifierAttribute="arn") // @Testing(tagsTest=false) -func ResourceClusterEndpoint() *schema.Resource { +func resourceClusterEndpoint() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceClusterEndpointCreate, ReadWithoutTimeout: resourceClusterEndpointRead, @@ -87,36 +89,37 @@ func ResourceClusterEndpoint() *schema.Resource { } func resourceClusterEndpointCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - const ( - clusterEndpointCreateTimeout = 30 * time.Minute - ) var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) endpointID := d.Get("cluster_endpoint_identifier").(string) input := &rds.CreateDBClusterEndpointInput{ DBClusterEndpointIdentifier: aws.String(endpointID), DBClusterIdentifier: aws.String(d.Get(names.AttrClusterIdentifier).(string)), EndpointType: aws.String(d.Get("custom_endpoint_type").(string)), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), } if v, ok := d.GetOk("excluded_members"); ok && v.(*schema.Set).Len() > 0 { - input.ExcludedMembers = flex.ExpandStringSet(v.(*schema.Set)) + input.ExcludedMembers = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("static_members"); ok && v.(*schema.Set).Len() > 0 { - input.StaticMembers = flex.ExpandStringSet(v.(*schema.Set)) + input.StaticMembers = flex.ExpandStringValueSet(v.(*schema.Set)) } - _, err := conn.CreateDBClusterEndpointWithContext(ctx, input) + _, err := conn.CreateDBClusterEndpoint(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS Cluster Endpoint (%s): %s", endpointID, err) } d.SetId(endpointID) - if _, err := waitClusterEndpointCreated(ctx, conn, d.Id(), clusterEndpointCreateTimeout); err != nil { + const ( + timeout = 30 * time.Minute + ) + if _, err := waitClusterEndpointCreated(ctx, conn, d.Id(), timeout); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster Endpoint (%s) create: %s", d.Id(), err) } @@ -125,9 +128,9 @@ func resourceClusterEndpointCreate(ctx context.Context, d *schema.ResourceData, func resourceClusterEndpointRead(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) - clusterEp, err := FindDBClusterEndpointByID(ctx, conn, d.Id()) + clusterEp, err := findDBClusterEndpointByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS Cluster Endpoint (%s) not found, removing from state", d.Id()) @@ -139,21 +142,20 @@ func resourceClusterEndpointRead(ctx context.Context, d *schema.ResourceData, me return sdkdiag.AppendErrorf(diags, "reading RDS Cluster Endpoint (%s): %s", d.Id(), err) } - arn := aws.StringValue(clusterEp.DBClusterEndpointArn) - d.Set(names.AttrARN, arn) + d.Set(names.AttrARN, clusterEp.DBClusterEndpointArn) d.Set("cluster_endpoint_identifier", clusterEp.DBClusterEndpointIdentifier) d.Set(names.AttrClusterIdentifier, clusterEp.DBClusterIdentifier) d.Set("custom_endpoint_type", clusterEp.CustomEndpointType) d.Set(names.AttrEndpoint, clusterEp.Endpoint) - d.Set("excluded_members", aws.StringValueSlice(clusterEp.ExcludedMembers)) - d.Set("static_members", aws.StringValueSlice(clusterEp.StaticMembers)) + d.Set("excluded_members", clusterEp.ExcludedMembers) + d.Set("static_members", clusterEp.StaticMembers) return diags } func resourceClusterEndpointUpdate(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) if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { input := &rds.ModifyDBClusterEndpointInput{ @@ -165,18 +167,19 @@ func resourceClusterEndpointUpdate(ctx context.Context, d *schema.ResourceData, } if v, ok := d.GetOk("excluded_members"); ok && v.(*schema.Set).Len() > 0 { - input.ExcludedMembers = flex.ExpandStringSet(v.(*schema.Set)) + input.ExcludedMembers = flex.ExpandStringValueSet(v.(*schema.Set)) } else { - input.ExcludedMembers = aws.StringSlice([]string{}) + input.ExcludedMembers = []string{} } if v, ok := d.GetOk("static_members"); ok && v.(*schema.Set).Len() > 0 { - input.StaticMembers = flex.ExpandStringSet(v.(*schema.Set)) + input.StaticMembers = flex.ExpandStringValueSet(v.(*schema.Set)) } else { - input.StaticMembers = aws.StringSlice([]string{}) + input.StaticMembers = []string{} } - _, err := conn.ModifyDBClusterEndpointWithContext(ctx, input) + _, err := conn.ModifyDBClusterEndpoint(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "modifying RDS Cluster Endpoint (%s): %s", d.Id(), err) } @@ -187,12 +190,13 @@ func resourceClusterEndpointUpdate(ctx context.Context, d *schema.ResourceData, func resourceClusterEndpointDelete(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 Endpoint: %s", d.Id()) - _, err := conn.DeleteDBClusterEndpointWithContext(ctx, &rds.DeleteDBClusterEndpointInput{ + _, err := conn.DeleteDBClusterEndpoint(ctx, &rds.DeleteDBClusterEndpointInput{ DBClusterEndpointIdentifier: aws.String(d.Id()), }) + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting RDS Cluster Endpoint (%s): %s", d.Id(), err) } @@ -204,39 +208,60 @@ func resourceClusterEndpointDelete(ctx context.Context, d *schema.ResourceData, return diags } -func FindDBClusterEndpointByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBClusterEndpoint, error) { +func findDBClusterEndpointByID(ctx context.Context, conn *rds.Client, id string) (*types.DBClusterEndpoint, error) { input := &rds.DescribeDBClusterEndpointsInput{ DBClusterEndpointIdentifier: aws.String(id), } + output, err := findDBClusterEndpoint(ctx, conn, input, tfslices.PredicateTrue[*types.DBClusterEndpoint]()) - output, err := conn.DescribeDBClusterEndpointsWithContext(ctx, input) if err != nil { return nil, err } - if output == nil || len(output.DBClusterEndpoints) == 0 || output.DBClusterEndpoints[0] == nil { - return nil, tfresource.NewEmptyResultError(input) + // Eventual consistency check. + if aws.ToString(output.DBClusterEndpointIdentifier) != id { + return nil, &retry.NotFoundError{ + LastRequest: input, + } } - if count := len(output.DBClusterEndpoints); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) + return output, nil +} + +func findDBClusterEndpoint(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClusterEndpointsInput, filter tfslices.Predicate[*types.DBClusterEndpoint]) (*types.DBClusterEndpoint, error) { + output, err := findDBClusterEndpoints(ctx, conn, input, filter) + + if err != nil { + return nil, err } - dbClusterEndpoint := output.DBClusterEndpoints[0] + return tfresource.AssertSingleValueResult(output) +} - // Eventual consistency check. - if aws.StringValue(dbClusterEndpoint.DBClusterEndpointIdentifier) != id { - return nil, &retry.NotFoundError{ - LastRequest: input, +func findDBClusterEndpoints(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClusterEndpointsInput, filter tfslices.Predicate[*types.DBClusterEndpoint]) ([]types.DBClusterEndpoint, error) { + var output []types.DBClusterEndpoint + + pages := rds.NewDescribeDBClusterEndpointsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if err != nil { + return nil, err + } + + for _, v := range page.DBClusterEndpoints { + if filter(&v) { + output = append(output, v) + } } } - return dbClusterEndpoint, nil + return output, nil } -func statusClusterEndpoint(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc { +func statusClusterEndpoint(ctx context.Context, conn *rds.Client, id string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindDBClusterEndpointByID(ctx, conn, id) + output, err := findDBClusterEndpointByID(ctx, conn, id) if tfresource.NotFound(err) { return nil, "", nil @@ -246,14 +271,14 @@ func statusClusterEndpoint(ctx context.Context, conn *rds.RDS, id string) retry. return nil, "", err } - return output, aws.StringValue(output.Status), nil + return output, aws.ToString(output.Status), nil } } -func waitClusterEndpointCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBClusterEndpoint, error) { +func waitClusterEndpointCreated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.DBClusterEndpoint, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{"creating"}, - Target: []string{"available"}, + Pending: []string{clusterEndpointStatusCreating}, + Target: []string{clusterEndpointStatusAvailable}, Refresh: statusClusterEndpoint(ctx, conn, id), Timeout: timeout, Delay: 5 * time.Second, @@ -262,16 +287,16 @@ func waitClusterEndpointCreated(ctx context.Context, conn *rds.RDS, id string, t outputRaw, err := stateConf.WaitForStateContext(ctx) - if output, ok := outputRaw.(*rds.DBClusterEndpoint); ok { + if output, ok := outputRaw.(*types.DBClusterEndpoint); ok { return output, err } return nil, err } -func waitClusterEndpointDeleted(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBClusterEndpoint, error) { +func waitClusterEndpointDeleted(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.DBClusterEndpoint, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{"available", "deleting"}, + Pending: []string{clusterEndpointStatusAvailable, clusterEndpointStatusDeleting}, Target: []string{}, Refresh: statusClusterEndpoint(ctx, conn, id), Timeout: timeout, @@ -281,7 +306,7 @@ func waitClusterEndpointDeleted(ctx context.Context, conn *rds.RDS, id string, t outputRaw, err := stateConf.WaitForStateContext(ctx) - if output, ok := outputRaw.(*rds.DBClusterEndpoint); ok { + if output, ok := outputRaw.(*types.DBClusterEndpoint); ok { return output, err } diff --git a/internal/service/rds/cluster_endpoint_test.go b/internal/service/rds/cluster_endpoint_test.go index 44064f7d6c2..a84a300a2b0 100644 --- a/internal/service/rds/cluster_endpoint_test.go +++ b/internal/service/rds/cluster_endpoint_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/YakDriver/regexache" - "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/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" @@ -29,8 +29,8 @@ func TestAccRDSClusterEndpoint_basic(t *testing.T) { } rInt := sdkacctest.RandInt() - var customReaderEndpoint rds.DBClusterEndpoint - var customEndpoint rds.DBClusterEndpoint + var customReaderEndpoint types.DBClusterEndpoint + var customEndpoint types.DBClusterEndpoint readerResourceName := "aws_rds_cluster_endpoint.reader" defaultResourceName := "aws_rds_cluster_endpoint.default" @@ -77,7 +77,7 @@ func TestAccRDSClusterEndpoint_tags(t *testing.T) { } rInt := sdkacctest.RandInt() - var customReaderEndpoint rds.DBClusterEndpoint + var customReaderEndpoint types.DBClusterEndpoint resourceName := "aws_rds_cluster_endpoint.reader" resource.ParallelTest(t, resource.TestCase{ @@ -120,28 +120,28 @@ func TestAccRDSClusterEndpoint_tags(t *testing.T) { }) } -func testAccCheckClusterEndpointAttributes(v *rds.DBClusterEndpoint) resource.TestCheckFunc { +func testAccCheckClusterEndpointAttributes(v *types.DBClusterEndpoint) resource.TestCheckFunc { return func(s *terraform.State) error { - if aws.StringValue(v.Endpoint) == "" { + if aws.ToString(v.Endpoint) == "" { return fmt.Errorf("empty endpoint domain") } - if aws.StringValue(v.CustomEndpointType) != "READER" && - aws.StringValue(v.CustomEndpointType) != "ANY" { - return fmt.Errorf("Incorrect endpoint type: expected: READER or ANY, got: %s", aws.StringValue(v.CustomEndpointType)) + if aws.ToString(v.CustomEndpointType) != "READER" && + aws.ToString(v.CustomEndpointType) != "ANY" { + return fmt.Errorf("Incorrect endpoint type: expected: READER or ANY, got: %s", aws.ToString(v.CustomEndpointType)) } if len(v.StaticMembers) == 0 && len(v.ExcludedMembers) == 0 { return fmt.Errorf("Empty members") } - for _, m := range aws.StringValueSlice(v.StaticMembers) { + for _, m := range v.StaticMembers { if !strings.HasPrefix(m, "tf-aurora-cluster-instance") { return fmt.Errorf("Incorrect StaticMember Cluster Instance Identifier prefix:\nexpected: %s\ngot: %s", "tf-aurora-cluster-instance", m) } } - for _, m := range aws.StringValueSlice(v.ExcludedMembers) { + for _, m := range v.ExcludedMembers { if !strings.HasPrefix(m, "tf-aurora-cluster-instance") { return fmt.Errorf("Incorrect ExcludeMember Cluster Instance Identifier prefix:\nexpected: %s\ngot: %s", "tf-aurora-cluster-instance", m) } @@ -153,7 +153,7 @@ func testAccCheckClusterEndpointAttributes(v *rds.DBClusterEndpoint) resource.Te func testAccCheckClusterEndpointDestroy(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_endpoint" { @@ -177,20 +177,17 @@ func testAccCheckClusterEndpointDestroy(ctx context.Context) resource.TestCheckF } } -func testAccCheckClusterEndpointExists(ctx context.Context, n string, v *rds.DBClusterEndpoint) resource.TestCheckFunc { +func testAccCheckClusterEndpointExists(ctx context.Context, n string, v *types.DBClusterEndpoint) 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("No RDS Cluster Endpoint ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindDBClusterEndpointByID(ctx, conn, rs.Primary.ID) + if err != nil { return err } diff --git a/internal/service/rds/consts.go b/internal/service/rds/consts.go index a4809384b0e..8377892463f 100644 --- a/internal/service/rds/consts.go +++ b/internal/service/rds/consts.go @@ -44,6 +44,12 @@ const ( clusterSnapshotAttributeNameRestore = "restore" ) +const ( + clusterEndpointStatusAvailable = "available" + clusterEndpointStatusCreating = "creating" + clusterEndpointStatusDeleting = "deleting" +) + const ( storageTypeStandard = "standard" storageTypeGP2 = "gp2" diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index c05e98103c7..e495ec127f8 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -7,6 +7,7 @@ package rds var ( ResourceCertificate = resourceCertificate ResourceCluster = resourceCluster + ResourceClusterEndpoint = resourceClusterEndpoint ResourceClusterParameterGroup = resourceClusterParameterGroup ResourceClusterSnapshot = resourceClusterSnapshot ResourceEventSubscription = resourceEventSubscription @@ -19,6 +20,7 @@ var ( ResourceSnapshotCopy = resourceSnapshotCopy ResourceSubnetGroup = resourceSubnetGroup + FindDBClusterEndpointByID = findDBClusterEndpointByID FindDBClusterParameterGroupByName = findDBClusterParameterGroupByName FindDBClusterSnapshotByID = findDBClusterSnapshotByID FindDBInstanceByID = findDBInstanceByIDSDKv1 diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index 78910dadd0d..ca119ec43a3 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -218,7 +218,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka TypeName: "aws_rds_cluster_activity_stream", }, { - Factory: ResourceClusterEndpoint, + Factory: resourceClusterEndpoint, TypeName: "aws_rds_cluster_endpoint", Name: "Cluster Endpoint", Tags: &types.ServicePackageResourceTags{ From 95815c3237827a6be3515f597c3c38c8d0538f25 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 12:50:19 -0400 Subject: [PATCH 24/30] r/aws_rds_cluster_endpoint: Fix acceptance tests. --- internal/service/rds/cluster_endpoint_test.go | 112 ++++++------------ 1 file changed, 36 insertions(+), 76 deletions(-) diff --git a/internal/service/rds/cluster_endpoint_test.go b/internal/service/rds/cluster_endpoint_test.go index a84a300a2b0..d091206484d 100644 --- a/internal/service/rds/cluster_endpoint_test.go +++ b/internal/service/rds/cluster_endpoint_test.go @@ -6,11 +6,9 @@ package rds_test import ( "context" "fmt" - "strings" "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go-v2/aws" "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" @@ -28,7 +26,7 @@ func TestAccRDSClusterEndpoint_basic(t *testing.T) { t.Skip("skipping long-running test in short mode") } - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) var customReaderEndpoint types.DBClusterEndpoint var customEndpoint types.DBClusterEndpoint readerResourceName := "aws_rds_cluster_endpoint.reader" @@ -41,12 +39,10 @@ func TestAccRDSClusterEndpoint_basic(t *testing.T) { CheckDestroy: testAccCheckClusterEndpointDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccClusterEndpointConfig_basic(rInt), - Check: resource.ComposeTestCheckFunc( + Config: testAccClusterEndpointConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckClusterEndpointExists(ctx, readerResourceName, &customReaderEndpoint), - testAccCheckClusterEndpointAttributes(&customReaderEndpoint), testAccCheckClusterEndpointExists(ctx, defaultResourceName, &customEndpoint), - testAccCheckClusterEndpointAttributes(&customEndpoint), acctest.MatchResourceAttrRegionalARN(readerResourceName, names.AttrARN, "rds", regexache.MustCompile(`cluster-endpoint:.+`)), resource.TestCheckResourceAttrSet(readerResourceName, names.AttrEndpoint), acctest.MatchResourceAttrRegionalARN(defaultResourceName, names.AttrARN, "rds", regexache.MustCompile(`cluster-endpoint:.+`)), @@ -56,13 +52,13 @@ func TestAccRDSClusterEndpoint_basic(t *testing.T) { ), }, { - ResourceName: "aws_rds_cluster_endpoint.reader", + ResourceName: readerResourceName, ImportState: true, ImportStateVerify: true, }, { - ResourceName: "aws_rds_cluster_endpoint.default", + ResourceName: defaultResourceName, ImportState: true, ImportStateVerify: true, }, @@ -76,7 +72,7 @@ func TestAccRDSClusterEndpoint_tags(t *testing.T) { t.Skip("skipping long-running test in short mode") } - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) var customReaderEndpoint types.DBClusterEndpoint resourceName := "aws_rds_cluster_endpoint.reader" @@ -87,7 +83,7 @@ func TestAccRDSClusterEndpoint_tags(t *testing.T) { CheckDestroy: testAccCheckClusterEndpointDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccClusterEndpointConfig_tags1(rInt, acctest.CtKey1, acctest.CtValue1), + Config: testAccClusterEndpointConfig_tags1(rName, acctest.CtKey1, acctest.CtValue1), Check: resource.ComposeTestCheckFunc( testAccCheckClusterEndpointExists(ctx, resourceName, &customReaderEndpoint), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), @@ -100,7 +96,7 @@ func TestAccRDSClusterEndpoint_tags(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccClusterEndpointConfig_tags2(rInt, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), + Config: testAccClusterEndpointConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), Check: resource.ComposeTestCheckFunc( testAccCheckClusterEndpointExists(ctx, resourceName, &customReaderEndpoint), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), @@ -109,7 +105,7 @@ func TestAccRDSClusterEndpoint_tags(t *testing.T) { ), }, { - Config: testAccClusterEndpointConfig_tags1(rInt, acctest.CtKey2, acctest.CtValue2), + Config: testAccClusterEndpointConfig_tags1(rName, acctest.CtKey2, acctest.CtValue2), Check: resource.ComposeTestCheckFunc( testAccCheckClusterEndpointExists(ctx, resourceName, &customReaderEndpoint), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), @@ -120,37 +116,6 @@ func TestAccRDSClusterEndpoint_tags(t *testing.T) { }) } -func testAccCheckClusterEndpointAttributes(v *types.DBClusterEndpoint) resource.TestCheckFunc { - return func(s *terraform.State) error { - if aws.ToString(v.Endpoint) == "" { - return fmt.Errorf("empty endpoint domain") - } - - if aws.ToString(v.CustomEndpointType) != "READER" && - aws.ToString(v.CustomEndpointType) != "ANY" { - return fmt.Errorf("Incorrect endpoint type: expected: READER or ANY, got: %s", aws.ToString(v.CustomEndpointType)) - } - - if len(v.StaticMembers) == 0 && len(v.ExcludedMembers) == 0 { - return fmt.Errorf("Empty members") - } - - for _, m := range v.StaticMembers { - if !strings.HasPrefix(m, "tf-aurora-cluster-instance") { - return fmt.Errorf("Incorrect StaticMember Cluster Instance Identifier prefix:\nexpected: %s\ngot: %s", "tf-aurora-cluster-instance", m) - } - } - - for _, m := range v.ExcludedMembers { - if !strings.HasPrefix(m, "tf-aurora-cluster-instance") { - return fmt.Errorf("Incorrect ExcludeMember Cluster Instance Identifier prefix:\nexpected: %s\ngot: %s", "tf-aurora-cluster-instance", m) - } - } - - return nil - } -} - func testAccCheckClusterEndpointDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) @@ -198,10 +163,8 @@ func testAccCheckClusterEndpointExists(ctx context.Context, n string, v *types.D } } -func testAccClusterEndpointBaseConfig(n int) string { - return acctest.ConfigCompose( - acctest.ConfigAvailableAZsNoOptIn(), - fmt.Sprintf(` +func testAccClusterEndpointConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` data "aws_rds_orderable_db_instance" "test" { engine = aws_rds_cluster.default.engine engine_version = aws_rds_cluster.default.engine_version @@ -209,42 +172,43 @@ data "aws_rds_orderable_db_instance" "test" { } resource "aws_rds_cluster" "default" { - cluster_identifier = "tf-aurora-cluster-%[1]d" + cluster_identifier = %[1]q availability_zones = [ data.aws_availability_zones.available.names[0], data.aws_availability_zones.available.names[1], data.aws_availability_zones.available.names[2] ] - database_name = "mydb" - master_username = "foo" - master_password = "mustbeeightcharaters" - db_cluster_parameter_group_name = "default.aurora5.6" - skip_final_snapshot = true + + database_name = "test" + engine = %[2]q + master_username = "tfacctest" + master_password = "avoid-plaintext-passwords" + skip_final_snapshot = true } resource "aws_rds_cluster_instance" "test1" { apply_immediately = true cluster_identifier = aws_rds_cluster.default.id - identifier = "tf-aurora-cluster-instance-test1-%[1]d" + identifier = "%[1]s-1" instance_class = data.aws_rds_orderable_db_instance.test.instance_class + engine = aws_rds_cluster.default.engine } resource "aws_rds_cluster_instance" "test2" { apply_immediately = true cluster_identifier = aws_rds_cluster.default.id - identifier = "tf-aurora-cluster-instance-test2-%[1]d" + identifier = "%[1]s-2" instance_class = data.aws_rds_orderable_db_instance.test.instance_class + engine = aws_rds_cluster.default.engine } -`, n)) +`, rName, tfrds.ClusterEngineAuroraMySQL)) } -func testAccClusterEndpointConfig_basic(n int) string { - return acctest.ConfigCompose( - testAccClusterEndpointBaseConfig(n), - fmt.Sprintf(` +func testAccClusterEndpointConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccClusterEndpointConfig_base(rName), fmt.Sprintf(` resource "aws_rds_cluster_endpoint" "reader" { cluster_identifier = aws_rds_cluster.default.id - cluster_endpoint_identifier = "reader-%[1]d" + cluster_endpoint_identifier = "%[1]s-reader" custom_endpoint_type = "READER" static_members = [aws_rds_cluster_instance.test2.id] @@ -252,21 +216,19 @@ resource "aws_rds_cluster_endpoint" "reader" { resource "aws_rds_cluster_endpoint" "default" { cluster_identifier = aws_rds_cluster.default.id - cluster_endpoint_identifier = "default-%[1]d" + cluster_endpoint_identifier = "%[1]s-default" custom_endpoint_type = "ANY" excluded_members = [aws_rds_cluster_instance.test2.id] } -`, n)) +`, rName)) } -func testAccClusterEndpointConfig_tags1(n int, tagKey1, tagValue1 string) string { - return acctest.ConfigCompose( - testAccClusterEndpointBaseConfig(n), - fmt.Sprintf(` +func testAccClusterEndpointConfig_tags1(rName, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(testAccClusterEndpointConfig_base(rName), fmt.Sprintf(` resource "aws_rds_cluster_endpoint" "reader" { cluster_identifier = aws_rds_cluster.default.id - cluster_endpoint_identifier = "reader-%[1]d" + cluster_endpoint_identifier = "%[1]s-reader" custom_endpoint_type = "READER" static_members = [aws_rds_cluster_instance.test2.id] @@ -275,16 +237,14 @@ resource "aws_rds_cluster_endpoint" "reader" { %[2]q = %[3]q } } -`, n, tagKey1, tagValue1)) +`, rName, tagKey1, tagValue1)) } -func testAccClusterEndpointConfig_tags2(n int, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return acctest.ConfigCompose( - testAccClusterEndpointBaseConfig(n), - fmt.Sprintf(` +func testAccClusterEndpointConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(testAccClusterEndpointConfig_base(rName), fmt.Sprintf(` resource "aws_rds_cluster_endpoint" "reader" { cluster_identifier = aws_rds_cluster.default.id - cluster_endpoint_identifier = "reader-%[1]d" + cluster_endpoint_identifier = "%[1]s-reader" custom_endpoint_type = "READER" static_members = [aws_rds_cluster_instance.test2.id] @@ -294,5 +254,5 @@ resource "aws_rds_cluster_endpoint" "reader" { %[4]q = %[5]q } } -`, n, tagKey1, tagValue1, tagKey2, tagValue2)) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } From ee1b5adf1b9824a9e829a8597c0f76f9ab73a521 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 14:01:56 -0400 Subject: [PATCH 25/30] r/aws_rds_cluster_role_association: Migrate to AWS SDK for Go v2. --- internal/service/rds/cluster_instance.go | 91 +++++++ .../service/rds/cluster_role_association.go | 238 ++++++++++++++---- .../rds/cluster_role_association_test.go | 32 +-- internal/service/rds/exports_test.go | 2 + internal/service/rds/find.go | 64 ----- internal/service/rds/id.go | 28 --- internal/service/rds/reserved_instance.go | 71 +++++- internal/service/rds/service_package_gen.go | 3 +- internal/service/rds/status.go | 45 ---- internal/service/rds/wait.go | 158 ------------ 10 files changed, 369 insertions(+), 363 deletions(-) delete mode 100644 internal/service/rds/find.go delete mode 100644 internal/service/rds/id.go delete mode 100644 internal/service/rds/status.go delete mode 100644 internal/service/rds/wait.go diff --git a/internal/service/rds/cluster_instance.go b/internal/service/rds/cluster_instance.go index d91da2ae775..9ed9701a95a 100644 --- a/internal/service/rds/cluster_instance.go +++ b/internal/service/rds/cluster_instance.go @@ -15,6 +15,7 @@ import ( "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-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -552,6 +553,96 @@ func resourceClusterInstanceDelete(ctx context.Context, d *schema.ResourceData, return diags } +func waitDBClusterInstanceCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBInstance, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{ + InstanceStatusBackingUp, + InstanceStatusConfiguringEnhancedMonitoring, + InstanceStatusConfiguringIAMDatabaseAuth, + InstanceStatusConfiguringLogExports, + InstanceStatusCreating, + InstanceStatusMaintenance, + InstanceStatusModifying, + InstanceStatusRebooting, + InstanceStatusRenaming, + InstanceStatusResettingMasterCredentials, + InstanceStatusStarting, + InstanceStatusStorageOptimization, + InstanceStatusUpgrading, + }, + Target: []string{InstanceStatusAvailable}, + Refresh: statusDBInstanceSDKv1(ctx, conn, id), + Timeout: timeout, + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*rds.DBInstance); ok { + return output, err + } + + return nil, err +} + +func waitDBClusterInstanceUpdated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBInstance, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{ + InstanceStatusBackingUp, + InstanceStatusConfiguringEnhancedMonitoring, + InstanceStatusConfiguringIAMDatabaseAuth, + InstanceStatusConfiguringLogExports, + InstanceStatusCreating, + InstanceStatusMaintenance, + InstanceStatusModifying, + InstanceStatusRebooting, + InstanceStatusRenaming, + InstanceStatusResettingMasterCredentials, + InstanceStatusStarting, + InstanceStatusStorageOptimization, + InstanceStatusUpgrading, + }, + Target: []string{InstanceStatusAvailable}, + Refresh: statusDBInstanceSDKv1(ctx, conn, id), + Timeout: timeout, + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*rds.DBInstance); ok { + return output, err + } + + return nil, err +} + +func waitDBClusterInstanceDeleted(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBInstance, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{ + InstanceStatusConfiguringLogExports, + InstanceStatusDeletePreCheck, + InstanceStatusDeleting, + InstanceStatusModifying, + }, + Target: []string{}, + Refresh: statusDBInstanceSDKv1(ctx, conn, id), + Timeout: timeout, + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*rds.DBInstance); ok { + return output, err + } + + return nil, err +} + func clusterSetResourceDataEngineVersionFromClusterInstance(d *schema.ResourceData, c *rds.DBInstance) { oldVersion := d.Get(names.AttrEngineVersion).(string) newVersion := aws.StringValue(c.EngineVersion) diff --git a/internal/service/rds/cluster_role_association.go b/internal/service/rds/cluster_role_association.go index fa1513839a9..3ad6f855a66 100644 --- a/internal/service/rds/cluster_role_association.go +++ b/internal/service/rds/cluster_role_association.go @@ -5,24 +5,29 @@ package rds import ( "context" + "fmt" "log" + "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/aws/arn" + "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" ) -// @SDKResource("aws_rds_cluster_role_association") -func ResourceClusterRoleAssociation() *schema.Resource { +// @SDKResource("aws_rds_cluster_role_association", name="Cluster IAM Role Association") +func resourceClusterRoleAssociation() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceClusterRoleAssociationCreate, ReadWithoutTimeout: resourceClusterRoleAssociationRead, @@ -60,40 +65,29 @@ func ResourceClusterRoleAssociation() *schema.Resource { func resourceClusterRoleAssociationCreate(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) dbClusterID := d.Get("db_cluster_identifier").(string) roleARN := d.Get(names.AttrRoleARN).(string) + id := clusterRoleAssociationCreateResourceID(dbClusterID, roleARN) input := &rds.AddRoleToDBClusterInput{ DBClusterIdentifier: aws.String(dbClusterID), FeatureName: aws.String(d.Get("feature_name").(string)), RoleArn: aws.String(roleARN), } - err := retry.RetryContext(ctx, propagationTimeout, func() *retry.RetryError { - var err error - _, err = conn.AddRoleToDBClusterWithContext(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.AddRoleToDBClusterWithContext(ctx, input) - } + _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, func() (interface{}, error) { + return conn.AddRoleToDBCluster(ctx, input) + }, errCodeInvalidParameterValue, "IAM role ARN value is invalid or does not include the required permissions") + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating RDS DB Cluster (%s) IAM Role (%s) Association: %s", dbClusterID, roleARN, err) + return sdkdiag.AppendErrorf(diags, "creating RDS Cluster IAM Role Association (%s): %s", id, err) } - d.SetId(ClusterRoleAssociationCreateResourceID(dbClusterID, roleARN)) + d.SetId(id) - _, err = waitDBClusterRoleAssociationCreated(ctx, conn, dbClusterID, roleARN, d.Timeout(schema.TimeoutCreate)) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Cluster (%s) IAM Role (%s) Association to create: %s", dbClusterID, roleARN, err) + if _, err := waitDBClusterRoleAssociationCreated(ctx, conn, dbClusterID, roleARN, d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster IAM Role Association (%s) create: %s", d.Id(), err) } return append(diags, resourceClusterRoleAssociationRead(ctx, d, meta)...) @@ -101,14 +95,14 @@ func resourceClusterRoleAssociationCreate(ctx context.Context, d *schema.Resourc func resourceClusterRoleAssociationRead(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) - dbClusterID, roleARN, err := ClusterRoleAssociationParseResourceID(d.Id()) + dbClusterID, roleARN, err := clusterRoleAssociationParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "parsing RDS DB Cluster IAM Role Association ID: %s", err) + return sdkdiag.AppendFromErr(diags, err) } - output, err := FindDBClusterRoleByDBClusterIDAndRoleARN(ctx, conn, dbClusterID, roleARN) + 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) @@ -117,7 +111,7 @@ func resourceClusterRoleAssociationRead(ctx context.Context, d *schema.ResourceD } if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS DB Cluster (%s) IAM Role (%s) Association: %s", dbClusterID, roleARN, err) + return sdkdiag.AppendErrorf(diags, "reading RDS Cluster IAM Role Association (%s): %s", d.Id(), err) } d.Set("db_cluster_identifier", dbClusterID) @@ -129,35 +123,193 @@ func resourceClusterRoleAssociationRead(ctx context.Context, d *schema.ResourceD func resourceClusterRoleAssociationDelete(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) - dbClusterID, roleARN, err := ClusterRoleAssociationParseResourceID(d.Id()) + dbClusterID, roleARN, err := clusterRoleAssociationParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "parsing RDS DB Cluster IAM Role Association ID: %s", err) + return sdkdiag.AppendFromErr(diags, err) } - input := &rds.RemoveRoleFromDBClusterInput{ + log.Printf("[DEBUG] Deleting RDS Cluster IAM Role Association: %s", d.Id()) + _, err = conn.RemoveRoleFromDBCluster(ctx, &rds.RemoveRoleFromDBClusterInput{ DBClusterIdentifier: aws.String(dbClusterID), FeatureName: aws.String(d.Get("feature_name").(string)), RoleArn: aws.String(roleARN), + }) + + if errs.IsA[*types.DBClusterNotFoundFault](err) || errs.IsA[*types.DBClusterRoleNotFoundFault](err) { + return diags } - log.Printf("[DEBUG] Deleting RDS DB Cluster IAM Role Association: %s", d.Id()) - _, err = conn.RemoveRoleFromDBClusterWithContext(ctx, input) + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting RDS Cluster IAM Role Association (%s): %s", d.Id(), err) + } - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterNotFoundFault) || tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterRoleNotFoundFault) { - return diags + if _, err := waitDBClusterRoleAssociationDeleted(ctx, conn, dbClusterID, roleARN, d.Timeout(schema.TimeoutDelete)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster IAM Role Association (%s) delete: %s", d.Id(), err) } + return diags +} + +const clusterRoleAssociationResourceIDSeparator = "," + +func clusterRoleAssociationCreateResourceID(dbClusterID, roleARN string) string { + parts := []string{dbClusterID, roleARN} + id := strings.Join(parts, clusterRoleAssociationResourceIDSeparator) + + return id +} + +func clusterRoleAssociationParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, clusterRoleAssociationResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected DBCLUSTERID%[2]sROLEARN", id, clusterRoleAssociationResourceIDSeparator) +} + +func findDBClusterRoleByTwoPartKey(ctx context.Context, conn *rds.Client, dbClusterID, roleARN string) (*types.DBClusterRole, error) { + dbCluster, err := findDBClusterByIDV2(ctx, conn, dbClusterID) + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting RDS DB Cluster (%s) IAM Role (%s) Association: %s", dbClusterID, roleARN, err) + return nil, err } - _, err = waitDBClusterRoleAssociationDeleted(ctx, conn, dbClusterID, roleARN, d.Timeout(schema.TimeoutDelete)) + output, err := tfresource.AssertSingleValueResult(tfslices.Filter(dbCluster.AssociatedRoles, func(v types.DBClusterRole) bool { + return aws.ToString(v.RoleArn) == roleARN + })) if err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Cluster (%s) IAM Role (%s) Association to delete: %s", dbClusterID, roleARN, err) + return nil, err } - return diags + if status := aws.ToString(output.Status); status == clusterRoleStatusDeleted { + return nil, &retry.NotFoundError{ + Message: status, + } + } + + return output, nil +} + +func statusDBClusterRole(ctx context.Context, conn *rds.Client, dbClusterID, roleARN string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findDBClusterRoleByTwoPartKey(ctx, conn, dbClusterID, roleARN) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.ToString(output.Status), nil + } +} + +func waitDBClusterRoleAssociationCreated(ctx context.Context, conn *rds.Client, dbClusterID, roleARN string, timeout time.Duration) (*types.DBClusterRole, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{clusterRoleStatusPending}, + Target: []string{clusterRoleStatusActive}, + Refresh: statusDBClusterRole(ctx, conn, dbClusterID, roleARN), + Timeout: timeout, + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.DBClusterRole); ok { + return output, err + } + + return nil, err +} + +func waitDBClusterRoleAssociationDeleted(ctx context.Context, conn *rds.Client, dbClusterID, roleARN string, timeout time.Duration) (*types.DBClusterRole, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{clusterRoleStatusActive, clusterRoleStatusPending}, + Target: []string{}, + Refresh: statusDBClusterRole(ctx, conn, dbClusterID, roleARN), + Timeout: timeout, + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.DBClusterRole); ok { + return output, err + } + + return nil, err +} + +// TODO Remove once aws_rds_cluster is migrated. +func findDBClusterByIDV2(ctx context.Context, conn *rds.Client, id string) (*types.DBCluster, error) { + input := &rds.DescribeDBClustersInput{ + DBClusterIdentifier: aws.String(id), + } + output, err := findDBClusterV2(ctx, conn, input, tfslices.PredicateTrue[*types.DBCluster]()) + + if err != nil { + return nil, err + } + + // Eventual consistency check. + if arn.IsARN(id) { + if aws.ToString(output.DBClusterArn) != id { + return nil, &retry.NotFoundError{ + LastRequest: input, + } + } + } else if aws.ToString(output.DBClusterIdentifier) != id { + return nil, &retry.NotFoundError{ + LastRequest: input, + } + } + + return output, nil +} + +func findDBClusterV2(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*types.DBCluster]) (*types.DBCluster, error) { + output, err := findDBClustersV2(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findDBClustersV2(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*types.DBCluster]) ([]types.DBCluster, error) { + var output []types.DBCluster + + pages := rds.NewDescribeDBClustersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*types.DBClusterNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + for _, v := range page.DBClusters { + if filter(&v) { + output = append(output, v) + } + } + } + + return output, nil } diff --git a/internal/service/rds/cluster_role_association_test.go b/internal/service/rds/cluster_role_association_test.go index e5da670e4a5..e1e10471d3c 100644 --- a/internal/service/rds/cluster_role_association_test.go +++ b/internal/service/rds/cluster_role_association_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - "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" @@ -22,7 +22,7 @@ import ( func TestAccRDSClusterRoleAssociation_basic(t *testing.T) { ctx := acctest.Context(t) - var dbClusterRole rds.DBClusterRole + var dbClusterRole types.DBClusterRole rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dbClusterResourceName := "aws_rds_cluster.test" iamRoleResourceName := "aws_iam_role.test" @@ -54,7 +54,7 @@ func TestAccRDSClusterRoleAssociation_basic(t *testing.T) { func TestAccRDSClusterRoleAssociation_disappears(t *testing.T) { ctx := acctest.Context(t) - var dbClusterRole rds.DBClusterRole + var dbClusterRole types.DBClusterRole rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster_role_association.test" @@ -78,7 +78,7 @@ func TestAccRDSClusterRoleAssociation_disappears(t *testing.T) { func TestAccRDSClusterRoleAssociation_Disappears_cluster(t *testing.T) { ctx := acctest.Context(t) - var dbClusterRole rds.DBClusterRole + var dbClusterRole types.DBClusterRole rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster_role_association.test" clusterResourceName := "aws_rds_cluster.test" @@ -103,7 +103,7 @@ func TestAccRDSClusterRoleAssociation_Disappears_cluster(t *testing.T) { func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { ctx := acctest.Context(t) - var dbClusterRole rds.DBClusterRole + var dbClusterRole types.DBClusterRole rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster_role_association.test" roleResourceName := "aws_iam_role.test" @@ -126,27 +126,22 @@ func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { }) } -func testAccCheckClusterRoleAssociationExists(ctx context.Context, resourceName string, v *rds.DBClusterRole) resource.TestCheckFunc { +func testAccCheckClusterRoleAssociationExists(ctx context.Context, resourceName string, v *types.DBClusterRole) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] - if !ok { return fmt.Errorf("Not found: %s", resourceName) } - dbClusterID, roleARN, err := tfrds.ClusterRoleAssociationParseResourceID(rs.Primary.ID) - if err != nil { - return err - } + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + output, err := tfrds.FindDBClusterRoleByTwoPartKey(ctx, conn, rs.Primary.Attributes["db_cluster_identifier"], rs.Primary.Attributes[names.AttrRoleARN]) - role, err := tfrds.FindDBClusterRoleByDBClusterIDAndRoleARN(ctx, conn, dbClusterID, roleARN) if err != nil { return err } - *v = *role + *v = *output return nil } @@ -154,19 +149,14 @@ func testAccCheckClusterRoleAssociationExists(ctx context.Context, resourceName func testAccCheckClusterRoleAssociationDestroy(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_db_cluster_role_association" { continue } - dbClusterID, roleARN, err := tfrds.ClusterRoleAssociationParseResourceID(rs.Primary.ID) - if err != nil { - return err - } - - _, err = tfrds.FindDBClusterRoleByDBClusterIDAndRoleARN(ctx, conn, dbClusterID, roleARN) + _, err := tfrds.FindDBClusterRoleByTwoPartKey(ctx, conn, rs.Primary.Attributes["db_cluster_identifier"], rs.Primary.Attributes[names.AttrRoleARN]) if tfresource.NotFound(err) { continue diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index e495ec127f8..43e13208758 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -9,6 +9,7 @@ var ( ResourceCluster = resourceCluster ResourceClusterEndpoint = resourceClusterEndpoint ResourceClusterParameterGroup = resourceClusterParameterGroup + ResourceClusterRoleAssociation = resourceClusterRoleAssociation ResourceClusterSnapshot = resourceClusterSnapshot ResourceEventSubscription = resourceEventSubscription ResourceParameterGroup = resourceParameterGroup @@ -22,6 +23,7 @@ var ( FindDBClusterEndpointByID = findDBClusterEndpointByID FindDBClusterParameterGroupByName = findDBClusterParameterGroupByName + FindDBClusterRoleByTwoPartKey = findDBClusterRoleByTwoPartKey FindDBClusterSnapshotByID = findDBClusterSnapshotByID FindDBInstanceByID = findDBInstanceByIDSDKv1 FindDBParameterGroupByName = findDBParameterGroupByName diff --git a/internal/service/rds/find.go b/internal/service/rds/find.go deleted file mode 100644 index 44401bb7ef4..00000000000 --- a/internal/service/rds/find.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package rds - -import ( - "context" - - "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/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" -) - -func FindDBClusterRoleByDBClusterIDAndRoleARN(ctx context.Context, conn *rds.RDS, dbClusterID, roleARN string) (*rds.DBClusterRole, error) { - dbCluster, err := FindDBClusterByID(ctx, conn, dbClusterID) - if err != nil { - return nil, err - } - - for _, associatedRole := range dbCluster.AssociatedRoles { - if aws.StringValue(associatedRole.RoleArn) == roleARN { - if status := aws.StringValue(associatedRole.Status); status == clusterRoleStatusDeleted { - return nil, &retry.NotFoundError{ - Message: status, - } - } - - return associatedRole, nil - } - } - - return nil, &retry.NotFoundError{} -} - -func FindReservedDBInstanceByID(ctx context.Context, conn *rds.RDS, id string) (*rds.ReservedDBInstance, error) { - input := &rds.DescribeReservedDBInstancesInput{ - ReservedDBInstanceId: aws.String(id), - } - - output, err := conn.DescribeReservedDBInstancesWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeReservedDBInstanceNotFoundFault) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - if output == nil || len(output.ReservedDBInstances) == 0 || output.ReservedDBInstances[0] == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - if count := len(output.ReservedDBInstances); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) - } - - return output.ReservedDBInstances[0], nil -} diff --git a/internal/service/rds/id.go b/internal/service/rds/id.go deleted file mode 100644 index fefb91a364a..00000000000 --- a/internal/service/rds/id.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package rds - -import ( - "fmt" - "strings" -) - -const clusterRoleAssociationResourceIDSeparator = "," - -func ClusterRoleAssociationCreateResourceID(dbClusterID, roleARN string) string { - parts := []string{dbClusterID, roleARN} - id := strings.Join(parts, clusterRoleAssociationResourceIDSeparator) - - return id -} - -func ClusterRoleAssociationParseResourceID(id string) (string, string, error) { - parts := strings.Split(id, clusterRoleAssociationResourceIDSeparator) - - if len(parts) == 2 && parts[0] != "" && parts[1] != "" { - return parts[0], parts[1], nil - } - - return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected DBCLUSTERID%[2]sROLEARN", id, clusterRoleAssociationResourceIDSeparator) -} diff --git a/internal/service/rds/reserved_instance.go b/internal/service/rds/reserved_instance.go index 45cc771a626..f5121a4791a 100644 --- a/internal/service/rds/reserved_instance.go +++ b/internal/service/rds/reserved_instance.go @@ -8,9 +8,11 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go-v2/aws" + "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/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/create" @@ -154,7 +156,7 @@ func resourceReservedInstanceCreate(ctx context.Context, d *schema.ResourceData, return create.AppendDiagError(diags, names.RDS, create.ErrActionCreating, ResNameReservedInstance, fmt.Sprintf("offering_id: %s, reservation_id: %s", d.Get("offering_id").(string), d.Get("reservation_id").(string)), err) } - d.SetId(aws.ToString(resp.ReservedDBInstance.ReservedDBInstanceId)) + d.SetId(aws.StringValue(resp.ReservedDBInstance.ReservedDBInstanceId)) if err := waitReservedInstanceCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return create.AppendDiagError(diags, names.RDS, create.ErrActionWaitingForCreation, ResNameReservedInstance, d.Id(), err) @@ -204,6 +206,69 @@ func resourceReservedInstanceUpdate(ctx context.Context, d *schema.ResourceData, return resourceReservedInstanceRead(ctx, d, meta) } +func FindReservedDBInstanceByID(ctx context.Context, conn *rds.RDS, id string) (*rds.ReservedDBInstance, error) { + input := &rds.DescribeReservedDBInstancesInput{ + ReservedDBInstanceId: aws.String(id), + } + + output, err := conn.DescribeReservedDBInstancesWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeReservedDBInstanceNotFoundFault) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.ReservedDBInstances) == 0 || output.ReservedDBInstances[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.ReservedDBInstances); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output.ReservedDBInstances[0], nil +} + +func statusReservedInstance(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := FindReservedDBInstanceByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.State), nil + } +} + +func waitReservedInstanceCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) error { + stateConf := &retry.StateChangeConf{ + Pending: []string{ + ReservedInstanceStatePaymentPending, + }, + Target: []string{ReservedInstanceStateActive}, + Refresh: statusReservedInstance(ctx, conn, id), + NotFoundChecks: 5, + Timeout: timeout, + MinTimeout: 10 * time.Second, + Delay: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} + func flattenRecurringCharges(recurringCharges []*rds.RecurringCharge) []interface{} { if len(recurringCharges) == 0 { return []interface{}{} @@ -213,7 +278,7 @@ func flattenRecurringCharges(recurringCharges []*rds.RecurringCharge) []interfac for _, recurringCharge := range recurringCharges { rawRecurringCharge := map[string]interface{}{ "recurring_charge_amount": recurringCharge.RecurringChargeAmount, - "recurring_charge_frequency": aws.ToString(recurringCharge.RecurringChargeFrequency), + "recurring_charge_frequency": aws.StringValue(recurringCharge.RecurringChargeFrequency), } rawRecurringCharges = append(rawRecurringCharges, rawRecurringCharge) diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index ca119ec43a3..b72e26c3613 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -242,8 +242,9 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceClusterRoleAssociation, + Factory: resourceClusterRoleAssociation, TypeName: "aws_rds_cluster_role_association", + Name: "Cluster IAM Role Association", }, { Factory: ResourceCustomDBEngineVersion, diff --git a/internal/service/rds/status.go b/internal/service/rds/status.go deleted file mode 100644 index dd46e8e442c..00000000000 --- a/internal/service/rds/status.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package rds - -import ( - "context" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" -) - -func statusDBClusterRole(ctx context.Context, conn *rds.RDS, dbClusterID, roleARN string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindDBClusterRoleByDBClusterIDAndRoleARN(ctx, conn, dbClusterID, roleARN) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.StringValue(output.Status), nil - } -} - -func statusReservedInstance(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := FindReservedDBInstanceByID(ctx, conn, id) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.StringValue(output.State), nil - } -} diff --git a/internal/service/rds/wait.go b/internal/service/rds/wait.go deleted file mode 100644 index a639d5d064f..00000000000 --- a/internal/service/rds/wait.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package rds - -import ( - "context" - "time" - - "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" -) - -func waitDBClusterRoleAssociationCreated(ctx context.Context, conn *rds.RDS, dbClusterID, roleARN string, timeout time.Duration) (*rds.DBClusterRole, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{clusterRoleStatusPending}, - Target: []string{clusterRoleStatusActive}, - Refresh: statusDBClusterRole(ctx, conn, dbClusterID, roleARN), - Timeout: timeout, - MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*rds.DBClusterRole); ok { - return output, err - } - - return nil, err -} - -func waitDBClusterRoleAssociationDeleted(ctx context.Context, conn *rds.RDS, dbClusterID, roleARN string, timeout time.Duration) (*rds.DBClusterRole, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{clusterRoleStatusActive, clusterRoleStatusPending}, - Target: []string{}, - Refresh: statusDBClusterRole(ctx, conn, dbClusterID, roleARN), - Timeout: timeout, - MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*rds.DBClusterRole); ok { - return output, err - } - - return nil, err -} - -func waitDBClusterInstanceCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBInstance, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{ - InstanceStatusBackingUp, - InstanceStatusConfiguringEnhancedMonitoring, - InstanceStatusConfiguringIAMDatabaseAuth, - InstanceStatusConfiguringLogExports, - InstanceStatusCreating, - InstanceStatusMaintenance, - InstanceStatusModifying, - InstanceStatusRebooting, - InstanceStatusRenaming, - InstanceStatusResettingMasterCredentials, - InstanceStatusStarting, - InstanceStatusStorageOptimization, - InstanceStatusUpgrading, - }, - Target: []string{InstanceStatusAvailable}, - Refresh: statusDBInstanceSDKv1(ctx, conn, id), - Timeout: timeout, - MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*rds.DBInstance); ok { - return output, err - } - - return nil, err -} - -func waitDBClusterInstanceUpdated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBInstance, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{ - InstanceStatusBackingUp, - InstanceStatusConfiguringEnhancedMonitoring, - InstanceStatusConfiguringIAMDatabaseAuth, - InstanceStatusConfiguringLogExports, - InstanceStatusCreating, - InstanceStatusMaintenance, - InstanceStatusModifying, - InstanceStatusRebooting, - InstanceStatusRenaming, - InstanceStatusResettingMasterCredentials, - InstanceStatusStarting, - InstanceStatusStorageOptimization, - InstanceStatusUpgrading, - }, - Target: []string{InstanceStatusAvailable}, - Refresh: statusDBInstanceSDKv1(ctx, conn, id), - Timeout: timeout, - MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*rds.DBInstance); ok { - return output, err - } - - return nil, err -} - -func waitDBClusterInstanceDeleted(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBInstance, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{ - InstanceStatusConfiguringLogExports, - InstanceStatusDeletePreCheck, - InstanceStatusDeleting, - InstanceStatusModifying, - }, - Target: []string{}, - Refresh: statusDBInstanceSDKv1(ctx, conn, id), - Timeout: timeout, - MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if output, ok := outputRaw.(*rds.DBInstance); ok { - return output, err - } - - return nil, err -} - -func waitReservedInstanceCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) error { - stateConf := &retry.StateChangeConf{ - Pending: []string{ - ReservedInstanceStatePaymentPending, - }, - Target: []string{ReservedInstanceStateActive}, - Refresh: statusReservedInstance(ctx, conn, id), - NotFoundChecks: 5, - Timeout: timeout, - MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, - } - - _, err := stateConf.WaitForStateContext(ctx) - - return err -} From 8df863199eba577970621b493ac0ce69e9d9373e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 14:05:54 -0400 Subject: [PATCH 26/30] Fix semgrep 'ci.semgrep.pluginsdk.append-Read-to-diags'. --- internal/service/rds/snapshot_copy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/rds/snapshot_copy.go b/internal/service/rds/snapshot_copy.go index b6a62803240..2684948ed2a 100644 --- a/internal/service/rds/snapshot_copy.go +++ b/internal/service/rds/snapshot_copy.go @@ -291,7 +291,7 @@ func resourceSnapshotCopyUpdate(ctx context.Context, d *schema.ResourceData, met } } - return resourceSnapshotCopyRead(ctx, d, meta) + return append(diags, resourceSnapshotCopyRead(ctx, d, meta)...) } func resourceSnapshotCopyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { From 07d29de782e6cd954f311dd8321bfd32ef16bd04 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 14:07:54 -0400 Subject: [PATCH 27/30] Fix golangci-lint 'whitespace'. --- internal/service/rds/flex.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/service/rds/flex.go b/internal/service/rds/flex.go index db7d3e9a7dc..bef9051f62f 100644 --- a/internal/service/rds/flex.go +++ b/internal/service/rds/flex.go @@ -59,7 +59,6 @@ func flattenParameters(apiObject []types.Parameter) []interface{} { } apiObjects = append(apiObjects, tfMap) - } return apiObjects From 008a0f16015aef2078a0a4094f69383c0a40a77d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 14:09:10 -0400 Subject: [PATCH 28/30] Fix golangci-lint 'unparam'. --- internal/service/rds/snapshot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/rds/snapshot.go b/internal/service/rds/snapshot.go index 8bc3e39561a..782c7be4995 100644 --- a/internal/service/rds/snapshot.go +++ b/internal/service/rds/snapshot.go @@ -345,7 +345,7 @@ func statusDBSnapshot(ctx context.Context, conn *rds.Client, id string) retry.St } } -func waitDBSnapshotCreated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.DBSnapshot, error) { +func waitDBSnapshotCreated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.DBSnapshot, error) { //nolint:unparam stateConf := &retry.StateChangeConf{ Pending: []string{dbSnapshotCreating}, Target: []string{dbSnapshotAvailable}, From 336d2c0982a08efa889d276144486110d2c11fb1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 14:24:23 -0400 Subject: [PATCH 29/30] Fix terrafmt error. --- internal/service/rds/snapshot_copy_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/service/rds/snapshot_copy_test.go b/internal/service/rds/snapshot_copy_test.go index 1119d40198a..b558ff7e182 100644 --- a/internal/service/rds/snapshot_copy_test.go +++ b/internal/service/rds/snapshot_copy_test.go @@ -331,9 +331,9 @@ resource "aws_db_snapshot_copy" "test" { func testAccSnapshotCopyConfig_share(rName string) string { return acctest.ConfigCompose(testAccSnapshotCopyConfig_base(rName), fmt.Sprintf(` resource "aws_db_snapshot_copy" "test" { - source_db_snapshot_identifier = aws_db_snapshot.test.db_snapshot_arn - target_db_snapshot_identifier = "%[1]s-target" - shared_accounts = ["all"] + source_db_snapshot_identifier = aws_db_snapshot.test.db_snapshot_arn + target_db_snapshot_identifier = "%[1]s-target" + shared_accounts = ["all"] } `, rName)) } From 446ff1b5089abb711a62368855065d61237ead13 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jul 2024 15:20:44 -0400 Subject: [PATCH 30/30] rds: Skip 'no matching RDS Reserved Instance Offering found' acceptance test failures. --- internal/service/rds/cluster_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index 767465ed38b..bc838bca70a 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -58,6 +58,7 @@ func testAccErrorCheckSkip(t *testing.T) resource.ErrorCheckFunc { "requested engine version was not found or does not support parallelquery functionality", "Backtrack is not enabled for the aurora engine", "Read replica DB clusters are not available in this region for engine aurora", + "no matching RDS Reserved Instance Offering found", ) }