From 042158dfd0506384745d11c04601628fef6551ed Mon Sep 17 00:00:00 2001 From: Marco Hoyer Date: Wed, 22 Feb 2023 21:35:38 +0100 Subject: [PATCH 01/16] add skip_destroy option for rds option and parameter group resource --- internal/service/rds/option_group.go | 10 +++++++++- internal/service/rds/parameter_group.go | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/service/rds/option_group.go b/internal/service/rds/option_group.go index 0049b188fa62..2c5614c23279 100644 --- a/internal/service/rds/option_group.go +++ b/internal/service/rds/option_group.go @@ -123,7 +123,10 @@ func ResourceOptionGroup() *schema.Resource { }, Set: resourceOptionHash, }, - + "skip_destroy": { + Type: schema.TypeBool, + Optional: true, + }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), }, @@ -325,6 +328,11 @@ func resourceOptionGroupDelete(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSConn() + if _, ok := d.GetOk("skip_destroy"); ok { + log.Printf("[DEBUG] Retaining DB Option Group %q", d.Id()) + return diags + } + deleteOpts := &rds.DeleteOptionGroupInput{ OptionGroupName: aws.String(d.Id()), } diff --git a/internal/service/rds/parameter_group.go b/internal/service/rds/parameter_group.go index 631a5f82816e..8f327831b52a 100644 --- a/internal/service/rds/parameter_group.go +++ b/internal/service/rds/parameter_group.go @@ -90,6 +90,10 @@ func ResourceParameterGroup() *schema.Resource { }, Set: resourceParameterHash, }, + "skip_destroy": { + Type: schema.TypeBool, + Optional: true, + }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), }, @@ -341,6 +345,12 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { conn := meta.(*conns.AWSClient).RDSClient() + + if _, ok := d.GetOk("skip_destroy"); ok { + log.Printf("[DEBUG] Retaining DB Parameter Group %q", d.Id()) + return diags + } + input := &rds_sdkv2.DeleteDBParameterGroupInput{ DBParameterGroupName: aws.String(d.Id()), } From 0aba84a5b79db9ba15ba3cde92760fc27a55ffa0 Mon Sep 17 00:00:00 2001 From: Marco Hoyer Date: Wed, 22 Feb 2023 21:37:09 +0100 Subject: [PATCH 02/16] tests --- internal/service/rds/option_group_test.go | 98 ++++++++++++++++---- internal/service/rds/parameter_group_test.go | 63 +++++++++++++ 2 files changed, 145 insertions(+), 16 deletions(-) diff --git a/internal/service/rds/option_group_test.go b/internal/service/rds/option_group_test.go index 89c6a01ddb2a..2cf98fe06319 100644 --- a/internal/service/rds/option_group_test.go +++ b/internal/service/rds/option_group_test.go @@ -46,7 +46,7 @@ func TestAccRDSOptionGroup_basic(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -75,7 +75,7 @@ func TestAccRDSOptionGroup_timeoutBlock(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -102,7 +102,7 @@ func TestAccRDSOptionGroup_namePrefix(t *testing.T) { ResourceName: "aws_db_option_group.test", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -128,7 +128,7 @@ func TestAccRDSOptionGroup_generatedName(t *testing.T) { ResourceName: "aws_db_option_group.test", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -157,7 +157,7 @@ func TestAccRDSOptionGroup_optionGroupDescription(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -185,7 +185,7 @@ func TestAccRDSOptionGroup_basicDestroyWithInstance(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -220,7 +220,7 @@ func TestAccRDSOptionGroup_Option_optionSettings(t *testing.T) { ImportStateVerify: true, // Ignore option since our current logic skips "unconfigured" default option settings // Even with Config set, ImportState TestStep does not "see" the configuration to check against - ImportStateVerifyIgnore: []string{"name_prefix", "option"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy", "option"}, }, { Config: testAccOptionGroupConfig_optionSettingsUpdate(rName), @@ -238,7 +238,7 @@ func TestAccRDSOptionGroup_Option_optionSettings(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -269,7 +269,7 @@ func TestAccRDSOptionGroup_OptionOptionSettings_iamRole(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -298,7 +298,7 @@ func TestAccRDSOptionGroup_sqlServerOptionsUpdate(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, { Config: testAccOptionGroupConfig_sqlServerEEOptionsUpdate(rName), @@ -338,7 +338,7 @@ func TestAccRDSOptionGroup_oracleOptionsUpdate(t *testing.T) { ImportState: true, ImportStateVerify: true, // Ignore option since API responds with **** instead of password - ImportStateVerifyIgnore: []string{"name_prefix", "option"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy", "option"}, }, { Config: testAccOptionGroupConfig_oracleEEOptionSettings(rName, "13.3.0.0.v2"), @@ -377,7 +377,7 @@ func TestAccRDSOptionGroup_OptionOptionSettings_multipleNonDefault(t *testing.T) ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, { Config: testAccOptionGroupConfig_settingsMultiple(rName, "example1,example2"), @@ -414,7 +414,7 @@ func TestAccRDSOptionGroup_multipleOptions(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, }, }) @@ -444,7 +444,7 @@ func TestAccRDSOptionGroup_tags(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, { Config: testAccOptionGroupConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), @@ -493,7 +493,7 @@ func TestAccRDSOptionGroup_Tags_withOptions(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ImportStateVerifyIgnore: []string{"name_prefix", "skip_destroy"}, }, { Config: testAccOptionGroupConfig_tagsOption2(rName, "key1", "value1updated", "key2", "value2"), @@ -518,6 +518,58 @@ func TestAccRDSOptionGroup_Tags_withOptions(t *testing.T) { }) } +func TestAccCheckOptionGroup_skipDestroy(t *testing.T) { + var v rds.OptionGroup + ctx := acctest.Context(t) + resourceName := "aws_db_option_group.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckOptionGroupNoDestroy(ctx, t), + Steps: []resource.TestStep{ + { + Config: testAccOptionGroupConfig_skip_destroy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckOptionGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"), + ), + }, + }, + }) +} + +func testAccCheckOptionGroupNoDestroy(ctx context.Context, t *testing.T) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn() + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_db_option_group" { + continue + } + + opts := rds.DescribeOptionGroupsInput{ + OptionGroupName: aws.String(rs.Primary.ID), + } + _, err := conn.DescribeOptionGroupsWithContext(ctx, &opts) + + // cleanup option group if it was properly verified not to be destroyed by tf in the first place + if err == nil { + deleteOpts := rds.DeleteOptionGroupInput{ + OptionGroupName: aws.String(rs.Primary.ID), + } + _, _ = conn.DeleteOptionGroupWithContext(ctx, &deleteOpts) + } + + return err + } + + return nil + } +} + func testAccCheckOptionGroupOptionSettingsIAMRole(optionGroup *rds.OptionGroup) resource.TestCheckFunc { return func(s *terraform.State) error { if optionGroup == nil { @@ -623,7 +675,6 @@ func testAccCheckOptionGroupDestroy(ctx context.Context) resource.TestCheckFunc return nil } } - func testAccOptionGroupConfig_timeoutBlock(rName string) string { return fmt.Sprintf(` data "aws_rds_engine_version" "default" { @@ -698,6 +749,21 @@ resource "aws_db_option_group" "test" { `, mySQLPreferredInstanceClasses, rName) } +func testAccOptionGroupConfig_skip_destroy(rName string) string { + return fmt.Sprintf(` +data "aws_rds_engine_version" "default" { + engine = "mysql" +} + +resource "aws_db_option_group" "test" { + name = %[1]q + engine_name = data.aws_rds_engine_version.default.engine + major_engine_version = regex("^\\d+\\.\\d+", data.aws_rds_engine_version.default.version) + skip_destroy = true +} +`, rName) +} + func testAccOptionGroupConfig_optionSettings(rName string) string { return fmt.Sprintf(` data "aws_rds_engine_version" "default" { diff --git a/internal/service/rds/parameter_group_test.go b/internal/service/rds/parameter_group_test.go index 16ad1c284767..9b65cd04c40a 100644 --- a/internal/service/rds/parameter_group_test.go +++ b/internal/service/rds/parameter_group_test.go @@ -993,6 +993,58 @@ func TestDBParameterModifyChunk(t *testing.T) { } } +func TestAccCheckRDSParameterGroup_skipDestroy(t *testing.T) { + var v rds.DBParameterGroup + ctx := acctest.Context(t) + resourceName := "aws_db_parameter_group.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccRDSParameterGroupNoDestroy(ctx, t), + Steps: []resource.TestStep{ + { + Config: testAccParameterGroupConfig_skip_destroy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckParameterGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"), + ), + }, + }, + }) +} + +func testAccRDSParameterGroupNoDestroy(ctx context.Context, t *testing.T) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn() + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_db_parameter_group" { + continue + } + + opts := rds.DescribeDBParameterGroupsInput{ + DBParameterGroupName: aws.String(rs.Primary.ID), + } + _, err := conn.DescribeDBParameterGroupsWithContext(ctx, &opts) + + // cleanup option group if it was properly verified not to be destroyed by tf in the first place + if err == nil { + deleteOpts := rds.DeleteDBParameterGroupInput{ + DBParameterGroupName: aws.String(rs.Primary.ID), + } + _, _ = conn.DeleteDBParameterGroupWithContext(ctx, &deleteOpts) + } + + return err + } + + return nil + } +} + func testAccCheckParameterGroupDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn() @@ -1232,6 +1284,17 @@ resource "aws_db_parameter_group" "test" { `, rName) } +func testAccParameterGroupConfig_skip_destroy(rName string) string { + return fmt.Sprintf(` +resource "aws_db_parameter_group" "test" { + name = %[1]q + family = "mysql5.6" + description = "Test parameter group for terraform" + skip_destroy = true +} +`, rName) +} + func testAccParameterGroupConfig_exceedDefaultLimit(rName string) string { return fmt.Sprintf(` resource "aws_db_parameter_group" "test" { From 32d641048d42a054eae5f39b0166dcd60d12b95c Mon Sep 17 00:00:00 2001 From: Marco Hoyer Date: Sun, 26 Feb 2023 00:53:34 +0100 Subject: [PATCH 03/16] docs --- website/docs/r/db_option_group.html.markdown | 1 + website/docs/r/db_parameter_group.html.markdown | 1 + 2 files changed, 2 insertions(+) diff --git a/website/docs/r/db_option_group.html.markdown b/website/docs/r/db_option_group.html.markdown index 7433c38ed72d..2f7760e876d7 100644 --- a/website/docs/r/db_option_group.html.markdown +++ b/website/docs/r/db_option_group.html.markdown @@ -68,6 +68,7 @@ The following arguments are supported: * `engine_name` - (Required) Specifies the name of the engine that this option group should be associated with. * `major_engine_version` - (Required) Specifies the major version of the engine that this option group should be associated with. * `option` - (Optional) A list of Options to apply. +* `skip_destroy` - (Optional) Set to true if you do not wish the option group to be deleted at destroy time, and instead just remove the option group from the Terraform state. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. Option blocks support the following: diff --git a/website/docs/r/db_parameter_group.html.markdown b/website/docs/r/db_parameter_group.html.markdown index 07449124480d..fe638c9e3707 100644 --- a/website/docs/r/db_parameter_group.html.markdown +++ b/website/docs/r/db_parameter_group.html.markdown @@ -84,6 +84,7 @@ The following arguments are supported: * `family` - (Required, Forces new resource) The family of the DB parameter group. * `description` - (Optional, Forces new resource) The description of the DB parameter group. Defaults to "Managed by Terraform". * `parameter` - (Optional) A list of DB parameters to apply. Note that parameters may differ from a family to an other. Full list of all parameters can be discovered via [`aws rds describe-db-parameters`](https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-parameters.html) after initial creation of the group. +* `skip_destroy` - (Optional) Set to true if you do not wish the parameter group to be deleted at destroy time, and instead just remove the parameter group from the Terraform state. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. Parameter blocks support the following: From f6e4ce36421c503739f724bdc6a88a36c81e013d Mon Sep 17 00:00:00 2001 From: Marco Hoyer Date: Sun, 26 Feb 2023 01:09:01 +0100 Subject: [PATCH 04/16] add missing ImportStateVerifyIgnore --- internal/service/rds/parameter_group_test.go | 44 +++++++++++--------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/internal/service/rds/parameter_group_test.go b/internal/service/rds/parameter_group_test.go index 9b65cd04c40a..c5d382f9acaf 100644 --- a/internal/service/rds/parameter_group_test.go +++ b/internal/service/rds/parameter_group_test.go @@ -53,9 +53,10 @@ func TestAccRDSParameterGroup_basic(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"skip_destroy"}, }, { Config: testAccParameterGroupConfig_addParameters(rName), @@ -318,9 +319,10 @@ func TestAccRDSParameterGroup_limit(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"skip_destroy"}, }, { Config: testAccParameterGroupConfig_updateExceedDefaultLimit(rName), @@ -598,9 +600,10 @@ func TestAccRDSParameterGroup_withApplyMethod(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"skip_destroy"}, }, }, }) @@ -628,9 +631,10 @@ func TestAccRDSParameterGroup_only(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"skip_destroy"}, }, }, }) @@ -660,7 +664,7 @@ func TestAccRDSParameterGroup_matchDefault(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"parameter"}, + ImportStateVerifyIgnore: []string{"skip_destroy", "parameter"}, }, }, }) @@ -700,9 +704,10 @@ func TestAccRDSParameterGroup_updateParameters(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"skip_destroy"}, }, { Config: testAccParameterGroupConfig_updateParametersUpdated(rName), @@ -753,9 +758,10 @@ func TestAccRDSParameterGroup_caseParameters(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"skip_destroy"}, }, { Config: testAccParameterGroupConfig_upperCase(rName, "max_connections"), From 8dd1dcadd3bec58af244b0cef1037447f6775cd4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Aug 2024 15:01:16 -0400 Subject: [PATCH 05/16] Add CHANGELOG entries. --- .changelog/29663.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/29663.txt diff --git a/.changelog/29663.txt b/.changelog/29663.txt new file mode 100644 index 000000000000..2b5b500b5295 --- /dev/null +++ b/.changelog/29663.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_db_option_group: Add `skip_destroy` argument +``` + +```release-note:enhancement +resource/aws_db_parameter_group: Add `skip_destroy` argument +``` \ No newline at end of file From 8b8691a44cb2aa22d168e68a50308e5b43c8b206 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Aug 2024 15:27:34 -0400 Subject: [PATCH 06/16] Run 'make fix-constants PKG=rds'. --- internal/service/rds/option_group.go | 7 +++++-- internal/service/rds/option_group_test.go | 4 ++-- internal/service/rds/parameter_group.go | 8 ++++++-- internal/service/rds/parameter_group_test.go | 4 ++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/internal/service/rds/option_group.go b/internal/service/rds/option_group.go index 4871f5aa5821..1cadaf1754ed 100644 --- a/internal/service/rds/option_group.go +++ b/internal/service/rds/option_group.go @@ -129,9 +129,10 @@ func resourceOptionGroup() *schema.Resource { ForceNew: true, Default: "Managed by Terraform", }, - "skip_destroy": { + names.AttrSkipDestroy: { Type: schema.TypeBool, Optional: true, + Default: false, }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), @@ -190,6 +191,8 @@ func resourceOptionGroupRead(ctx context.Context, d *schema.ResourceData, meta i return sdkdiag.AppendErrorf(diags, "setting option: %s", err) } d.Set("option_group_description", option.OptionGroupDescription) + // Support in-place update of non-refreshable attribute. + d.Set(names.AttrSkipDestroy, d.Get(names.AttrSkipDestroy)) return diags } @@ -246,7 +249,7 @@ func resourceOptionGroupDelete(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSClient(ctx) - if _, ok := d.GetOk("skip_destroy"); ok { + if _, ok := d.GetOk(names.AttrSkipDestroy); ok { log.Printf("[DEBUG] Retaining RDS DB Option Group: %s", d.Id()) return diags } diff --git a/internal/service/rds/option_group_test.go b/internal/service/rds/option_group_test.go index a492edceb377..9f9d99b577da 100644 --- a/internal/service/rds/option_group_test.go +++ b/internal/service/rds/option_group_test.go @@ -601,7 +601,7 @@ func TestAccRDSOptionGroup_badDiffs(t *testing.T) { }) } -func TestAccCheckOptionGroup_skipDestroy(t *testing.T) { +func TestAccRDSOptionGroup_skipDestroy(t *testing.T) { var v types.OptionGroup ctx := acctest.Context(t) resourceName := "aws_db_option_group.test" @@ -617,7 +617,7 @@ func TestAccCheckOptionGroup_skipDestroy(t *testing.T) { Config: testAccOptionGroupConfig_skipDestroy(rName), Check: resource.ComposeTestCheckFunc( testAccCheckOptionGroupExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"), + resource.TestCheckResourceAttr(resourceName, names.AttrSkipDestroy, acctest.CtTrue), ), }, }, diff --git a/internal/service/rds/parameter_group.go b/internal/service/rds/parameter_group.go index c8e9cf0b339c..7f88932b6f4f 100644 --- a/internal/service/rds/parameter_group.go +++ b/internal/service/rds/parameter_group.go @@ -99,9 +99,10 @@ func resourceParameterGroup() *schema.Resource { }, Set: resourceParameterHash, }, - "skip_destroy": { + names.AttrSkipDestroy: { Type: schema.TypeBool, Optional: true, + Default: false, }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), @@ -229,6 +230,9 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met return sdkdiag.AppendErrorf(diags, "setting parameter: %s", err) } + // Support in-place update of non-refreshable attribute. + d.Set(names.AttrSkipDestroy, d.Get(names.AttrSkipDestroy)) + return diags } @@ -309,7 +313,7 @@ func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, m var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSClient(ctx) - if _, ok := d.GetOk("skip_destroy"); ok { + if _, ok := d.GetOk(names.AttrSkipDestroy); ok { log.Printf("[DEBUG] Retaining RDS DB Parameter Group: %s", d.Id()) return diags } diff --git a/internal/service/rds/parameter_group_test.go b/internal/service/rds/parameter_group_test.go index 14d9c547ee64..44fd5b631841 100644 --- a/internal/service/rds/parameter_group_test.go +++ b/internal/service/rds/parameter_group_test.go @@ -1045,7 +1045,7 @@ func TestAccRDSParameterGroup_caseParameters(t *testing.T) { }) } -func TestAccCheckRDSParameterGroup_skipDestroy(t *testing.T) { +func TestAccRDSParameterGroup_skipDestroy(t *testing.T) { var v types.DBParameterGroup ctx := acctest.Context(t) resourceName := "aws_db_parameter_group.test" @@ -1061,7 +1061,7 @@ func TestAccCheckRDSParameterGroup_skipDestroy(t *testing.T) { Config: testAccParameterGroupConfig_skipDestroy(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "skip_destroy", "true"), + resource.TestCheckResourceAttr(resourceName, names.AttrSkipDestroy, acctest.CtTrue), ), }, }, From e8dde1586cbe19fc7c90fbde114ebd126b43a7dd Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Aug 2024 17:06:51 -0400 Subject: [PATCH 07/16] r/aws_rds_global_cluster: Migrate to AWS SDK for Go v2. --- internal/service/rds/cluster.go | 12 +- internal/service/rds/cluster_instance.go | 2 +- internal/service/rds/cluster_migrate.go | 2 +- .../service/rds/cluster_role_association.go | 14 +- internal/service/rds/consts.go | 46 +- internal/service/rds/exports_test.go | 3 + internal/service/rds/global_cluster.go | 527 ++++++++---------- internal/service/rds/global_cluster_test.go | 61 +- internal/service/rds/instance.go | 2 +- internal/service/rds/instance_migrate.go | 4 +- internal/service/rds/service_package_gen.go | 3 +- internal/service/rds/sweep.go | 208 +++---- 12 files changed, 428 insertions(+), 456 deletions(-) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index df88e5f83839..6d7f9fd49202 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -227,7 +227,7 @@ func resourceCluster() *schema.Resource { ForceNew: true, ValidateFunc: validation.Any( validation.StringMatch(regexache.MustCompile(fmt.Sprintf(`^%s.*$`, InstanceEngineCustomPrefix)), fmt.Sprintf("must begin with %s", InstanceEngineCustomPrefix)), - validation.StringInSlice(ClusterEngine_Values(), false), + validation.StringInSlice(clusterEngine_Values(), false), ), }, "engine_lifecycle_support": { @@ -346,7 +346,7 @@ func resourceCluster() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validation.StringInSlice(NetworkType_Values(), false), + ValidateFunc: validation.StringInSlice(networkType_Values(), false), }, "performance_insights_enabled": { Type: schema.TypeBool, @@ -415,7 +415,7 @@ func resourceCluster() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validation.StringInSlice(RestoreType_Values(), false), + ValidateFunc: validation.StringInSlice(restoreType_Values(), false), }, "source_cluster_identifier": { Type: schema.TypeString, @@ -530,8 +530,8 @@ func resourceCluster() *schema.Resource { "timeout_action": { Type: schema.TypeString, Optional: true, - Default: TimeoutActionRollbackCapacityChange, - ValidateFunc: validation.StringInSlice(TimeoutAction_Values(), false), + Default: timeoutActionRollbackCapacityChange, + ValidateFunc: validation.StringInSlice(timeoutAction_Values(), false), }, }, }, @@ -1365,7 +1365,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set("global_cluster_identifier", "") if aws.StringValue(dbc.EngineMode) == engineModeGlobal || aws.StringValue(dbc.EngineMode) == engineModeProvisioned { - globalCluster, err := FindGlobalClusterByDBClusterARN(ctx, conn, aws.StringValue(dbc.DBClusterArn)) + globalCluster, err := findGlobalClusterByDBClusterARN(ctx, meta.(*conns.AWSClient).RDSClient(ctx), aws.StringValue(dbc.DBClusterArn)) if err == nil { d.Set("global_cluster_identifier", globalCluster.GlobalClusterIdentifier) diff --git a/internal/service/rds/cluster_instance.go b/internal/service/rds/cluster_instance.go index 9ed9701a95a5..9a1339d966fe 100644 --- a/internal/service/rds/cluster_instance.go +++ b/internal/service/rds/cluster_instance.go @@ -116,7 +116,7 @@ func ResourceClusterInstance() *schema.Resource { ForceNew: true, ValidateFunc: validation.Any( validation.StringMatch(regexache.MustCompile(fmt.Sprintf(`^%s.*$`, InstanceEngineCustomPrefix)), fmt.Sprintf("must begin with %s", InstanceEngineCustomPrefix)), - validation.StringInSlice(ClusterInstanceEngine_Values(), false), + validation.StringInSlice(clusterInstanceEngine_Values(), false), ), }, names.AttrEngineVersion: { diff --git a/internal/service/rds/cluster_migrate.go b/internal/service/rds/cluster_migrate.go index 66c8c7c24ad6..282c077ce92e 100644 --- a/internal/service/rds/cluster_migrate.go +++ b/internal/service/rds/cluster_migrate.go @@ -347,7 +347,7 @@ func resourceClusterResourceV0() *schema.Resource { "timeout_action": { Type: schema.TypeString, Optional: true, - Default: TimeoutActionRollbackCapacityChange, + Default: timeoutActionRollbackCapacityChange, }, }, }, diff --git a/internal/service/rds/cluster_role_association.go b/internal/service/rds/cluster_role_association.go index a99c025e9b3c..ee063ee3c530 100644 --- a/internal/service/rds/cluster_role_association.go +++ b/internal/service/rds/cluster_role_association.go @@ -249,12 +249,12 @@ func waitDBClusterRoleAssociationDeleted(ctx context.Context, conn *rds.Client, return nil, err } -// TODO Remove once aws_rds_cluster is migrated. -func findDBClusterByIDV2(ctx context.Context, conn *rds.Client, id string) (*types.DBCluster, error) { +// TODO Remove once aws_rds_cluster is migrated? +func findDBClusterByIDV2(ctx context.Context, conn *rds.Client, id string, optFns ...func(*rds.Options)) (*types.DBCluster, error) { input := &rds.DescribeDBClustersInput{ DBClusterIdentifier: aws.String(id), } - output, err := findDBClusterV2(ctx, conn, input, tfslices.PredicateTrue[*types.DBCluster]()) + output, err := findDBClusterV2(ctx, conn, input, tfslices.PredicateTrue[*types.DBCluster](), optFns...) if err != nil { return nil, err @@ -276,8 +276,8 @@ func findDBClusterByIDV2(ctx context.Context, conn *rds.Client, id string) (*typ 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) +func findDBClusterV2(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*types.DBCluster], optFns ...func(*rds.Options)) (*types.DBCluster, error) { + output, err := findDBClustersV2(ctx, conn, input, filter, optFns...) if err != nil { return nil, err @@ -286,12 +286,12 @@ func findDBClusterV2(ctx context.Context, conn *rds.Client, input *rds.DescribeD return tfresource.AssertSingleValueResult(output) } -func findDBClustersV2(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*types.DBCluster]) ([]types.DBCluster, error) { +func findDBClustersV2(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*types.DBCluster], optFns ...func(*rds.Options)) ([]types.DBCluster, error) { var output []types.DBCluster pages := rds.NewDescribeDBClustersPaginator(conn, input) for pages.HasMorePages() { - page, err := pages.NextPage(ctx) + page, err := pages.NextPage(ctx, optFns...) if errs.IsA[*types.DBClusterNotFoundFault](err) { return nil, &retry.NotFoundError{ diff --git a/internal/service/rds/consts.go b/internal/service/rds/consts.go index 299a99512889..f334769e935f 100644 --- a/internal/service/rds/consts.go +++ b/internal/service/rds/consts.go @@ -60,7 +60,7 @@ const ( storageTypeAuroraIOPT1 = "aurora-iopt1" ) -func StorageType_Values() []string { +func storageType_Values() []string { return []string{ storageTypeStandard, storageTypeGP2, @@ -126,11 +126,11 @@ const ( ) const ( - GlobalClusterStatusAvailable = "available" - GlobalClusterStatusCreating = "creating" - GlobalClusterStatusDeleting = "deleting" - GlobalClusterStatusModifying = "modifying" - GlobalClusterStatusUpgrading = "upgrading" + globalClusterStatusAvailable = "available" + globalClusterStatusCreating = "creating" + globalClusterStatusDeleting = "deleting" + globalClusterStatusModifying = "modifying" + globalClusterStatusUpgrading = "upgrading" ) const ( @@ -157,7 +157,7 @@ const ( ClusterEngineCustomPrefix = "custom-" ) -func ClusterEngine_Values() []string { +func clusterEngine_Values() []string { return []string{ ClusterEngineAuroraMySQL, ClusterEngineAuroraPostgreSQL, @@ -166,7 +166,7 @@ func ClusterEngine_Values() []string { } } -func ClusterInstanceEngine_Values() []string { +func clusterInstanceEngine_Values() []string { return []string{ ClusterEngineAuroraMySQL, ClusterEngineAuroraPostgreSQL, @@ -265,38 +265,38 @@ func InstanceExportableLogType_Values() []string { } const ( - NetworkTypeDual = "DUAL" - NetworkTypeIPv4 = "IPV4" + networkTypeDual = "DUAL" + networkTypeIPv4 = "IPV4" ) -func NetworkType_Values() []string { +func networkType_Values() []string { return []string{ - NetworkTypeDual, - NetworkTypeIPv4, + networkTypeDual, + networkTypeIPv4, } } const ( - RestoreTypeCopyOnWrite = "copy-on-write" - RestoreTypeFullCopy = "full-copy" + restoreTypeCopyOnWrite = "copy-on-write" + restoreTypeFullCopy = "full-copy" ) -func RestoreType_Values() []string { +func restoreType_Values() []string { return []string{ - RestoreTypeCopyOnWrite, - RestoreTypeFullCopy, + restoreTypeCopyOnWrite, + restoreTypeFullCopy, } } const ( - TimeoutActionForceApplyCapacityChange = "ForceApplyCapacityChange" - TimeoutActionRollbackCapacityChange = "RollbackCapacityChange" + timeoutActionForceApplyCapacityChange = "ForceApplyCapacityChange" + timeoutActionRollbackCapacityChange = "RollbackCapacityChange" ) -func TimeoutAction_Values() []string { +func timeoutAction_Values() []string { return []string{ - TimeoutActionForceApplyCapacityChange, - TimeoutActionRollbackCapacityChange, + timeoutActionForceApplyCapacityChange, + timeoutActionRollbackCapacityChange, } } diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index f85e8ab39594..68e331a6a30e 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -13,6 +13,7 @@ var ( ResourceClusterSnapshot = resourceClusterSnapshot ResourceCustomDBEngineVersion = resourceCustomDBEngineVersion ResourceEventSubscription = resourceEventSubscription + ResourceGlobalCluster = resourceGlobalCluster ResourceInstanceAutomatedBackupsReplication = resourceInstanceAutomatedBackupsReplication ResourceInstanceRoleAssociation = resourceInstanceRoleAssociation ResourceIntegration = newIntegrationResource @@ -27,6 +28,7 @@ var ( ResourceSnapshotCopy = resourceSnapshotCopy ResourceSubnetGroup = resourceSubnetGroup + ClusterIDAndRegionFromARN = clusterIDAndRegionFromARN FindCustomDBEngineVersionByTwoPartKey = findCustomDBEngineVersionByTwoPartKey FindDBClusterEndpointByID = findDBClusterEndpointByID FindDBClusterParameterGroupByName = findDBClusterParameterGroupByName @@ -44,6 +46,7 @@ var ( FindDefaultCertificate = findDefaultCertificate FindDefaultDBProxyTargetGroupByDBProxyName = findDefaultDBProxyTargetGroupByDBProxyName FindEventSubscriptionByID = findEventSubscriptionByID + FindGlobalClusterByID = findGlobalClusterByID FindIntegrationByARN = findIntegrationByARN FindOptionGroupByName = findOptionGroupByName FindReservedDBInstanceByID = findReservedDBInstanceByID diff --git a/internal/service/rds/global_cluster.go b/internal/service/rds/global_cluster.go index 56b08ce80395..f68218852bba 100644 --- a/internal/service/rds/global_cluster.go +++ b/internal/service/rds/global_cluster.go @@ -7,26 +7,29 @@ import ( "context" "fmt" "log" + "slices" "strings" "time" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws/arn" - "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/service/rds" + "github.com/aws/aws-sdk-go-v2/service/rds/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/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" ) -// @SDKResource("aws_rds_global_cluster") -func ResourceGlobalCluster() *schema.Resource { +// @SDKResource("aws_rds_global_cluster", name="Global Cluster") +func resourceGlobalCluster() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceGlobalClusterCreate, ReadWithoutTimeout: resourceGlobalClusterRead, @@ -131,7 +134,7 @@ func ResourceGlobalCluster() *schema.Resource { func resourceGlobalClusterCreate(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) globalClusterID := d.Get("global_cluster_identifier").(string) input := &rds.CreateGlobalClusterInput{ @@ -173,15 +176,15 @@ func resourceGlobalClusterCreate(ctx context.Context, d *schema.ResourceData, me input.Engine = aws.String(globalClusterEngineAurora) } - output, err := conn.CreateGlobalClusterWithContext(ctx, input) + output, err := conn.CreateGlobalCluster(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS Global Cluster (%s): %s", globalClusterID, err) } - d.SetId(aws.StringValue(output.GlobalCluster.GlobalClusterIdentifier)) + d.SetId(aws.ToString(output.GlobalCluster.GlobalClusterIdentifier)) - if err := waitGlobalClusterCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + if _, err := waitGlobalClusterCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS Global Cluster (%s) create: %s", d.Id(), err) } @@ -190,9 +193,9 @@ func resourceGlobalClusterCreate(ctx context.Context, d *schema.ResourceData, me func resourceGlobalClusterRead(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) - globalCluster, err := FindGlobalClusterByID(ctx, conn, d.Id()) + globalCluster, err := findGlobalClusterByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS Global Cluster (%s) not found, removing from state", d.Id()) @@ -216,8 +219,7 @@ func resourceGlobalClusterRead(ctx context.Context, d *schema.ResourceData, meta d.Set("global_cluster_resource_id", globalCluster.GlobalClusterResourceId) d.Set(names.AttrStorageEncrypted, globalCluster.StorageEncrypted) - oldEngineVersion := d.Get(names.AttrEngineVersion).(string) - newEngineVersion := aws.StringValue(globalCluster.EngineVersion) + oldEngineVersion, newEngineVersion := d.Get(names.AttrEngineVersion).(string), aws.ToString(globalCluster.EngineVersion) // For example a configured engine_version of "5.6.10a" and a returned engine_version of "5.6.global_10a". if oldParts, newParts := strings.Split(oldEngineVersion, "."), strings.Split(newEngineVersion, "."); len(oldParts) == 3 && //nolint:gocritic // Ignore 'badCond' @@ -237,23 +239,22 @@ func resourceGlobalClusterRead(ctx context.Context, d *schema.ResourceData, meta func resourceGlobalClusterUpdate(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(names.AttrEngineVersion) { + if err := globalClusterUpgradeEngineVersion(ctx, conn, d, d.Timeout(schema.TimeoutUpdate)); err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + } input := &rds.ModifyGlobalClusterInput{ DeletionProtection: aws.Bool(d.Get(names.AttrDeletionProtection).(bool)), GlobalClusterIdentifier: aws.String(d.Id()), } - if d.HasChange(names.AttrEngineVersion) { - if err := globalClusterUpgradeEngineVersion(ctx, d, meta, d.Timeout(schema.TimeoutUpdate)); err != nil { - return sdkdiag.AppendErrorf(diags, "updating RDS Global Cluster (%s): %s", d.Id(), err) - } - } + _, err := conn.ModifyGlobalCluster(ctx, input) - log.Printf("[DEBUG] Updating RDS Global Cluster (%s): %s", d.Id(), input) - _, err := conn.ModifyGlobalClusterWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeGlobalClusterNotFoundFault) { + if errs.IsA[*types.GlobalClusterNotFoundFault](err) { return diags } @@ -261,7 +262,7 @@ func resourceGlobalClusterUpdate(ctx context.Context, d *schema.ResourceData, me return sdkdiag.AppendErrorf(diags, "updating RDS Global Cluster (%s): %s", d.Id(), err) } - if err := waitGlobalClusterUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { + if _, err := waitGlobalClusterUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS Global Cluster (%s) update: %s", d.Id(), err) } @@ -270,51 +271,49 @@ func resourceGlobalClusterUpdate(ctx context.Context, d *schema.ResourceData, me func resourceGlobalClusterDelete(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) deadline := tfresource.NewDeadline(d.Timeout(schema.TimeoutDelete)) if d.Get(names.AttrForceDestroy).(bool) { log.Printf("[DEBUG] Removing cluster members from RDS Global Cluster: %s", d.Id()) - // The writer cluster must be removed last + // The writer cluster must be removed last. var writerARN string globalClusterMembers := d.Get("global_cluster_members").(*schema.Set) if globalClusterMembers.Len() > 0 { - for _, globalClusterMemberRaw := range globalClusterMembers.List() { - globalClusterMember, ok := globalClusterMemberRaw.(map[string]interface{}) - + for _, tfMapRaw := range globalClusterMembers.List() { + tfMap, ok := tfMapRaw.(map[string]interface{}) if !ok { continue } - dbClusterArn, ok := globalClusterMember["db_cluster_arn"].(string) - + dbClusterARN, ok := tfMap["db_cluster_arn"].(string) if !ok { continue } - if globalClusterMember["is_writer"].(bool) { - writerARN = dbClusterArn + if tfMap["is_writer"].(bool) { + writerARN = dbClusterARN continue } input := &rds.RemoveFromGlobalClusterInput{ - DbClusterIdentifier: aws.String(dbClusterArn), + DbClusterIdentifier: aws.String(dbClusterARN), GlobalClusterIdentifier: aws.String(d.Id()), } - _, err := conn.RemoveFromGlobalClusterWithContext(ctx, input) + _, err := conn.RemoveFromGlobalCluster(ctx, input) - if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "is not found in global cluster") { + if tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "is not found in global cluster") { continue } if err != nil { - return sdkdiag.AppendErrorf(diags, "removing RDS DB Cluster (%s) from Global Cluster (%s): %s", dbClusterArn, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "removing RDS DB Cluster (%s) from RDS Global Cluster (%s): %s", dbClusterARN, d.Id(), err) } - if err := waitForGlobalClusterRemoval(ctx, conn, dbClusterArn, deadline.Remaining()); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Cluster (%s) removal from RDS Global Cluster (%s): %s", dbClusterArn, d.Id(), err) + if _, err := waitGlobalClusterMemberRemoved(ctx, conn, dbClusterARN, deadline.Remaining()); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Cluster (%s) removal from RDS Global Cluster (%s): %s", dbClusterARN, d.Id(), err) } } @@ -323,23 +322,20 @@ func resourceGlobalClusterDelete(ctx context.Context, d *schema.ResourceData, me GlobalClusterIdentifier: aws.String(d.Id()), } - _, err := conn.RemoveFromGlobalClusterWithContext(ctx, input) + _, err := conn.RemoveFromGlobalCluster(ctx, input) + if err != nil { - if !tfawserr.ErrMessageContains(err, "InvalidParameterValue", "is not found in global cluster") { - return sdkdiag.AppendErrorf(diags, "removing RDS DB Cluster (%s) from Global Cluster (%s): %s", writerARN, d.Id(), err) + if !tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "is not found in global cluster") { + return sdkdiag.AppendErrorf(diags, "removing RDS DB Cluster (%s) from RDS Global Cluster (%s): %s", writerARN, d.Id(), err) } } - if err := waitForGlobalClusterRemoval(ctx, conn, writerARN, deadline.Remaining()); err != nil { + if _, err := waitGlobalClusterMemberRemoved(ctx, conn, writerARN, deadline.Remaining()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Cluster (%s) removal from RDS Global Cluster (%s): %s", writerARN, d.Id(), err) } } } - input := &rds.DeleteGlobalClusterInput{ - GlobalClusterIdentifier: aws.String(d.Id()), - } - log.Printf("[DEBUG] Deleting RDS Global Cluster: %s", d.Id()) // Allow for eventual consistency @@ -355,71 +351,56 @@ func resourceGlobalClusterDelete(ctx context.Context, d *schema.ResourceData, me } else { timeout = y } - err := retry.RetryContext(ctx, timeout, func() *retry.RetryError { - _, err := conn.DeleteGlobalClusterWithContext(ctx, input) - - if tfawserr.ErrMessageContains(err, rds.ErrCodeInvalidGlobalClusterStateFault, "is not empty") { - return retry.RetryableError(err) - } - - if err != nil { - return retry.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.DeleteGlobalClusterWithContext(ctx, input) - } + _, err := tfresource.RetryWhenIsAErrorMessageContains[*types.InvalidGlobalClusterStateFault](ctx, timeout, func() (interface{}, error) { + return conn.DeleteGlobalCluster(ctx, &rds.DeleteGlobalClusterInput{ + GlobalClusterIdentifier: aws.String(d.Id()), + }) + }, "is not empty") - if tfawserr.ErrCodeEquals(err, rds.ErrCodeGlobalClusterNotFoundFault) { + if errs.IsA[*types.GlobalClusterNotFoundFault](err) { return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting RDS Global Cluster: %s", err) + return sdkdiag.AppendErrorf(diags, "deleting RDS Global Cluster (%s): %s", d.Id(), err) } - if err := waitGlobalClusterDeleted(ctx, conn, d.Id(), deadline.Remaining()); err != nil { + if _, err := waitGlobalClusterDeleted(ctx, conn, d.Id(), deadline.Remaining()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS Global Cluster (%s) delete: %s", d.Id(), err) } return diags } -func FindGlobalClusterByDBClusterARN(ctx context.Context, conn *rds.RDS, dbClusterARN string) (*rds.GlobalCluster, error) { +func findGlobalClusterByDBClusterARN(ctx context.Context, conn *rds.Client, dbClusterARN string) (*types.GlobalCluster, error) { input := &rds.DescribeGlobalClustersInput{ - Filters: []*rds.Filter{ + Filters: []types.Filter{ { Name: aws.String("db-cluster-id"), - Values: aws.StringSlice([]string{dbClusterARN}), + Values: []string{dbClusterARN}, }, }, } - return findGlobalCluster(ctx, conn, input, func(v *rds.GlobalCluster) bool { - for _, v := range v.GlobalClusterMembers { - if aws.StringValue(v.DBClusterArn) == dbClusterARN { - return true - } - } - return false + return findGlobalCluster(ctx, conn, input, func(v *types.GlobalCluster) bool { + return slices.ContainsFunc(v.GlobalClusterMembers, func(v types.GlobalClusterMember) bool { + return aws.ToString(v.DBClusterArn) == dbClusterARN + }) }) } -func FindGlobalClusterByID(ctx context.Context, conn *rds.RDS, id string) (*rds.GlobalCluster, error) { +func findGlobalClusterByID(ctx context.Context, conn *rds.Client, id string) (*types.GlobalCluster, error) { input := &rds.DescribeGlobalClustersInput{ GlobalClusterIdentifier: aws.String(id), } - output, err := findGlobalCluster(ctx, conn, input, tfslices.PredicateTrue[*rds.GlobalCluster]()) + output, err := findGlobalCluster(ctx, conn, input, tfslices.PredicateTrue[*types.GlobalCluster]()) if err != nil { return nil, err } // Eventual consistency check. - if aws.StringValue(output.GlobalClusterIdentifier) != id { + if aws.ToString(output.GlobalClusterIdentifier) != id { return nil, &retry.NotFoundError{ LastRequest: input, } @@ -428,69 +409,47 @@ func FindGlobalClusterByID(ctx context.Context, conn *rds.RDS, id string) (*rds. return output, nil } -func findGlobalCluster(ctx context.Context, conn *rds.RDS, input *rds.DescribeGlobalClustersInput, filter tfslices.Predicate[*rds.GlobalCluster]) (*rds.GlobalCluster, error) { +func findGlobalCluster(ctx context.Context, conn *rds.Client, input *rds.DescribeGlobalClustersInput, filter tfslices.Predicate[*types.GlobalCluster]) (*types.GlobalCluster, error) { output, err := findGlobalClusters(ctx, conn, input, filter) if err != nil { return nil, err } - return tfresource.AssertSinglePtrResult(output) + return tfresource.AssertSingleValueResult(output) } -func findGlobalClusters(ctx context.Context, conn *rds.RDS, input *rds.DescribeGlobalClustersInput, filter tfslices.Predicate[*rds.GlobalCluster]) ([]*rds.GlobalCluster, error) { - var output []*rds.GlobalCluster +func findGlobalClusters(ctx context.Context, conn *rds.Client, input *rds.DescribeGlobalClustersInput, filter tfslices.Predicate[*types.GlobalCluster]) ([]types.GlobalCluster, error) { + var output []types.GlobalCluster - err := conn.DescribeGlobalClustersPagesWithContext(ctx, input, func(page *rds.DescribeGlobalClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + pages := rds.NewDescribeGlobalClustersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - for _, v := range page.GlobalClusters { - if v != nil && filter(v) { - output = append(output, v) + if errs.IsA[*types.GlobalClusterNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, } } - return !lastPage - }) - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeGlobalClusterNotFoundFault) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, + if err != nil { + return nil, err } - } - - if err != nil { - return nil, err - } - - return output, nil -} -func flattenGlobalClusterMembers(apiObjects []*rds.GlobalClusterMember) []interface{} { - if len(apiObjects) == 0 { - return nil - } - - var tfList []interface{} - - for _, apiObject := range apiObjects { - tfMap := map[string]interface{}{ - "db_cluster_arn": aws.StringValue(apiObject.DBClusterArn), - "is_writer": aws.BoolValue(apiObject.IsWriter), + for _, v := range page.GlobalClusters { + if filter(&v) { + output = append(output, v) + } } - - tfList = append(tfList, tfMap) } - return tfList + return output, nil } -func statusGlobalCluster(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc { +func statusGlobalCluster(ctx context.Context, conn *rds.Client, id string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindGlobalClusterByID(ctx, conn, id) + output, err := findGlobalClusterByID(ctx, conn, id) if tfresource.NotFound(err) { return nil, "", nil @@ -500,57 +459,73 @@ func statusGlobalCluster(ctx context.Context, conn *rds.RDS, id string) retry.St return nil, "", err } - return output, aws.StringValue(output.Status), nil + return output, aws.ToString(output.Status), nil } } -func waitGlobalClusterCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) error { +func waitGlobalClusterCreated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.GlobalCluster, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{GlobalClusterStatusCreating}, - Target: []string{GlobalClusterStatusAvailable}, + Pending: []string{globalClusterStatusCreating}, + Target: []string{globalClusterStatusAvailable}, Refresh: statusGlobalCluster(ctx, conn, id), Timeout: timeout, } - _, err := stateConf.WaitForStateContext(ctx) + outputRaw, err := stateConf.WaitForStateContext(ctx) - return err + if output, ok := outputRaw.(*types.GlobalCluster); ok { + return output, err + } + + return nil, err } -func waitGlobalClusterUpdated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) error { +func waitGlobalClusterUpdated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.GlobalCluster, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{GlobalClusterStatusModifying, GlobalClusterStatusUpgrading}, - Target: []string{GlobalClusterStatusAvailable}, + Pending: []string{globalClusterStatusModifying, globalClusterStatusUpgrading}, + Target: []string{globalClusterStatusAvailable}, Refresh: statusGlobalCluster(ctx, conn, id), Timeout: timeout, Delay: 30 * time.Second, } - _, err := stateConf.WaitForStateContext(ctx) + outputRaw, err := stateConf.WaitForStateContext(ctx) - return err + if output, ok := outputRaw.(*types.GlobalCluster); ok { + return output, err + } + + return nil, err } -func waitGlobalClusterDeleted(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) error { +func waitGlobalClusterDeleted(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.GlobalCluster, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{GlobalClusterStatusAvailable, GlobalClusterStatusDeleting}, + Pending: []string{globalClusterStatusAvailable, globalClusterStatusDeleting}, Target: []string{}, Refresh: statusGlobalCluster(ctx, conn, id), Timeout: timeout, NotFoundChecks: 1, } - _, err := stateConf.WaitForStateContext(ctx) + outputRaw, err := stateConf.WaitForStateContext(ctx) - return err + if output, ok := outputRaw.(*types.GlobalCluster); ok { + return output, err + } + + return nil, err } -func waitForGlobalClusterRemoval(ctx context.Context, conn *rds.RDS, dbClusterARN string, timeout time.Duration) error { - _, err := tfresource.RetryUntilNotFound(ctx, timeout, func() (interface{}, error) { - return FindGlobalClusterByDBClusterARN(ctx, conn, dbClusterARN) +func waitGlobalClusterMemberRemoved(ctx context.Context, conn *rds.Client, dbClusterARN string, timeout time.Duration) (*types.GlobalCluster, error) { + outputRaw, err := tfresource.RetryUntilNotFound(ctx, timeout, func() (interface{}, error) { + return findGlobalClusterByDBClusterARN(ctx, conn, dbClusterARN) }) - return err + if output, ok := outputRaw.(*types.GlobalCluster); ok { + return output, err + } + + return nil, err } // globalClusterUpgradeEngineVersion upgrades the engine version of the RDS Global Cluster, accommodating @@ -563,23 +538,21 @@ func waitForGlobalClusterRemoval(ctx context.Context, conn *rds.RDS, dbClusterAR // IMPORTANT: Altering the error handling in `globalClusterUpgradeMajorEngineVersion` can disrupt the // logic, including the handling of errors that signify the need for a minor version upgrade. -func globalClusterUpgradeEngineVersion(ctx context.Context, d *schema.ResourceData, meta interface{}, timeout time.Duration) error { +func globalClusterUpgradeEngineVersion(ctx context.Context, conn *rds.Client, d *schema.ResourceData, timeout time.Duration) error { log.Printf("[DEBUG] Upgrading RDS Global Cluster (%s) engine version: %s", d.Id(), d.Get(names.AttrEngineVersion)) - err := globalClusterUpgradeMajorEngineVersion(ctx, meta, d.Id(), d.Get(names.AttrEngineVersion).(string), timeout) - - if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "only supports Major Version Upgrades") { - err = globalClusterUpgradeMinorEngineVersion(ctx, meta, d.Get("global_cluster_members").(*schema.Set), d.Id(), d.Get(names.AttrEngineVersion).(string), timeout) + err := globalClusterUpgradeMajorEngineVersion(ctx, conn, d.Id(), d.Get(names.AttrEngineVersion).(string), timeout) - if err != nil { - return fmt.Errorf("while upgrading minor version of RDS Global Cluster (%s): %w", d.Id(), err) + if tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "only supports Major Version Upgrades") { + if err := globalClusterUpgradeMinorEngineVersion(ctx, conn, d.Id(), d.Get(names.AttrEngineVersion).(string), d.Get("global_cluster_members").(*schema.Set), timeout); err != nil { + return fmt.Errorf("upgrading minor version of RDS Global Cluster (%s): %w", d.Id(), err) } return nil } if err != nil { - return fmt.Errorf("while upgrading major version of RDS Global Cluster (%s): %w", d.Id(), err) + return fmt.Errorf("upgrading major version of RDS Global Cluster (%s): %w", d.Id(), err) } return nil @@ -589,189 +562,168 @@ func globalClusterUpgradeEngineVersion(ctx context.Context, d *schema.ResourceDa // error that is more than just an error but also indicates a minor version should be tried. It's IMPORTANT // to not handle this like any other error, such as, for example, retrying the error, since it indicates // another branch of logic. Please use caution when updating!! -func globalClusterUpgradeMajorEngineVersion(ctx context.Context, meta interface{}, clusterID string, engineVersion string, timeout time.Duration) error { - conn := meta.(*conns.AWSClient).RDSConn(ctx) - +func globalClusterUpgradeMajorEngineVersion(ctx context.Context, conn *rds.Client, globalClusterID, engineVersion string, timeout time.Duration) error { input := &rds.ModifyGlobalClusterInput{ AllowMajorVersionUpgrade: aws.Bool(true), EngineVersion: aws.String(engineVersion), - GlobalClusterIdentifier: aws.String(clusterID), + GlobalClusterIdentifier: aws.String(globalClusterID), } - err := retry.RetryContext(ctx, timeout, func() *retry.RetryError { - _, err := conn.ModifyGlobalClusterWithContext(ctx, input) - if err != nil { - if tfawserr.ErrCodeEquals(err, rds.ErrCodeGlobalClusterNotFoundFault) { - return retry.NonRetryableError(err) + _, err := tfresource.RetryWhen(ctx, timeout, + func() (interface{}, error) { + return conn.ModifyGlobalCluster(ctx, input) + }, + func(err error) (bool, error) { + if errs.IsA[*types.GlobalClusterNotFoundFault](err) { + return false, err } - if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "only supports Major Version Upgrades") { - return retry.NonRetryableError(err) // NOT retryable !! AND indicates this should be a minor version upgrade + if tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "only supports Major Version Upgrades") { + return false, err // NOT retryable !! AND indicates this should be a minor version upgrade } - return retry.RetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.ModifyGlobalClusterWithContext(ctx, input) - } + return true, err + }, + ) if err != nil { - return fmt.Errorf("while upgrading major version of RDS Global Cluster (%s): %w", clusterID, err) + return fmt.Errorf("modifying RDS Global Cluster (%s) EngineVersion: %w", globalClusterID, err) } - globalCluster, err := FindGlobalClusterByID(ctx, conn, clusterID) + globalCluster, err := findGlobalClusterByID(ctx, conn, globalClusterID) if err != nil { - return fmt.Errorf("while upgrading major version of RDS Global Cluster (%s): %w", clusterID, err) + return fmt.Errorf("after major engine_version upgrade to RDS Global Cluster (%s): %w", globalClusterID, err) } for _, clusterMember := range globalCluster.GlobalClusterMembers { - arnID := aws.StringValue(clusterMember.DBClusterArn) + memberARN := aws.ToString(clusterMember.DBClusterArn) - if arnID == "" { + if memberARN == "" { continue } - dbi, clusterRegion, err := ClusterIDRegionFromARN(arnID) + clusterID, clusterRegion, err := clusterIDAndRegionFromARN(memberARN) if err != nil { - return fmt.Errorf("while upgrading RDS Global Cluster Cluster major engine version: %w", err) + return err } - if dbi == "" { + if clusterID == "" { continue } - // Clusters may not all be in the same region. - useConn := meta.(*conns.AWSClient).RDSConnForRegion(ctx, clusterRegion) + optFn := func(o *rds.Options) { + o.Region = clusterRegion + } - if err := WaitForClusterUpdate(ctx, useConn, dbi, timeout); err != nil { - return fmt.Errorf("failed to update engine_version, waiting for RDS Global Cluster (%s) to update: %s", dbi, err) + if _, err := waitGlobalClusterMemberUpdated(ctx, conn, clusterID, timeout, optFn); err != nil { + return fmt.Errorf("waiting for RDS Global Cluster (%s) member (%s) update: %w", globalClusterID, clusterID, err) } } return err } -func globalClusterUpgradeMinorEngineVersion(ctx context.Context, meta interface{}, clusterMembers *schema.Set, clusterID, engineVersion string, timeout time.Duration) error { - conn := meta.(*conns.AWSClient).RDSConn(ctx) - - log.Printf("[INFO] Performing RDS Global Cluster (%s) minor version (%s) upgrade", clusterID, engineVersion) +func globalClusterUpgradeMinorEngineVersion(ctx context.Context, conn *rds.Client, globalClusterID, engineVersion string, clusterMembers *schema.Set, timeout time.Duration) error { + log.Printf("[INFO] Performing RDS Global Cluster (%s) minor version (%s) upgrade", globalClusterID, engineVersion) leelooMultiPass := false // only one pass is needed - for _, clusterMemberRaw := range clusterMembers.List() { - clusterMember := clusterMemberRaw.(map[string]interface{}) + for _, tfMapRaw := range clusterMembers.List() { + tfMap := tfMapRaw.(map[string]interface{}) // DBClusterIdentifier supposedly can be either ARN or ID, and both used to work, - // but as of now, only ID works - if clusterMemberArn, ok := clusterMember["db_cluster_arn"]; !ok || clusterMemberArn.(string) == "" { + // but as of now, only ID works. + if memberARN, ok := tfMap["db_cluster_arn"]; !ok || memberARN.(string) == "" { continue } - arnID := clusterMember["db_cluster_arn"].(string) + memberARN := tfMap["db_cluster_arn"].(string) - dbi, clusterRegion, err := ClusterIDRegionFromARN(arnID) + clusterID, clusterRegion, err := clusterIDAndRegionFromARN(memberARN) if err != nil { - return fmt.Errorf("while upgrading RDS Global Cluster Cluster minor engine version: %w", err) + return err } - if dbi == "" { + if clusterID == "" { continue } - useConn := meta.(*conns.AWSClient).RDSConnForRegion(ctx, clusterRegion) + optFn := func(o *rds.Options) { + o.Region = clusterRegion + } // pre-wait for the cluster to be in a state where it can be updated - if err := WaitForClusterUpdate(ctx, useConn, dbi, timeout); err != nil { - return fmt.Errorf("failed to update engine_version, waiting for RDS Global Cluster Cluster (%s) to update: %s", dbi, err) + if _, err := waitGlobalClusterMemberUpdated(ctx, conn, clusterID, timeout, optFn); err != nil { + return fmt.Errorf("waiting for RDS Global Cluster (%s) member (%s) update: %w", globalClusterID, clusterID, err) } - modInput := &rds.ModifyDBClusterInput{ + input := &rds.ModifyDBClusterInput{ ApplyImmediately: aws.Bool(true), - DBClusterIdentifier: aws.String(dbi), + DBClusterIdentifier: aws.String(clusterID), EngineVersion: aws.String(engineVersion), } - log.Printf("[INFO] Performing RDS Global Cluster (%s) Cluster (%s) minor version (%s) upgrade", clusterID, dbi, engineVersion) - - err = retry.RetryContext(ctx, timeout, func() *retry.RetryError { - _, err := useConn.ModifyDBClusterWithContext(ctx, modInput) - 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) + log.Printf("[INFO] Performing RDS Global Cluster (%s) Cluster (%s) minor version (%s) upgrade", globalClusterID, clusterID, engineVersion) + _, err = tfresource.RetryWhen(ctx, timeout, + func() (interface{}, error) { + return conn.ModifyDBCluster(ctx, input, optFn) + }, + func(err error) (bool, error) { + if tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "IAM role ARN value is invalid or does not include the required permissions") { + return true, err } - if tfawserr.ErrMessageContains(err, rds.ErrCodeInvalidDBClusterStateFault, "Cannot modify engine version without a primary instance in DB cluster") { - return retry.NonRetryableError(err) + if errs.IsAErrorMessageContains[*types.InvalidDBClusterStateFault](err, "Cannot modify engine version without a primary instance in DB cluster") { + return false, err } - if tfawserr.ErrCodeEquals(err, rds.ErrCodeInvalidDBClusterStateFault) { - return retry.RetryableError(err) + if errs.IsA[*types.InvalidDBClusterStateFault](err) { + return true, err } - return retry.NonRetryableError(err) - } - return nil - }) - - if tfresource.TimedOut(err) { - _, err := useConn.ModifyDBClusterWithContext(ctx, modInput) - if err != nil { - return err - } - } + return false, err + }, + ) - if tfawserr.ErrMessageContains(err, "InvalidGlobalClusterStateFault", "is upgrading") { + if errs.IsAErrorMessageContains[*types.InvalidGlobalClusterStateFault](err, "is upgrading") { leelooMultiPass = true continue } - if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "upgrade global replicas first") { + if tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "upgrade global replicas first") { leelooMultiPass = true continue } if err != nil { - return fmt.Errorf("failed to update engine_version on RDS Global Cluster Cluster (%s): %s", dbi, err) + return fmt.Errorf("modifying RDS Global Cluster (%s) member (%s) EngineVersion: %w", globalClusterID, clusterID, err) } - log.Printf("[INFO] Waiting for RDS Global Cluster (%s) Cluster (%s) minor version (%s) upgrade", clusterID, dbi, engineVersion) - if err := WaitForClusterUpdate(ctx, useConn, dbi, timeout); err != nil { - return fmt.Errorf("failed to update engine_version, waiting for RDS Global Cluster Cluster (%s) to update: %s", dbi, err) + if _, err := waitGlobalClusterMemberUpdated(ctx, conn, clusterID, timeout, optFn); err != nil { + return fmt.Errorf("waiting for RDS Global Cluster (%s) member (%s) update: %w", globalClusterID, clusterID, err) } } - globalCluster, err := FindGlobalClusterByID(ctx, conn, clusterID) - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeGlobalClusterNotFoundFault) { - return fmt.Errorf("after upgrading engine_version, could not find RDS Global Cluster (%s): %s", clusterID, err) - } + globalCluster, err := findGlobalClusterByID(ctx, conn, globalClusterID) if err != nil { - return fmt.Errorf("after minor engine_version upgrade to RDS Global Cluster (%s): %s", clusterID, err) + return fmt.Errorf("after minor engine_version upgrade to RDS Global Cluster (%s) members: %w", globalClusterID, err) } - if globalCluster == nil { - return fmt.Errorf("after minor engine_version upgrade to RDS Global Cluster (%s): empty response", clusterID) - } - - if leelooMultiPass || aws.StringValue(globalCluster.EngineVersion) != engineVersion { - log.Printf("[DEBUG] RDS Global Cluster (%s) upgrade did not take effect, trying again", clusterID) + if leelooMultiPass || aws.ToString(globalCluster.EngineVersion) != engineVersion { + log.Printf("[DEBUG] RDS Global Cluster (%s) upgrade did not take effect, trying again", globalClusterID) - return globalClusterUpgradeMinorEngineVersion(ctx, meta, clusterMembers, clusterID, engineVersion, timeout) + return globalClusterUpgradeMinorEngineVersion(ctx, conn, globalClusterID, engineVersion, clusterMembers, timeout) } return nil } -func ClusterIDRegionFromARN(arnID string) (string, string, error) { - parsedARN, err := arn.Parse(arnID) +func clusterIDAndRegionFromARN(clusterARN string) (string, string, error) { + parsedARN, err := arn.Parse(clusterARN) if err != nil { - return "", "", fmt.Errorf("could not parse ARN (%s): %w", arnID, err) + return "", "", fmt.Errorf("could not parse ARN (%s): %w", clusterARN, err) } dbi := "" @@ -780,11 +732,11 @@ func ClusterIDRegionFromARN(arnID string) (string, string, error) { parts := strings.Split(parsedARN.Resource, ":") if len(parts) < 2 { - return "", "", fmt.Errorf("could not get DB Cluster ID from parsing ARN (%s): %w", arnID, err) + return "", "", fmt.Errorf("could not get DB Cluster ID from parsing ARN (%s): %w", clusterARN, err) } - if parsedARN.Service != rds.EndpointsID || parts[0] != "cluster" { - return "", "", fmt.Errorf("wrong ARN (%s) for a DB Cluster", arnID) + if parsedARN.Service != "rds" || parts[0] != "cluster" { + return "", "", fmt.Errorf("wrong ARN (%s) for a DB Cluster", clusterARN) } dbi = parts[1] @@ -793,59 +745,64 @@ func ClusterIDRegionFromARN(arnID string) (string, string, error) { return dbi, parsedARN.Region, nil } -var resourceClusterUpdatePendingStates = []string{ - "backing-up", - "configuring-iam-database-auth", - "modifying", - "renaming", - "resetting-master-credentials", - "upgrading", +// TODO Remove once aws_rds_cluster is migrated? +func statusDBClusterV2(ctx context.Context, conn *rds.Client, id string, optFns ...func(*rds.Options)) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findDBClusterByIDV2(ctx, conn, id, optFns...) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.ToString(output.Status), nil + } } -func WaitForClusterUpdate(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) error { +func waitGlobalClusterMemberUpdated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration, optFns ...func(*rds.Options)) (*types.DBCluster, error) { stateConf := &retry.StateChangeConf{ - Pending: resourceClusterUpdatePendingStates, - Target: []string{"available"}, - Refresh: resourceClusterStateRefreshFunc(ctx, conn, id), + Pending: []string{ + clusterStatusBackingUp, + clusterStatusConfiguringIAMDatabaseAuth, + clusterStatusModifying, + clusterStatusRenaming, + clusterStatusResettingMasterCredentials, + clusterStatusUpgrading, + }, + Target: []string{clusterStatusAvailable}, + Refresh: statusDBClusterV2(ctx, conn, id, optFns...), Timeout: timeout, MinTimeout: 10 * time.Second, - Delay: 30 * time.Second, // Wait 30 secs before starting + Delay: 30 * time.Second, } - _, err := stateConf.WaitForStateContext(ctx) - return err -} - -func resourceClusterStateRefreshFunc(ctx context.Context, conn *rds.RDS, dbClusterIdentifier string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - resp, err := conn.DescribeDBClustersWithContext(ctx, &rds.DescribeDBClustersInput{ - DBClusterIdentifier: aws.String(dbClusterIdentifier), - }) - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterNotFoundFault) { - return 42, "destroyed", nil - } + outputRaw, err := stateConf.WaitForStateContext(ctx) - if err != nil { - return nil, "", err - } + if output, ok := outputRaw.(*types.DBCluster); ok { + return output, err + } - var dbc *rds.DBCluster + return nil, err +} - for _, c := range resp.DBClusters { - if aws.StringValue(c.DBClusterIdentifier) == dbClusterIdentifier { - dbc = c - } - } +func flattenGlobalClusterMembers(apiObjects []types.GlobalClusterMember) []interface{} { + if len(apiObjects) == 0 { + return nil + } - if dbc == nil { - return 42, "destroyed", nil - } + var tfList []interface{} - if dbc.Status != nil { - log.Printf("[DEBUG] DB Cluster status (%s): %s", dbClusterIdentifier, *dbc.Status) + for _, apiObject := range apiObjects { + tfMap := map[string]interface{}{ + "db_cluster_arn": aws.ToString(apiObject.DBClusterArn), + "is_writer": aws.ToBool(apiObject.IsWriter), } - return dbc, aws.StringValue(dbc.Status), nil + tfList = append(tfList, tfMap) } + + return tfList } diff --git a/internal/service/rds/global_cluster_test.go b/internal/service/rds/global_cluster_test.go index 6b131a36f918..0a9abfd2ab7d 100644 --- a/internal/service/rds/global_cluster_test.go +++ b/internal/service/rds/global_cluster_test.go @@ -10,9 +10,10 @@ import ( "testing" "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/aws-sdk-go-base/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -23,7 +24,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestClusterIDRegionFromARN(t *testing.T) { +func TestClusterIDAndRegionFromARN(t *testing.T) { t.Parallel() testCases := []struct { @@ -82,7 +83,7 @@ func TestClusterIDRegionFromARN(t *testing.T) { t.Run(testCase.TestName, func(t *testing.T) { t.Parallel() - gotID, gotRegion, gotErr := tfrds.ClusterIDRegionFromARN(testCase.Input) + gotID, gotRegion, gotErr := tfrds.ClusterIDAndRegionFromARN(testCase.Input) if gotErr != nil && !testCase.ExpectedErr { t.Errorf("got no error, expected one: %s", testCase.Input) @@ -101,7 +102,7 @@ func TestClusterIDRegionFromARN(t *testing.T) { func TestAccRDSGlobalCluster_basic(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1 rds.GlobalCluster + var globalCluster1 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -137,7 +138,7 @@ func TestAccRDSGlobalCluster_basic(t *testing.T) { func TestAccRDSGlobalCluster_disappears(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1 rds.GlobalCluster + var globalCluster1 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -161,7 +162,7 @@ func TestAccRDSGlobalCluster_disappears(t *testing.T) { func TestAccRDSGlobalCluster_databaseName(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1, globalCluster2 rds.GlobalCluster + var globalCluster1, globalCluster2 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -197,7 +198,7 @@ func TestAccRDSGlobalCluster_databaseName(t *testing.T) { func TestAccRDSGlobalCluster_deletionProtection(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1, globalCluster2 rds.GlobalCluster + var globalCluster1, globalCluster2 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -233,7 +234,7 @@ func TestAccRDSGlobalCluster_deletionProtection(t *testing.T) { func TestAccRDSGlobalCluster_engineLifecycleSupport_disabled(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1 rds.GlobalCluster + var globalCluster1 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -269,7 +270,7 @@ func TestAccRDSGlobalCluster_EngineVersion_updateMinor(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var globalCluster1, globalCluster2 rds.GlobalCluster + var globalCluster1, globalCluster2 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -309,7 +310,7 @@ func TestAccRDSGlobalCluster_EngineVersion_updateMajor(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var globalCluster1, globalCluster2 rds.GlobalCluster + var globalCluster1, globalCluster2 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -353,7 +354,7 @@ func TestAccRDSGlobalCluster_EngineVersion_updateMinorMultiRegion(t *testing.T) // as compatible for minor version upgrade fails with // InvalidParameterValue: In-place minor version upgrade of Aurora MySQL global database cluster 'xyz' to Aurora MySQL engine version 8.0.mysql_aurora.3.05.2 isn't supported. The selected target version 8.0.mysql_aurora.3.05.2 supports a higher version of community MySQL that introduces changes incompatible with previous minor versions of Aurora MySQL. See the Aurora documentation for how to perform a minor version upgrade on global database clusters. - var globalCluster1, globalCluster2 rds.GlobalCluster + var globalCluster1, globalCluster2 types.GlobalCluster rNameGlobal := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) // don't need to be unique but makes debugging easier rNamePrimary := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rNameSecondary := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -390,7 +391,7 @@ func TestAccRDSGlobalCluster_EngineVersion_updateMajorMultiRegion(t *testing.T) t.Skip("skipping long-running test in short mode") } - var globalCluster1, globalCluster2 rds.GlobalCluster + var globalCluster1, globalCluster2 types.GlobalCluster rNameGlobal := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) // don't need to be unique but makes debugging easier rNamePrimary := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rNameSecondary := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -423,7 +424,7 @@ func TestAccRDSGlobalCluster_EngineVersion_updateMajorMultiRegion(t *testing.T) func TestAccRDSGlobalCluster_EngineVersion_auroraMySQL(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1 rds.GlobalCluster + var globalCluster1 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -451,7 +452,7 @@ func TestAccRDSGlobalCluster_EngineVersion_auroraMySQL(t *testing.T) { func TestAccRDSGlobalCluster_EngineVersion_auroraPostgreSQL(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1 rds.GlobalCluster + var globalCluster1 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -479,7 +480,7 @@ func TestAccRDSGlobalCluster_EngineVersion_auroraPostgreSQL(t *testing.T) { func TestAccRDSGlobalCluster_forceDestroy(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1 rds.GlobalCluster + var globalCluster1 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -502,7 +503,7 @@ func TestAccRDSGlobalCluster_forceDestroy(t *testing.T) { func TestAccRDSGlobalCluster_sourceDBClusterIdentifier(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1 rds.GlobalCluster + var globalCluster1 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) clusterResourceName := "aws_rds_cluster.test" resourceName := "aws_rds_global_cluster.test" @@ -532,7 +533,7 @@ func TestAccRDSGlobalCluster_sourceDBClusterIdentifier(t *testing.T) { func TestAccRDSGlobalCluster_SourceDBClusterIdentifier_storageEncrypted(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1 rds.GlobalCluster + var globalCluster1 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) clusterResourceName := "aws_rds_cluster.test" resourceName := "aws_rds_global_cluster.test" @@ -562,7 +563,7 @@ func TestAccRDSGlobalCluster_SourceDBClusterIdentifier_storageEncrypted(t *testi func TestAccRDSGlobalCluster_storageEncrypted(t *testing.T) { ctx := acctest.Context(t) - var globalCluster1, globalCluster2 rds.GlobalCluster + var globalCluster1, globalCluster2 types.GlobalCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_global_cluster.test" @@ -596,14 +597,14 @@ func TestAccRDSGlobalCluster_storageEncrypted(t *testing.T) { }) } -func testAccCheckGlobalClusterExists(ctx context.Context, n string, v *rds.GlobalCluster) resource.TestCheckFunc { +func testAccCheckGlobalClusterExists(ctx context.Context, n string, v *types.GlobalCluster) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindGlobalClusterByID(ctx, conn, rs.Primary.ID) @@ -619,7 +620,7 @@ func testAccCheckGlobalClusterExists(ctx context.Context, n string, v *rds.Globa func testAccCheckGlobalClusterDestroy(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_global_cluster" { @@ -643,19 +644,19 @@ func testAccCheckGlobalClusterDestroy(ctx context.Context) resource.TestCheckFun } } -func testAccCheckGlobalClusterNotRecreated(i, j *rds.GlobalCluster) resource.TestCheckFunc { +func testAccCheckGlobalClusterNotRecreated(i, j *types.GlobalCluster) resource.TestCheckFunc { return func(s *terraform.State) error { - if aws.StringValue(i.GlobalClusterArn) != aws.StringValue(j.GlobalClusterArn) { - return fmt.Errorf("RDS Global Cluster was recreated. got: %s, expected: %s", aws.StringValue(i.GlobalClusterArn), aws.StringValue(j.GlobalClusterArn)) + if aws.ToString(i.GlobalClusterArn) != aws.ToString(j.GlobalClusterArn) { + return fmt.Errorf("RDS Global Cluster was recreated. got: %s, expected: %s", aws.ToString(i.GlobalClusterArn), aws.ToString(j.GlobalClusterArn)) } return nil } } -func testAccCheckGlobalClusterRecreated(i, j *rds.GlobalCluster) resource.TestCheckFunc { +func testAccCheckGlobalClusterRecreated(i, j *types.GlobalCluster) resource.TestCheckFunc { return func(s *terraform.State) error { - if aws.StringValue(i.GlobalClusterResourceId) == aws.StringValue(j.GlobalClusterResourceId) { + if aws.ToString(i.GlobalClusterResourceId) == aws.ToString(j.GlobalClusterResourceId) { return errors.New("RDS Global Cluster was not recreated") } @@ -664,11 +665,11 @@ func testAccCheckGlobalClusterRecreated(i, j *rds.GlobalCluster) resource.TestCh } func testAccPreCheckGlobalCluster(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) input := &rds.DescribeGlobalClustersInput{} - _, err := conn.DescribeGlobalClustersWithContext(ctx, input) + _, err := conn.DescribeGlobalClusters(ctx, input) if acctest.PreCheckSkipError(err) || tfawserr.ErrMessageContains(err, "InvalidParameterValue", "Access Denied to API Version: APIGlobalDatabases") { t.Skipf("skipping acceptance testing: %s", err) diff --git a/internal/service/rds/instance.go b/internal/service/rds/instance.go index b2ccebb537b9..7d73b705fb5f 100644 --- a/internal/service/rds/instance.go +++ b/internal/service/rds/instance.go @@ -481,7 +481,7 @@ func ResourceInstance() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validation.StringInSlice(NetworkType_Values(), false), + ValidateFunc: validation.StringInSlice(networkType_Values(), false), }, "option_group_name": { Type: schema.TypeString, diff --git a/internal/service/rds/instance_migrate.go b/internal/service/rds/instance_migrate.go index 79496b96642e..7937ae880a8e 100644 --- a/internal/service/rds/instance_migrate.go +++ b/internal/service/rds/instance_migrate.go @@ -732,7 +732,7 @@ func resourceInstanceResourceV1() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validation.StringInSlice(NetworkType_Values(), false), + ValidateFunc: validation.StringInSlice(networkType_Values(), false), }, "option_group_name": { Type: schema.TypeString, @@ -900,7 +900,7 @@ func resourceInstanceResourceV1() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validation.StringInSlice(StorageType_Values(), false), + ValidateFunc: validation.StringInSlice(storageType_Values(), false), }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index c3944913c96c..6daf4a3774cc 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -269,8 +269,9 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceGlobalCluster, + Factory: resourceGlobalCluster, TypeName: "aws_rds_global_cluster", + Name: "Global Cluster", }, { Factory: resourceReservedInstance, diff --git a/internal/service/rds/sweep.go b/internal/service/rds/sweep.go index c0ed8499c534..9f69fddbfefc 100644 --- a/internal/service/rds/sweep.go +++ b/internal/service/rds/sweep.go @@ -11,11 +11,13 @@ import ( "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/go-multierror" + + // "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/sweep" "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv1" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + + // "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -36,23 +38,25 @@ func RegisterSweepers() { }, }) - resource.AddTestSweepers("aws_rds_cluster", &resource.Sweeper{ - Name: "aws_rds_cluster", - F: sweepClusters, - Dependencies: []string{ - "aws_db_instance", - }, - }) + // TODO + // resource.AddTestSweepers("aws_rds_cluster", &resource.Sweeper{ + // Name: "aws_rds_cluster", + // F: sweepClusters, + // Dependencies: []string{ + // "aws_db_instance", + // }, + // }) resource.AddTestSweepers("aws_db_event_subscription", &resource.Sweeper{ Name: "aws_db_event_subscription", F: sweepEventSubscriptions, }) - resource.AddTestSweepers("aws_rds_global_cluster", &resource.Sweeper{ - Name: "aws_rds_global_cluster", - F: sweepGlobalClusters, - }) + // TODO + // resource.AddTestSweepers("aws_rds_global_cluster", &resource.Sweeper{ + // Name: "aws_rds_global_cluster", + // F: sweepGlobalClusters, + // }) resource.AddTestSweepers("aws_db_instance", &resource.Sweeper{ Name: "aws_db_instance", @@ -209,70 +213,73 @@ func sweepClusterSnapshots(region string) error { return nil } -func sweepClusters(region string) error { - ctx := sweep.Context(region) - client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { - return fmt.Errorf("error getting client: %s", err) - } - conn := client.RDSConn(ctx) +/* +TODO - var sweeperErrs *multierror.Error - sweepResources := make([]sweep.Sweepable, 0) - - input := &rds.DescribeDBClustersInput{} - err = conn.DescribeDBClustersPagesWithContext(ctx, input, func(page *rds.DescribeDBClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage + func sweepClusters(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) } + conn := client.RDSConn(ctx) - for _, v := range page.DBClusters { - arn := aws.StringValue(v.DBClusterArn) - id := aws.StringValue(v.DBClusterIdentifier) - r := resourceCluster() - d := r.Data(nil) - d.SetId(id) - d.Set(names.AttrApplyImmediately, true) - d.Set(names.AttrARN, arn) - d.Set("delete_automated_backups", true) - d.Set(names.AttrDeletionProtection, false) - d.Set("skip_final_snapshot", true) + var sweeperErrs *multierror.Error + sweepResources := make([]sweep.Sweepable, 0) - if engineMode := aws.StringValue(v.EngineMode); engineMode == engineModeGlobal || engineMode == engineModeProvisioned { - globalCluster, err := FindGlobalClusterByDBClusterARN(ctx, conn, arn) - if err != nil { - if !tfresource.NotFound(err) { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("reading RDS Global Cluster information for DB Cluster (%s): %s", id, err)) - continue + input := &rds.DescribeDBClustersInput{} + err = conn.DescribeDBClustersPagesWithContext(ctx, input, func(page *rds.DescribeDBClustersOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.DBClusters { + arn := aws.StringValue(v.DBClusterArn) + id := aws.StringValue(v.DBClusterIdentifier) + r := resourceCluster() + d := r.Data(nil) + d.SetId(id) + d.Set(names.AttrApplyImmediately, true) + d.Set(names.AttrARN, arn) + d.Set("delete_automated_backups", true) + d.Set(names.AttrDeletionProtection, false) + d.Set("skip_final_snapshot", true) + + if engineMode := aws.StringValue(v.EngineMode); engineMode == engineModeGlobal || engineMode == engineModeProvisioned { + globalCluster, err := findGlobalClusterByDBClusterARN(ctx, conn, arn) + if err != nil { + if !tfresource.NotFound(err) { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("reading RDS Global Cluster information for DB Cluster (%s): %s", id, err)) + continue + } } - } - if globalCluster != nil && globalCluster.GlobalClusterIdentifier != nil { - d.Set("global_cluster_identifier", globalCluster.GlobalClusterIdentifier) + if globalCluster != nil && globalCluster.GlobalClusterIdentifier != nil { + d.Set("global_cluster_identifier", globalCluster.GlobalClusterIdentifier) + } } + + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + return !lastPage + }) + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS Cluster sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + } + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing RDS Clusters (%s): %w", region, err)) } - return !lastPage - }) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS Cluster sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing RDS Clusters (%s): %w", region, err)) - } + err = sweep.SweepOrchestrator(ctx, sweepResources) + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping RDS Clusters (%s): %w", region, err)) + } - err = sweep.SweepOrchestrator(ctx, sweepResources) - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping RDS Clusters (%s): %w", region, err)) + return sweeperErrs.ErrorOrNil() } - - return sweeperErrs.ErrorOrNil() -} - +*/ func sweepEventSubscriptions(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) @@ -317,52 +324,55 @@ func sweepEventSubscriptions(region string) error { return nil } -func sweepGlobalClusters(region string) error { - ctx := sweep.Context(region) - client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { - return fmt.Errorf("error getting client: %s", err) - } - conn := client.RDSConn(ctx) - input := &rds.DescribeGlobalClustersInput{} - sweepResources := make([]sweep.Sweepable, 0) +/* +TODO - err = conn.DescribeGlobalClustersPagesWithContext(ctx, input, func(page *rds.DescribeGlobalClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage + func sweepGlobalClusters(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) } + conn := client.RDSConn(ctx) + input := &rds.DescribeGlobalClustersInput{} + sweepResources := make([]sweep.Sweepable, 0) - for _, v := range page.GlobalClusters { - r := ResourceGlobalCluster() - d := r.Data(nil) - d.SetId(aws.StringValue(v.GlobalClusterIdentifier)) - d.Set(names.AttrForceDestroy, true) - d.Set("global_cluster_members", flattenGlobalClusterMembers(v.GlobalClusterMembers)) + err = conn.DescribeGlobalClustersPagesWithContext(ctx, input, func(page *rds.DescribeGlobalClustersOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } - sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) - } + for _, v := range page.GlobalClusters { + r := resourceGlobalCluster() + d := r.Data(nil) + d.SetId(aws.StringValue(v.GlobalClusterIdentifier)) + d.Set(names.AttrForceDestroy, true) + d.Set("global_cluster_members", flattenGlobalClusterMembers(v.GlobalClusterMembers)) - return !lastPage - }) + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS Global Cluster sweep for %s: %s", region, err) - return nil - } + return !lastPage + }) - if err != nil { - return fmt.Errorf("error listing RDS Global Clusters (%s): %w", region, err) - } + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS Global Cluster sweep for %s: %s", region, err) + return nil + } - err = sweep.SweepOrchestrator(ctx, sweepResources) + if err != nil { + return fmt.Errorf("error listing RDS Global Clusters (%s): %w", region, err) + } - if err != nil { - return fmt.Errorf("error sweeping RDS Global Clusters (%s): %w", region, err) - } + err = sweep.SweepOrchestrator(ctx, sweepResources) - return nil -} + if err != nil { + return fmt.Errorf("error sweeping RDS Global Clusters (%s): %w", region, err) + } + return nil + } +*/ func sweepInstances(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) From 6125620e0fc3c60e0c31c1b231c4939586b1c842 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 6 Aug 2024 19:13:33 -0400 Subject: [PATCH 08/16] Fix 'empty retryable error received. This is a bug with the Terraform provider and should be reported as a GitHub issue in the provider repository'. --- internal/service/rds/global_cluster.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/service/rds/global_cluster.go b/internal/service/rds/global_cluster.go index f68218852bba..49cb26a5afb2 100644 --- a/internal/service/rds/global_cluster.go +++ b/internal/service/rds/global_cluster.go @@ -582,7 +582,8 @@ func globalClusterUpgradeMajorEngineVersion(ctx context.Context, conn *rds.Clien return false, err // NOT retryable !! AND indicates this should be a minor version upgrade } - return true, err + // Any other errors are retryable. + return err != nil, err }, ) From a3575b5a6b93ee0b34d9f6922998a61b91d71f33 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Aug 2024 10:32:34 -0400 Subject: [PATCH 09/16] rds: Migrate sweepers to AWS SDK for Go v2. --- internal/service/rds/sweep.go | 509 ++++++++++++++++------------------ 1 file changed, 234 insertions(+), 275 deletions(-) diff --git a/internal/service/rds/sweep.go b/internal/service/rds/sweep.go index 9f69fddbfefc..222da60afdcf 100644 --- a/internal/service/rds/sweep.go +++ b/internal/service/rds/sweep.go @@ -8,16 +8,14 @@ import ( "log" "strings" - "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/go-multierror" + "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-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/sweep" - "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv1" - - // "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -38,25 +36,23 @@ func RegisterSweepers() { }, }) - // TODO - // resource.AddTestSweepers("aws_rds_cluster", &resource.Sweeper{ - // Name: "aws_rds_cluster", - // F: sweepClusters, - // Dependencies: []string{ - // "aws_db_instance", - // }, - // }) + resource.AddTestSweepers("aws_rds_cluster", &resource.Sweeper{ + Name: "aws_rds_cluster", + F: sweepClusters, + Dependencies: []string{ + "aws_db_instance", + }, + }) resource.AddTestSweepers("aws_db_event_subscription", &resource.Sweeper{ Name: "aws_db_event_subscription", F: sweepEventSubscriptions, }) - // TODO - // resource.AddTestSweepers("aws_rds_global_cluster", &resource.Sweeper{ - // Name: "aws_rds_global_cluster", - // F: sweepGlobalClusters, - // }) + resource.AddTestSweepers("aws_rds_global_cluster", &resource.Sweeper{ + Name: "aws_rds_global_cluster", + F: sweepGlobalClusters, + }) resource.AddTestSweepers("aws_db_instance", &resource.Sweeper{ Name: "aws_db_instance", @@ -119,19 +115,28 @@ func sweepClusterParameterGroups(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) input := &rds.DescribeDBClusterParameterGroupsInput{} sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeDBClusterParameterGroupsPagesWithContext(ctx, input, func(page *rds.DescribeDBClusterParameterGroupsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeDBClusterParameterGroupsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS Cluster Parameter Group sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS Cluster Parameter Groups (%s): %w", region, err) } for _, v := range page.DBClusterParameterGroups { - name := aws.StringValue(v.DBClusterParameterGroupName) + name := aws.ToString(v.DBClusterParameterGroupName) if strings.HasPrefix(name, "default.") { + log.Printf("[INFO] Skipping RDS Cluster Parameter Group %s", name) continue } @@ -141,17 +146,6 @@ func sweepClusterParameterGroups(region string) error { sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS Cluster Parameter Group sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS Cluster Parameter Groups (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -169,39 +163,36 @@ func sweepClusterSnapshots(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) input := &rds.DescribeDBClusterSnapshotsInput{ // "InvalidDBClusterSnapshotStateFault: Only manual snapshots may be deleted." - Filters: []*rds.Filter{{ + Filters: []types.Filter{{ Name: aws.String("snapshot-type"), - Values: aws.StringSlice([]string{"manual"}), + Values: []string{"manual"}, }}, } sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeDBClusterSnapshotsPagesWithContext(ctx, input, func(page *rds.DescribeDBClusterSnapshotsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeDBClusterSnapshotsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS DB Cluster Snapshot sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS DB Cluster Snapshots (%s): %w", region, err) } for _, v := range page.DBClusterSnapshots { r := resourceClusterSnapshot() d := r.Data(nil) - d.SetId(aws.StringValue(v.DBClusterSnapshotIdentifier)) + d.SetId(aws.ToString(v.DBClusterSnapshotIdentifier)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS DB Cluster Snapshot sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS DB Cluster Snapshots (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -213,106 +204,98 @@ func sweepClusterSnapshots(region string) error { return nil } -/* -TODO +func sweepClusters(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.RDSClient(ctx) + input := &rds.DescribeDBClustersInput{} + sweepResources := make([]sweep.Sweepable, 0) + + pages := rds.NewDescribeDBClustersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - func sweepClusters(region string) error { - ctx := sweep.Context(region) - client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { - return fmt.Errorf("error getting client: %s", err) + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS Cluster sweep for %s: %s", region, err) + return nil } - conn := client.RDSConn(ctx) - - var sweeperErrs *multierror.Error - sweepResources := make([]sweep.Sweepable, 0) - input := &rds.DescribeDBClustersInput{} - err = conn.DescribeDBClustersPagesWithContext(ctx, input, func(page *rds.DescribeDBClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + if err != nil { + return fmt.Errorf("error listing RDS Clusters (%s): %w", region, err) + } - for _, v := range page.DBClusters { - arn := aws.StringValue(v.DBClusterArn) - id := aws.StringValue(v.DBClusterIdentifier) - r := resourceCluster() - d := r.Data(nil) - d.SetId(id) - d.Set(names.AttrApplyImmediately, true) - d.Set(names.AttrARN, arn) - d.Set("delete_automated_backups", true) - d.Set(names.AttrDeletionProtection, false) - d.Set("skip_final_snapshot", true) - - if engineMode := aws.StringValue(v.EngineMode); engineMode == engineModeGlobal || engineMode == engineModeProvisioned { - globalCluster, err := findGlobalClusterByDBClusterARN(ctx, conn, arn) - if err != nil { - if !tfresource.NotFound(err) { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("reading RDS Global Cluster information for DB Cluster (%s): %s", id, err)) - continue - } - } + for _, v := range page.DBClusters { + arn := aws.ToString(v.DBClusterArn) + id := aws.ToString(v.DBClusterIdentifier) + r := resourceCluster() + d := r.Data(nil) + d.SetId(id) + d.Set(names.AttrApplyImmediately, true) + d.Set(names.AttrARN, arn) + d.Set("delete_automated_backups", true) + d.Set(names.AttrDeletionProtection, false) + d.Set("skip_final_snapshot", true) - if globalCluster != nil && globalCluster.GlobalClusterIdentifier != nil { - d.Set("global_cluster_identifier", globalCluster.GlobalClusterIdentifier) + if engineMode := aws.ToString(v.EngineMode); engineMode == engineModeGlobal || engineMode == engineModeProvisioned { + globalCluster, err := findGlobalClusterByDBClusterARN(ctx, conn, arn) + if err != nil { + if !tfresource.NotFound(err) { + log.Printf("[WARN] Reading RDS Global Cluster information for DB Cluster (%s): %s", id, err) + continue } } - sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + if globalCluster != nil && globalCluster.GlobalClusterIdentifier != nil { + d.Set("global_cluster_identifier", globalCluster.GlobalClusterIdentifier) + } } - return !lastPage - }) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS Cluster sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing RDS Clusters (%s): %w", region, err)) + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + } - err = sweep.SweepOrchestrator(ctx, sweepResources) - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping RDS Clusters (%s): %w", region, err)) - } + err = sweep.SweepOrchestrator(ctx, sweepResources) - return sweeperErrs.ErrorOrNil() + if err != nil { + return fmt.Errorf("error sweeping RDS Clusters (%s): %w", region, err) } -*/ + + return nil +} + func sweepEventSubscriptions(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) input := &rds.DescribeEventSubscriptionsInput{} sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeEventSubscriptionsPagesWithContext(ctx, input, func(page *rds.DescribeEventSubscriptionsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeEventSubscriptionsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS Event Subscription sweep for %s: %s", region, err) + return nil } - for _, eventSubscription := range page.EventSubscriptionsList { + if err != nil { + return fmt.Errorf("error listing RDS Event Subscriptions (%s): %w", region, err) + } + + for _, v := range page.EventSubscriptionsList { r := resourceEventSubscription() d := r.Data(nil) - d.SetId(aws.StringValue(eventSubscription.CustSubscriptionId)) + d.SetId(aws.ToString(v.CustSubscriptionId)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS Event Subscription sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS Event Subscriptions (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -324,38 +307,21 @@ func sweepEventSubscriptions(region string) error { return nil } -/* -TODO - - func sweepGlobalClusters(region string) error { - ctx := sweep.Context(region) - client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { - return fmt.Errorf("error getting client: %s", err) - } - conn := client.RDSConn(ctx) - input := &rds.DescribeGlobalClustersInput{} - sweepResources := make([]sweep.Sweepable, 0) - - err = conn.DescribeGlobalClustersPagesWithContext(ctx, input, func(page *rds.DescribeGlobalClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - for _, v := range page.GlobalClusters { - r := resourceGlobalCluster() - d := r.Data(nil) - d.SetId(aws.StringValue(v.GlobalClusterIdentifier)) - d.Set(names.AttrForceDestroy, true) - d.Set("global_cluster_members", flattenGlobalClusterMembers(v.GlobalClusterMembers)) +func sweepGlobalClusters(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.RDSClient(ctx) + input := &rds.DescribeGlobalClustersInput{} + sweepResources := make([]sweep.Sweepable, 0) - sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) - } + pages := rds.NewDescribeGlobalClustersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - return !lastPage - }) - - if awsv1.SkipSweepError(err) { + if awsv2.SkipSweepError(err) { log.Printf("[WARN] Skipping RDS Global Cluster sweep for %s: %s", region, err) return nil } @@ -364,15 +330,26 @@ TODO return fmt.Errorf("error listing RDS Global Clusters (%s): %w", region, err) } - err = sweep.SweepOrchestrator(ctx, sweepResources) + for _, v := range page.GlobalClusters { + r := resourceGlobalCluster() + d := r.Data(nil) + d.SetId(aws.ToString(v.GlobalClusterIdentifier)) + d.Set(names.AttrForceDestroy, true) + d.Set("global_cluster_members", flattenGlobalClusterMembers(v.GlobalClusterMembers)) - if err != nil { - return fmt.Errorf("error sweeping RDS Global Clusters (%s): %w", region, err) + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + } + + err = sweep.SweepOrchestrator(ctx, sweepResources) - return nil + if err != nil { + return fmt.Errorf("error sweeping RDS Global Clusters (%s): %w", region, err) } -*/ + + return nil +} + func sweepInstances(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) @@ -380,18 +357,26 @@ func sweepInstances(region string) error { return fmt.Errorf("error getting client: %s", err) } input := &rds.DescribeDBInstancesInput{} - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeDBInstancesPagesWithContext(ctx, input, func(page *rds.DescribeDBInstancesOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeDBInstancesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS DB Instance sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS DB Instances (%s): %w", region, err) } for _, v := range page.DBInstances { r := ResourceInstance() d := r.Data(nil) - d.SetId(aws.StringValue(v.DbiResourceId)) + d.SetId(aws.ToString(v.DbiResourceId)) d.Set(names.AttrApplyImmediately, true) d.Set("delete_automated_backups", true) d.Set(names.AttrDeletionProtection, false) @@ -400,17 +385,6 @@ func sweepInstances(region string) error { sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS DB Instance sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS DB Instances (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -429,18 +403,27 @@ func sweepOptionGroups(region string) error { return fmt.Errorf("error getting client: %s", err) } input := &rds.DescribeOptionGroupsInput{} - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeOptionGroupsPagesWithContext(ctx, input, func(page *rds.DescribeOptionGroupsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeOptionGroupsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS Option Group sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS Option Groups (%s): %w", region, err) } for _, v := range page.OptionGroupsList { - name := aws.StringValue(v.OptionGroupName) + name := aws.ToString(v.OptionGroupName) if strings.HasPrefix(name, "default:") { + log.Printf("[INFO] Skipping RDS Option Group %s", name) continue } @@ -450,17 +433,6 @@ func sweepOptionGroups(region string) error { sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS Option Group sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS Option Groups (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -479,18 +451,27 @@ func sweepParameterGroups(region string) error { return fmt.Errorf("error getting client: %s", err) } input := &rds.DescribeDBParameterGroupsInput{} - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeDBParameterGroupsPagesWithContext(ctx, input, func(page *rds.DescribeDBParameterGroupsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeDBParameterGroupsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS DB Parameter Group sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS DB Parameter Groups (%s): %w", region, err) } for _, v := range page.DBParameterGroups { - name := aws.StringValue(v.DBParameterGroupName) + name := aws.ToString(v.DBParameterGroupName) if strings.HasPrefix(name, "default.") { + log.Printf("[INFO] Skipping RDS DB Parameter Group %s", name) continue } @@ -500,17 +481,6 @@ func sweepParameterGroups(region string) error { sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS DB Parameter Group sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS DB Parameter Groups (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -528,33 +498,30 @@ func sweepProxies(region string) error { if err != nil { return fmt.Errorf("Error getting client: %s", err) } - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) input := &rds.DescribeDBProxiesInput{} sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeDBProxiesPagesWithContext(ctx, input, func(page *rds.DescribeDBProxiesOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeDBProxiesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS DB Proxy sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS DB Proxies (%s): %w", region, err) } for _, v := range page.DBProxies { r := resourceProxy() d := r.Data(nil) - d.SetId(aws.StringValue(v.DBProxyName)) + d.SetId(aws.ToString(v.DBProxyName)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS DB Proxy sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS DB Proxies (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -572,19 +539,28 @@ func sweepSnapshots(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) input := &rds.DescribeDBSnapshotsInput{} sweepResources := make([]sweep.Sweepable, 0) - 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) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS DB Snapshot sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS DB Snapshots (%s): %w", region, err) } for _, v := range page.DBSnapshots { - id := aws.StringValue(v.DBSnapshotIdentifier) + id := aws.ToString(v.DBSnapshotIdentifier) if strings.HasPrefix(id, "rds:") { + log.Printf("[INFO] Skipping RDS DB Snapshot %s", id) continue } @@ -594,17 +570,6 @@ func sweepSnapshots(region string) error { sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS DB Snapshot sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS DB Snapshots (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -622,33 +587,30 @@ func sweepSubnetGroups(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) input := &rds.DescribeDBSubnetGroupsInput{} sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeDBSubnetGroupsPagesWithContext(ctx, input, func(page *rds.DescribeDBSubnetGroupsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeDBSubnetGroupsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS DB Subnet Group sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS DB Subnet Groups (%s): %w", region, err) } for _, v := range page.DBSubnetGroups { r := resourceSubnetGroup() d := r.Data(nil) - d.SetId(aws.StringValue(v.DBSubnetGroupName)) + d.SetId(aws.ToString(v.DBSubnetGroupName)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS DB Subnet Group sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS DB Subnet Groups (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -666,18 +628,26 @@ func sweepInstanceAutomatedBackups(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.RDSConn(ctx) + conn := client.RDSClient(ctx) input := &rds.DescribeDBInstanceAutomatedBackupsInput{} sweepResources := make([]sweep.Sweepable, 0) var backupARNs []string - err = conn.DescribeDBInstanceAutomatedBackupsPagesWithContext(ctx, input, func(page *rds.DescribeDBInstanceAutomatedBackupsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := rds.NewDescribeDBInstanceAutomatedBackupsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping RDS Instance Automated Backup sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing RDS Instance Automated Backups (%s): %w", region, err) } for _, v := range page.DBInstanceAutomatedBackups { - arn := aws.StringValue(v.DBInstanceAutomatedBackupsArn) + arn := aws.ToString(v.DBInstanceAutomatedBackupsArn) r := resourceInstanceAutomatedBackupsReplication() d := r.Data(nil) d.SetId(arn) @@ -686,17 +656,6 @@ func sweepInstanceAutomatedBackups(region string) error { sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - - return !lastPage - }) - - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping RDS Instance Automated Backup sweep for %s: %s", region, err) - return nil - } - - if err != nil { - return fmt.Errorf("error listing RDS Instance Automated Backups (%s): %w", region, err) } err = sweep.SweepOrchestrator(ctx, sweepResources) @@ -708,16 +667,16 @@ func sweepInstanceAutomatedBackups(region string) error { // Since there is no resource for automated backups themselves, they are swept here. for _, v := range backupARNs { log.Printf("[DEBUG] Deleting RDS Instance Automated Backup: %s", v) - _, err = conn.DeleteDBInstanceAutomatedBackupWithContext(ctx, &rds.DeleteDBInstanceAutomatedBackupInput{ + _, err = conn.DeleteDBInstanceAutomatedBackup(ctx, &rds.DeleteDBInstanceAutomatedBackupInput{ DBInstanceAutomatedBackupsArn: aws.String(v), }) - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBInstanceAutomatedBackupNotFoundFault) { + if errs.IsA[*types.DBInstanceAutomatedBackupNotFoundFault](err) { continue } if err != nil { - log.Printf("[ERROR] deleting RDS Instance Automated Backup (%s): %s", v, err) + log.Printf("[WARN] Deleting RDS Instance Automated Backup (%s): %s", v, err) } } From 566a479519dcd4cee56d1822e12b6818dc4b62b1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Aug 2024 10:46:17 -0400 Subject: [PATCH 10/16] r/aws_rds_cluster_activity_stream: Migrate to AWS SDK for Go v2. --- .../service/rds/cluster_activity_stream.go | 94 +++++++++++-------- .../rds/cluster_activity_stream_test.go | 22 ++--- internal/service/rds/exports_test.go | 2 + internal/service/rds/service_package_gen.go | 3 +- 4 files changed, 66 insertions(+), 55 deletions(-) diff --git a/internal/service/rds/cluster_activity_stream.go b/internal/service/rds/cluster_activity_stream.go index affd3ea42284..f64b42f2bdaf 100644 --- a/internal/service/rds/cluster_activity_stream.go +++ b/internal/service/rds/cluster_activity_stream.go @@ -8,21 +8,22 @@ 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" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_rds_cluster_activity_stream") -func ResourceClusterActivityStream() *schema.Resource { +// @SDKResource("aws_rds_cluster_activity_stream", name="Cluster Activity Stream") +func resourceClusterActivityStream() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceClusterActivityStreamCreate, ReadWithoutTimeout: resourceClusterActivityStreamRead, @@ -49,10 +50,10 @@ func ResourceClusterActivityStream() *schema.Resource { ForceNew: true, }, names.AttrMode: { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(rds.ActivityStreamMode_Values(), false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[types.ActivityStreamMode](), }, names.AttrResourceARN: { Type: schema.TypeString, @@ -66,18 +67,18 @@ func ResourceClusterActivityStream() *schema.Resource { func resourceClusterActivityStreamCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) arn := d.Get(names.AttrResourceARN).(string) input := &rds.StartActivityStreamInput{ ApplyImmediately: aws.Bool(true), EngineNativeAuditFieldsIncluded: aws.Bool(d.Get("engine_native_audit_fields_included").(bool)), KmsKeyId: aws.String(d.Get(names.AttrKMSKeyID).(string)), - Mode: aws.String(d.Get(names.AttrMode).(string)), + Mode: types.ActivityStreamMode(d.Get(names.AttrMode).(string)), ResourceArn: aws.String(arn), } - _, err := conn.StartActivityStreamWithContext(ctx, input) + _, err := conn.StartActivityStream(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS Cluster Activity Stream (%s): %s", arn, err) @@ -85,7 +86,7 @@ func resourceClusterActivityStreamCreate(ctx context.Context, d *schema.Resource d.SetId(arn) - if err := waitActivityStreamStarted(ctx, conn, d.Id()); err != nil { + if _, err := waitActivityStreamStarted(ctx, conn, d.Id()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster Activity Stream (%s) start: %s", d.Id(), err) } @@ -94,9 +95,9 @@ func resourceClusterActivityStreamCreate(ctx context.Context, d *schema.Resource func resourceClusterActivityStreamRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) - output, err := FindDBClusterWithActivityStream(ctx, conn, d.Id()) + output, err := findDBClusterWithActivityStream(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS Cluster Activity Stream (%s) not found, removing from state", d.Id()) @@ -118,42 +119,44 @@ func resourceClusterActivityStreamRead(ctx context.Context, d *schema.ResourceDa func resourceClusterActivityStreamDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) log.Printf("[DEBUG] Deleting RDS Cluster Activity Stream: %s", d.Id()) - _, err := conn.StopActivityStreamWithContext(ctx, &rds.StopActivityStreamInput{ + _, err := conn.StopActivityStream(ctx, &rds.StopActivityStreamInput{ ApplyImmediately: aws.Bool(true), ResourceArn: aws.String(d.Id()), }) + if err != nil { return sdkdiag.AppendErrorf(diags, "stopping RDS Cluster Activity Stream (%s): %s", d.Id(), err) } - if err := waitActivityStreamStopped(ctx, conn, d.Id()); err != nil { + if _, err := waitActivityStreamStopped(ctx, conn, d.Id()); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for RDS Cluster Activity Stream (%s) stop: %s", d.Id(), err) } return diags } -func FindDBClusterWithActivityStream(ctx context.Context, conn *rds.RDS, arn string) (*rds.DBCluster, error) { - output, err := FindDBClusterByID(ctx, conn, arn) +func findDBClusterWithActivityStream(ctx context.Context, conn *rds.Client, arn string) (*types.DBCluster, error) { + output, err := findDBClusterByIDV2(ctx, conn, arn) + if err != nil { return nil, err } - if status := aws.StringValue(output.ActivityStreamStatus); status == rds.ActivityStreamStatusStopped { + if status := output.ActivityStreamStatus; status == types.ActivityStreamStatusStopped { return nil, &retry.NotFoundError{ - Message: status, + Message: string(status), } } return output, nil } -func statusDBClusterActivityStream(ctx context.Context, conn *rds.RDS, arn string) retry.StateRefreshFunc { +func statusDBClusterActivityStream(ctx context.Context, conn *rds.Client, arn string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindDBClusterWithActivityStream(ctx, conn, arn) + output, err := findDBClusterWithActivityStream(ctx, conn, arn) if tfresource.NotFound(err) { return nil, "", nil @@ -163,41 +166,50 @@ func statusDBClusterActivityStream(ctx context.Context, conn *rds.RDS, arn strin return nil, "", err } - return output, aws.StringValue(output.ActivityStreamStatus), nil + return output, string(output.ActivityStreamStatus), nil } } -const ( - dbClusterActivityStreamStartedTimeout = 30 * time.Minute - dbClusterActivityStreamStoppedTimeout = 30 * time.Minute -) - -func waitActivityStreamStarted(ctx context.Context, conn *rds.RDS, arn string) error { +func waitActivityStreamStarted(ctx context.Context, conn *rds.Client, arn string) (*types.DBCluster, error) { + const ( + timeout = 30 * time.Minute + ) stateConf := &retry.StateChangeConf{ - Pending: []string{rds.ActivityStreamStatusStarting}, - Target: []string{rds.ActivityStreamStatusStarted}, + Pending: enum.Slice(types.ActivityStreamStatusStarting), + Target: enum.Slice(types.ActivityStreamStatusStarted), Refresh: statusDBClusterActivityStream(ctx, conn, arn), - Timeout: dbClusterActivityStreamStartedTimeout, + Timeout: timeout, MinTimeout: 10 * time.Second, Delay: 30 * time.Second, } - _, err := stateConf.WaitForStateContext(ctx) + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.DBCluster); ok { + return output, err + } - return err + return nil, err } -func waitActivityStreamStopped(ctx context.Context, conn *rds.RDS, arn string) error { +func waitActivityStreamStopped(ctx context.Context, conn *rds.Client, arn string) (*types.DBCluster, error) { + const ( + timeout = 30 * time.Minute + ) stateConf := &retry.StateChangeConf{ - Pending: []string{rds.ActivityStreamStatusStopping}, + Pending: enum.Slice(types.ActivityStreamStatusStopping), Target: []string{}, Refresh: statusDBClusterActivityStream(ctx, conn, arn), - Timeout: dbClusterActivityStreamStoppedTimeout, + Timeout: timeout, MinTimeout: 10 * time.Second, Delay: 30 * time.Second, } - _, err := stateConf.WaitForStateContext(ctx) + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*types.DBCluster); ok { + return output, err + } - return err + return nil, err } diff --git a/internal/service/rds/cluster_activity_stream_test.go b/internal/service/rds/cluster_activity_stream_test.go index dc729cd6535d..90972e11844c 100644 --- a/internal/service/rds/cluster_activity_stream_test.go +++ b/internal/service/rds/cluster_activity_stream_test.go @@ -8,8 +8,7 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/service/rds" + "github.com/aws/aws-sdk-go-v2/service/rds/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -22,14 +21,14 @@ import ( func TestAccRDSClusterActivityStream_basic(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster_activity_stream.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartition(t, endpoints.AwsPartitionID) + acctest.PreCheckPartition(t, names.StandardPartitionID) }, ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -55,14 +54,14 @@ func TestAccRDSClusterActivityStream_basic(t *testing.T) { func TestAccRDSClusterActivityStream_disappears(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster_activity_stream.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartition(t, endpoints.AwsPartitionID) + acctest.PreCheckPartition(t, names.StandardPartitionID) }, ErrorCheck: acctest.ErrorCheck(t, names.RDSServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -80,20 +79,17 @@ func TestAccRDSClusterActivityStream_disappears(t *testing.T) { }) } -func testAccCheckClusterActivityStreamExists(ctx context.Context, n string, v *rds.DBCluster) resource.TestCheckFunc { +func testAccCheckClusterActivityStreamExists(ctx context.Context, n string, v *types.DBCluster) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("RDS Cluster Activity Stream ID is not set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindDBClusterWithActivityStream(ctx, conn, rs.Primary.ID) + if err != nil { return err } @@ -106,7 +102,7 @@ func testAccCheckClusterActivityStreamExists(ctx context.Context, n string, v *r func testAccCheckClusterActivityStreamDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_rds_cluster_activity_stream" { diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index 68e331a6a30e..ced3ac141906 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 + ResourceClusterActivityStream = resourceClusterActivityStream ResourceClusterEndpoint = resourceClusterEndpoint ResourceClusterParameterGroup = resourceClusterParameterGroup ResourceClusterRoleAssociation = resourceClusterRoleAssociation @@ -34,6 +35,7 @@ var ( FindDBClusterParameterGroupByName = findDBClusterParameterGroupByName FindDBClusterRoleByTwoPartKey = findDBClusterRoleByTwoPartKey FindDBClusterSnapshotByID = findDBClusterSnapshotByID + FindDBClusterWithActivityStream = findDBClusterWithActivityStream FindDBInstanceAutomatedBackupByARN = findDBInstanceAutomatedBackupByARN FindDBInstanceByID = findDBInstanceByIDSDKv1 FindDBInstanceRoleByTwoPartKey = findDBInstanceRoleByTwoPartKey diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index 6daf4a3774cc..3810a1594f14 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -228,8 +228,9 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceClusterActivityStream, + Factory: resourceClusterActivityStream, TypeName: "aws_rds_cluster_activity_stream", + Name: "Cluster Activity Stream", }, { Factory: resourceClusterEndpoint, From a92eb75e5e30253f2c278f0283f44ce1c973cb78 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Aug 2024 10:56:54 -0400 Subject: [PATCH 11/16] Fix golangci-lint 'unparam'. --- internal/service/rds/global_cluster.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/rds/global_cluster.go b/internal/service/rds/global_cluster.go index 49cb26a5afb2..d8228120a3bf 100644 --- a/internal/service/rds/global_cluster.go +++ b/internal/service/rds/global_cluster.go @@ -516,7 +516,7 @@ func waitGlobalClusterDeleted(ctx context.Context, conn *rds.Client, id string, return nil, err } -func waitGlobalClusterMemberRemoved(ctx context.Context, conn *rds.Client, dbClusterARN string, timeout time.Duration) (*types.GlobalCluster, error) { +func waitGlobalClusterMemberRemoved(ctx context.Context, conn *rds.Client, dbClusterARN string, timeout time.Duration) (*types.GlobalCluster, error) { //nolint:unparam outputRaw, err := tfresource.RetryUntilNotFound(ctx, timeout, func() (interface{}, error) { return findGlobalClusterByDBClusterARN(ctx, conn, dbClusterARN) }) @@ -763,7 +763,7 @@ func statusDBClusterV2(ctx context.Context, conn *rds.Client, id string, optFns } } -func waitGlobalClusterMemberUpdated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration, optFns ...func(*rds.Options)) (*types.DBCluster, error) { +func waitGlobalClusterMemberUpdated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration, optFns ...func(*rds.Options)) (*types.DBCluster, error) { //nolint:unparam stateConf := &retry.StateChangeConf{ Pending: []string{ clusterStatusBackingUp, From dfd6aa622d8080e5f7b8fd4c36e386ecec1f03ac Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Aug 2024 11:33:42 -0400 Subject: [PATCH 12/16] Fix 'InvalidParameterCombination: Activity Streams feature expected to be started, but is stopped' when stopping activity stream. --- internal/service/rds/cluster_activity_stream.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/service/rds/cluster_activity_stream.go b/internal/service/rds/cluster_activity_stream.go index f64b42f2bdaf..909a34222a55 100644 --- a/internal/service/rds/cluster_activity_stream.go +++ b/internal/service/rds/cluster_activity_stream.go @@ -11,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/rds" "github.com/aws/aws-sdk-go-v2/service/rds/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -127,6 +128,10 @@ func resourceClusterActivityStreamDelete(ctx context.Context, d *schema.Resource ResourceArn: aws.String(d.Id()), }) + if tfawserr.ErrMessageContains(err, errCodeInvalidParameterCombination, "Activity Streams feature expected to be started, but is stopped") { + return diags + } + if err != nil { return sdkdiag.AppendErrorf(diags, "stopping RDS Cluster Activity Stream (%s): %s", d.Id(), err) } From 6cb07cff2b65ca89f4f869ea2581b049199fbc9d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Aug 2024 13:50:54 -0400 Subject: [PATCH 13/16] d/aws_rds_cluster: Migrate to AWS SDK for Go v2. --- internal/service/rds/cluster_data_source.go | 54 +++++++++------------ internal/service/rds/service_package_gen.go | 4 +- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/internal/service/rds/cluster_data_source.go b/internal/service/rds/cluster_data_source.go index 78a6a1866588..60685dc8a5a7 100644 --- a/internal/service/rds/cluster_data_source.go +++ b/internal/service/rds/cluster_data_source.go @@ -6,17 +6,21 @@ package rds import ( "context" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "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" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_rds_cluster") -func DataSourceCluster() *schema.Resource { +// @SDKDataSource("aws_rds_cluster", name="Cluster") +// @Tags +// @Testing(tagsTest=false) +func dataSourceCluster() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceClusterRead, @@ -173,29 +177,25 @@ func DataSourceCluster() *schema.Resource { func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).RDSConn(ctx) - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + conn := meta.(*conns.AWSClient).RDSClient(ctx) dbClusterID := d.Get(names.AttrClusterIdentifier).(string) - dbc, err := FindDBClusterByID(ctx, conn, dbClusterID) + dbc, err := findDBClusterByIDV2(ctx, conn, dbClusterID) if err != nil { return sdkdiag.AppendErrorf(diags, "reading RDS Cluster (%s): %s", dbClusterID, err) } - d.SetId(aws.StringValue(dbc.DBClusterIdentifier)) - - clusterARN := aws.StringValue(dbc.DBClusterArn) + d.SetId(aws.ToString(dbc.DBClusterIdentifier)) + clusterARN := aws.ToString(dbc.DBClusterArn) d.Set(names.AttrARN, clusterARN) - d.Set(names.AttrAvailabilityZones, aws.StringValueSlice(dbc.AvailabilityZones)) + d.Set(names.AttrAvailabilityZones, dbc.AvailabilityZones) d.Set("backtrack_window", dbc.BacktrackWindow) d.Set("backup_retention_period", dbc.BackupRetentionPeriod) d.Set(names.AttrClusterIdentifier, dbc.DBClusterIdentifier) - var clusterMembers []string - for _, v := range dbc.DBClusterMembers { - clusterMembers = append(clusterMembers, aws.StringValue(v.DBInstanceIdentifier)) - } - d.Set("cluster_members", clusterMembers) + d.Set("cluster_members", tfslices.ApplyToAll(dbc.DBClusterMembers, func(v types.DBClusterMember) string { + return aws.ToString(v.DBInstanceIdentifier) + })) d.Set("cluster_resource_id", dbc.DbClusterResourceId) // Only set the DatabaseName if it is not nil. There is a known API bug where // RDS accepts a DatabaseName but does not return it, causing a perpetual @@ -207,18 +207,16 @@ func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta int d.Set("db_cluster_parameter_group_name", dbc.DBClusterParameterGroup) d.Set("db_subnet_group_name", dbc.DBSubnetGroup) d.Set("db_system_id", dbc.DBSystemId) - d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(dbc.EnabledCloudwatchLogsExports)) + d.Set("enabled_cloudwatch_logs_exports", dbc.EnabledCloudwatchLogsExports) d.Set(names.AttrEndpoint, dbc.Endpoint) d.Set(names.AttrEngine, dbc.Engine) d.Set("engine_mode", dbc.EngineMode) d.Set(names.AttrEngineVersion, dbc.EngineVersion) d.Set(names.AttrHostedZoneID, dbc.HostedZoneId) d.Set("iam_database_authentication_enabled", dbc.IAMDatabaseAuthenticationEnabled) - var iamRoleARNs []string - for _, v := range dbc.AssociatedRoles { - iamRoleARNs = append(iamRoleARNs, aws.StringValue(v.RoleArn)) - } - d.Set("iam_roles", iamRoleARNs) + d.Set("iam_roles", tfslices.ApplyToAll(dbc.AssociatedRoles, func(v types.DBClusterRole) string { + return aws.ToString(v.RoleArn) + })) d.Set(names.AttrKMSKeyID, dbc.KmsKeyId) if dbc.MasterUserSecret != nil { if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecret(dbc.MasterUserSecret)}); err != nil { @@ -233,17 +231,11 @@ func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta int d.Set("reader_endpoint", dbc.ReaderEndpoint) d.Set("replication_source_identifier", dbc.ReplicationSourceIdentifier) d.Set(names.AttrStorageEncrypted, dbc.StorageEncrypted) - var securityGroupIDs []string - for _, v := range dbc.VpcSecurityGroups { - securityGroupIDs = append(securityGroupIDs, aws.StringValue(v.VpcSecurityGroupId)) - } - d.Set(names.AttrVPCSecurityGroupIDs, securityGroupIDs) - - tags := KeyValueTags(ctx, dbc.TagList) + d.Set(names.AttrVPCSecurityGroupIDs, tfslices.ApplyToAll(dbc.VpcSecurityGroups, func(v types.VpcSecurityGroupMembership) string { + return aws.ToString(v.VpcSecurityGroupId) + })) - if err := d.Set(names.AttrTags, tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return sdkdiag.AppendErrorf(diags, "setting tags: %s", err) - } + setTagsOutV2(ctx, dbc.TagList) return diags } diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index 3810a1594f14..f4ba61f6bfc3 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -87,8 +87,10 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac Name: "Certificate", }, { - Factory: DataSourceCluster, + Factory: dataSourceCluster, TypeName: "aws_rds_cluster", + Name: "Cluster", + Tags: &types.ServicePackageResourceTags{}, }, { Factory: DataSourceClusters, From 3494dcefc673e24f879ecdb5b160fcfa2b54e16a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Aug 2024 14:37:28 -0400 Subject: [PATCH 14/16] r/aws_rds_cluster: Migrate to AWS SDK for Go v2. --- internal/service/rds/cluster.go | 406 ++++++++---------- .../service/rds/cluster_activity_stream.go | 2 +- internal/service/rds/cluster_data_source.go | 2 +- internal/service/rds/cluster_instance.go | 72 +++- .../service/rds/cluster_role_association.go | 68 +-- internal/service/rds/cluster_test.go | 214 +++++---- .../service/rds/clusters_data_source_test.go | 4 +- internal/service/rds/exports_test.go | 1 + internal/service/rds/flex.go | 22 + internal/service/rds/global_cluster.go | 19 +- internal/service/rds/instance.go | 22 +- internal/service/rds/instance_data_source.go | 2 +- 12 files changed, 417 insertions(+), 417 deletions(-) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index 6d7f9fd49202..a3685ed4ae97 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -11,10 +11,11 @@ import ( "time" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/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/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" @@ -22,6 +23,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/create" + "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" @@ -35,7 +37,6 @@ import ( const ( clusterScalingConfiguration_DefaultMinCapacity = 1 clusterScalingConfiguration_DefaultMaxCapacity = 16 - clusterTimeoutDelete = 2 * time.Minute ) // @SDKResource("aws_rds_cluster", name="Cluster") @@ -630,7 +631,7 @@ func resourceCluster() *schema.Resource { func resourceClusterCreate(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) identifier := create.NewNameGenerator( create.WithConfiguredName(d.Get(names.AttrClusterIdentifier).(string)), @@ -656,11 +657,11 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int Engine: aws.String(d.Get(names.AttrEngine).(string)), EngineMode: aws.String(d.Get("engine_mode").(string)), SnapshotIdentifier: aws.String(v.(string)), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), } if v, ok := d.GetOk(names.AttrAvailabilityZones); ok && v.(*schema.Set).Len() > 0 { - input.AvailabilityZones = flex.ExpandStringSet(v.(*schema.Set)) + input.AvailabilityZones = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("backtrack_window"); ok { @@ -668,7 +669,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("backup_retention_period"); ok { - modifyDbClusterInput.BackupRetentionPeriod = aws.Int64(int64(v.(int))) + modifyDbClusterInput.BackupRetentionPeriod = aws.Int32(int32(v.(int))) requiresModifyDbCluster = true } @@ -693,7 +694,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("enabled_cloudwatch_logs_exports"); ok && v.(*schema.Set).Len() > 0 { - input.EnableCloudwatchLogsExports = flex.ExpandStringSet(v.(*schema.Set)) + input.EnableCloudwatchLogsExports = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("engine_lifecycle_support"); ok { @@ -732,7 +733,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk(names.AttrPort); ok { - input.Port = aws.Int64(int64(v.(int))) + input.Port = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk("preferred_backup_window"); ok { @@ -755,15 +756,15 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk(names.AttrVPCSecurityGroupIDs); ok && v.(*schema.Set).Len() > 0 { - input.VpcSecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set)) + input.VpcSecurityGroupIds = flex.ExpandStringValueSet(v.(*schema.Set)) } - log.Printf("[DEBUG] Creating RDS Cluster: %s", input) _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, func() (interface{}, error) { - return conn.RestoreDBClusterFromSnapshotWithContext(ctx, input) + return conn.RestoreDBClusterFromSnapshot(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 Cluster (restore from snapshot) (%s): %s", identifier, err) } @@ -787,11 +788,11 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int S3Prefix: aws.String(tfMap[names.AttrBucketPrefix].(string)), SourceEngine: aws.String(tfMap["source_engine"].(string)), SourceEngineVersion: aws.String(tfMap["source_engine_version"].(string)), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), } if v, ok := d.GetOk(names.AttrAvailabilityZones); ok && v.(*schema.Set).Len() > 0 { - input.AvailabilityZones = flex.ExpandStringSet(v.(*schema.Set)) + input.AvailabilityZones = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("backtrack_window"); ok { @@ -799,7 +800,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("backup_retention_period"); ok { - input.BackupRetentionPeriod = aws.Int64(int64(v.(int))) + input.BackupRetentionPeriod = aws.Int32(int32(v.(int))) } if v := d.Get(names.AttrDatabaseName); v.(string) != "" { @@ -823,7 +824,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("enabled_cloudwatch_logs_exports"); ok && v.(*schema.Set).Len() > 0 { - input.EnableCloudwatchLogsExports = flex.ExpandStringSet(v.(*schema.Set)) + input.EnableCloudwatchLogsExports = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("engine_lifecycle_support"); ok { @@ -859,7 +860,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk(names.AttrPort); ok { - input.Port = aws.Int64(int64(v.(int))) + input.Port = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk("preferred_backup_window"); ok { @@ -875,12 +876,12 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk(names.AttrVPCSecurityGroupIDs); ok && v.(*schema.Set).Len() > 0 { - input.VpcSecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set)) + input.VpcSecurityGroupIds = flex.ExpandStringValueSet(v.(*schema.Set)) } _, err := tfresource.RetryWhen(ctx, propagationTimeout, func() (interface{}, error) { - return conn.RestoreDBClusterFromS3WithContext(ctx, input) + return conn.RestoreDBClusterFromS3(ctx, input) }, func(err error) (bool, error) { // InvalidParameterValue: Files from the specified Amazon S3 bucket cannot be downloaded. @@ -900,6 +901,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int return false, err }, ) + if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS Cluster (restore from S3) (%s): %s", identifier, err) } @@ -909,24 +911,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), DBClusterIdentifier: aws.String(identifier), DeletionProtection: aws.Bool(d.Get(names.AttrDeletionProtection).(bool)), - Tags: getTagsIn(ctx), - } - - if v, ok := tfMap["restore_to_time"].(string); ok && v != "" { - v, _ := time.Parse(time.RFC3339, v) - input.RestoreToTime = aws.Time(v) - } - - if v, ok := tfMap["source_cluster_identifier"].(string); ok && v != "" { - input.SourceDBClusterIdentifier = aws.String(v) - } - - if v, ok := tfMap["source_cluster_resource_id"].(string); ok && v != "" { - input.SourceDbClusterResourceId = aws.String(v) - } - - if v, ok := tfMap["use_latest_restorable_time"].(bool); ok && v { - input.UseLatestRestorableTime = aws.Bool(v) + Tags: getTagsInV2(ctx), } if v, ok := d.GetOk("backtrack_window"); ok { @@ -934,7 +919,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("backup_retention_period"); ok { - modifyDbClusterInput.BackupRetentionPeriod = aws.Int64(int64(v.(int))) + modifyDbClusterInput.BackupRetentionPeriod = aws.Int32(int32(v.(int))) requiresModifyDbCluster = true } @@ -955,7 +940,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("enabled_cloudwatch_logs_exports"); ok && v.(*schema.Set).Len() > 0 { - input.EnableCloudwatchLogsExports = flex.ExpandStringSet(v.(*schema.Set)) + input.EnableCloudwatchLogsExports = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("iam_database_authentication_enabled"); ok { @@ -990,7 +975,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk(names.AttrPort); ok { - input.Port = aws.Int64(int64(v.(int))) + input.Port = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk("preferred_backup_window"); ok { @@ -1003,6 +988,11 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int requiresModifyDbCluster = true } + if v, ok := tfMap["restore_to_time"].(string); ok && v != "" { + v, _ := time.Parse(time.RFC3339, v) + input.RestoreToTime = aws.Time(v) + } + if v, ok := tfMap["restore_type"].(string); ok { input.RestoreType = aws.String(v) } @@ -1017,12 +1007,24 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int requiresModifyDbCluster = true } + if v, ok := tfMap["source_cluster_identifier"].(string); ok && v != "" { + input.SourceDBClusterIdentifier = aws.String(v) + } + + if v, ok := tfMap["source_cluster_resource_id"].(string); ok && v != "" { + input.SourceDbClusterResourceId = aws.String(v) + } + + if v, ok := tfMap["use_latest_restorable_time"].(bool); ok && v { + input.UseLatestRestorableTime = aws.Bool(v) + } + if v, ok := d.GetOk(names.AttrVPCSecurityGroupIDs); ok && v.(*schema.Set).Len() > 0 { - input.VpcSecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set)) + input.VpcSecurityGroupIds = flex.ExpandStringValueSet(v.(*schema.Set)) } - log.Printf("[DEBUG] Creating RDS Cluster: %s", input) - _, err := conn.RestoreDBClusterToPointInTimeWithContext(ctx, input) + _, err := conn.RestoreDBClusterToPointInTime(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "creating RDS Cluster (restore to point-in-time) (%s): %s", identifier, err) } @@ -1033,15 +1035,15 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int DeletionProtection: aws.Bool(d.Get(names.AttrDeletionProtection).(bool)), Engine: aws.String(d.Get(names.AttrEngine).(string)), EngineMode: aws.String(d.Get("engine_mode").(string)), - Tags: getTagsIn(ctx), + Tags: getTagsInV2(ctx), } if v, ok := d.GetOkExists(names.AttrAllocatedStorage); ok { - input.AllocatedStorage = aws.Int64(int64(v.(int))) + input.AllocatedStorage = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk(names.AttrAvailabilityZones); ok && v.(*schema.Set).Len() > 0 { - input.AvailabilityZones = flex.ExpandStringSet(v.(*schema.Set)) + input.AvailabilityZones = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("backtrack_window"); ok { @@ -1049,7 +1051,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("backup_retention_period"); ok { - input.BackupRetentionPeriod = aws.Int64(int64(v.(int))) + input.BackupRetentionPeriod = aws.Int32(int32(v.(int))) } if v := d.Get("ca_certificate_identifier"); v.(string) != "" { @@ -1097,7 +1099,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("enabled_cloudwatch_logs_exports"); ok && v.(*schema.Set).Len() > 0 { - input.EnableCloudwatchLogsExports = flex.ExpandStringSet(v.(*schema.Set)) + input.EnableCloudwatchLogsExports = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("engine_lifecycle_support"); ok { @@ -1117,7 +1119,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOkExists(names.AttrIOPS); ok { - input.Iops = aws.Int64(int64(v.(int))) + input.Iops = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk(names.AttrKMSKeyID); ok { @@ -1136,6 +1138,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int if v, ok := d.GetOk("master_password"); ok { input.MasterUserPassword = aws.String(v.(string)) } + if v, ok := d.GetOk("master_user_secret_kms_key_id"); ok { input.MasterUserSecretKmsKeyId = aws.String(v.(string)) } @@ -1157,11 +1160,11 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk("performance_insights_retention_period"); ok { - input.PerformanceInsightsRetentionPeriod = aws.Int64(int64(v.(int))) + input.PerformanceInsightsRetentionPeriod = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk(names.AttrPort); ok { - input.Port = aws.Int64(int64(v.(int))) + input.Port = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk("preferred_backup_window"); ok { @@ -1197,14 +1200,15 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } if v, ok := d.GetOk(names.AttrVPCSecurityGroupIDs); ok && v.(*schema.Set).Len() > 0 { - input.VpcSecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set)) + input.VpcSecurityGroupIds = flex.ExpandStringValueSet(v.(*schema.Set)) } _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, func() (interface{}, error) { - return conn.CreateDBClusterWithContext(ctx, input) + return conn.CreateDBCluster(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 Cluster (%s): %s", identifier, err) } @@ -1219,7 +1223,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int if v, ok := d.GetOk("iam_roles"); ok && v.(*schema.Set).Len() > 0 { for _, v := range v.(*schema.Set).List() { if err := addIAMRoleToCluster(ctx, conn, d.Id(), v.(string)); err != nil { - return sdkdiag.AppendErrorf(diags, "adding IAM Role (%s) to RDS Cluster (%s): %s", v, d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } } } @@ -1227,7 +1231,8 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int if requiresModifyDbCluster { modifyDbClusterInput.DBClusterIdentifier = aws.String(d.Id()) - _, err := conn.ModifyDBClusterWithContext(ctx, modifyDbClusterInput) + _, err := conn.ModifyDBCluster(ctx, modifyDbClusterInput) + if err != nil { return sdkdiag.AppendErrorf(diags, "updating RDS Cluster (%s): %s", d.Id(), err) } @@ -1241,9 +1246,9 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) - dbc, err := FindDBClusterByID(ctx, conn, d.Id()) + dbc, err := findDBClusterByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS Cluster (%s) not found, removing from state", d.Id()) @@ -1256,9 +1261,9 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter } d.Set(names.AttrAllocatedStorage, dbc.AllocatedStorage) - clusterARN := aws.StringValue(dbc.DBClusterArn) + clusterARN := aws.ToString(dbc.DBClusterArn) d.Set(names.AttrARN, clusterARN) - d.Set(names.AttrAvailabilityZones, aws.StringValueSlice(dbc.AvailabilityZones)) + d.Set(names.AttrAvailabilityZones, dbc.AvailabilityZones) d.Set("backtrack_window", dbc.BacktrackWindow) d.Set("backup_retention_period", dbc.BackupRetentionPeriod) if dbc.CertificateDetails != nil { @@ -1266,12 +1271,10 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set("ca_certificate_valid_till", dbc.CertificateDetails.ValidTill.Format(time.RFC3339)) } d.Set(names.AttrClusterIdentifier, dbc.DBClusterIdentifier) - d.Set("cluster_identifier_prefix", create.NamePrefixFromName(aws.StringValue(dbc.DBClusterIdentifier))) - var clusterMembers []string - for _, v := range dbc.DBClusterMembers { - clusterMembers = append(clusterMembers, aws.StringValue(v.DBInstanceIdentifier)) - } - d.Set("cluster_members", clusterMembers) + d.Set("cluster_identifier_prefix", create.NamePrefixFromName(aws.ToString(dbc.DBClusterIdentifier))) + d.Set("cluster_members", tfslices.ApplyToAll(dbc.DBClusterMembers, func(v types.DBClusterMember) string { + return aws.ToString(v.DBInstanceIdentifier) + })) d.Set("cluster_resource_id", dbc.DbClusterResourceId) d.Set("copy_tags_to_snapshot", dbc.CopyTagsToSnapshot) // Only set the DatabaseName if it is not nil. There is a known API bug where @@ -1286,7 +1289,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set("db_subnet_group_name", dbc.DBSubnetGroup) d.Set("db_system_id", dbc.DBSystemId) d.Set(names.AttrDeletionProtection, dbc.DeletionProtection) - if len(dbc.DomainMemberships) > 0 && dbc.DomainMemberships[0] != nil { + if len(dbc.DomainMemberships) > 0 { domainMembership := dbc.DomainMemberships[0] d.Set(names.AttrDomain, domainMembership.Domain) d.Set("domain_iam_role_name", domainMembership.IAMRoleName) @@ -1294,7 +1297,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set(names.AttrDomain, nil) d.Set("domain_iam_role_name", nil) } - d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(dbc.EnabledCloudwatchLogsExports)) + d.Set("enabled_cloudwatch_logs_exports", dbc.EnabledCloudwatchLogsExports) d.Set("enable_http_endpoint", dbc.HttpEndpointEnabled) d.Set(names.AttrEndpoint, dbc.Endpoint) d.Set(names.AttrEngine, dbc.Engine) @@ -1303,14 +1306,11 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter clusterSetResourceDataEngineVersionFromCluster(d, dbc) d.Set(names.AttrHostedZoneID, dbc.HostedZoneId) d.Set("iam_database_authentication_enabled", dbc.IAMDatabaseAuthenticationEnabled) - var iamRoleARNs []string - for _, v := range dbc.AssociatedRoles { - iamRoleARNs = append(iamRoleARNs, aws.StringValue(v.RoleArn)) - } - d.Set("iam_roles", iamRoleARNs) + d.Set("iam_roles", tfslices.ApplyToAll(dbc.AssociatedRoles, func(v types.DBClusterRole) string { + return aws.ToString(v.RoleArn) + })) d.Set(names.AttrIOPS, dbc.Iops) d.Set(names.AttrKMSKeyID, dbc.KmsKeyId) - // Note: the following attributes are not returned by the API // when conducting a read after a create, so we rely on Terraform's // implicit state passthrough, and they are treated as virtual attributes. @@ -1355,17 +1355,15 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter } d.Set(names.AttrStorageEncrypted, dbc.StorageEncrypted) d.Set(names.AttrStorageType, dbc.StorageType) - var securityGroupIDs []string - for _, v := range dbc.VpcSecurityGroups { - securityGroupIDs = append(securityGroupIDs, aws.StringValue(v.VpcSecurityGroupId)) - } - d.Set(names.AttrVPCSecurityGroupIDs, securityGroupIDs) + d.Set(names.AttrVPCSecurityGroupIDs, tfslices.ApplyToAll(dbc.VpcSecurityGroups, func(v types.VpcSecurityGroupMembership) string { + return aws.ToString(v.VpcSecurityGroupId) + })) // Fetch and save Global Cluster if engine mode global d.Set("global_cluster_identifier", "") - if aws.StringValue(dbc.EngineMode) == engineModeGlobal || aws.StringValue(dbc.EngineMode) == engineModeProvisioned { - globalCluster, err := findGlobalClusterByDBClusterARN(ctx, meta.(*conns.AWSClient).RDSClient(ctx), aws.StringValue(dbc.DBClusterArn)) + if engineMode := aws.ToString(dbc.EngineMode); engineMode == engineModeGlobal || engineMode == engineModeProvisioned { + globalCluster, err := findGlobalClusterByDBClusterARN(ctx, conn, clusterARN) if err == nil { d.Set("global_cluster_identifier", globalCluster.GlobalClusterIdentifier) @@ -1377,13 +1375,13 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter } } - setTagsOut(ctx, dbc.TagList) + setTagsOutV2(ctx, dbc.TagList) return diags } func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) if d.HasChangesExcept( names.AttrAllowMajorVersionUpgrade, @@ -1401,7 +1399,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange(names.AttrAllocatedStorage) { - input.AllocatedStorage = aws.Int64(int64(d.Get(names.AttrAllocatedStorage).(int))) + input.AllocatedStorage = aws.Int32(int32(d.Get(names.AttrAllocatedStorage).(int))) } if v, ok := d.GetOk(names.AttrAllowMajorVersionUpgrade); ok { @@ -1413,7 +1411,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange("backup_retention_period") { - input.BackupRetentionPeriod = aws.Int64(int64(d.Get("backup_retention_period").(int))) + input.BackupRetentionPeriod = aws.Int32(int32(d.Get("backup_retention_period").(int))) } if d.HasChange("ca_certificate_identifier") { @@ -1463,13 +1461,12 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange("enabled_cloudwatch_logs_exports") { - oraw, nraw := d.GetChange("enabled_cloudwatch_logs_exports") - o := oraw.(*schema.Set) - n := nraw.(*schema.Set) + o, n := d.GetChange("enabled_cloudwatch_logs_exports") + os, ns := o.(*schema.Set), n.(*schema.Set) - input.CloudwatchLogsExportConfiguration = &rds.CloudwatchLogsExportConfiguration{ - DisableLogTypes: flex.ExpandStringSet(o.Difference(n)), - EnableLogTypes: flex.ExpandStringSet(n.Difference(o)), + input.CloudwatchLogsExportConfiguration = &types.CloudwatchLogsExportConfiguration{ + DisableLogTypes: flex.ExpandStringValueSet(os.Difference(ns)), + EnableLogTypes: flex.ExpandStringValueSet(ns.Difference(os)), } } @@ -1489,17 +1486,19 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange(names.AttrIOPS) { - input.Iops = aws.Int64(int64(d.Get(names.AttrIOPS).(int))) + input.Iops = aws.Int32(int32(d.Get(names.AttrIOPS).(int))) } if d.HasChange("manage_master_user_password") { input.ManageMasterUserPassword = aws.Bool(d.Get("manage_master_user_password").(bool)) } + if d.HasChange("master_password") { if v, ok := d.GetOk("master_password"); ok { input.MasterUserPassword = aws.String(v.(string)) } } + if d.HasChange("master_user_secret_kms_key_id") { if v, ok := d.GetOk("master_user_secret_kms_key_id"); ok { input.MasterUserSecretKmsKeyId = aws.String(v.(string)) @@ -1519,11 +1518,11 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange("performance_insights_retention_period") { - input.PerformanceInsightsRetentionPeriod = aws.Int64(int64(d.Get("performance_insights_retention_period").(int))) + input.PerformanceInsightsRetentionPeriod = aws.Int32(int32(d.Get("performance_insights_retention_period").(int))) } if d.HasChange(names.AttrPort) { - input.Port = aws.Int64(int64(d.Get(names.AttrPort).(int))) + input.Port = aws.Int32(int32(d.Get(names.AttrPort).(int))) } if d.HasChange("preferred_backup_window") { @@ -1552,22 +1551,25 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int if d.HasChange(names.AttrVPCSecurityGroupIDs) { if v, ok := d.GetOk(names.AttrVPCSecurityGroupIDs); ok && v.(*schema.Set).Len() > 0 { - input.VpcSecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set)) + input.VpcSecurityGroupIds = flex.ExpandStringValueSet(v.(*schema.Set)) } else { - input.VpcSecurityGroupIds = aws.StringSlice(nil) + input.VpcSecurityGroupIds = []string{} } } - _, err := tfresource.RetryWhen(ctx, 5*time.Minute, + const ( + timeout = 5 * time.Minute + ) + _, err := tfresource.RetryWhen(ctx, timeout, func() (interface{}, error) { - return conn.ModifyDBClusterWithContext(ctx, input) + return conn.ModifyDBCluster(ctx, input) }, func(err error) (bool, error) { if tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "IAM role ARN value is invalid or does not include the required permissions") { return true, err } - if tfawserr.ErrCodeEquals(err, rds.ErrCodeInvalidDBClusterStateFault) { + if errs.IsA[*types.InvalidDBClusterStateFault](err) { return true, err } @@ -1579,6 +1581,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int return false, err }, ) + if err != nil { return sdkdiag.AppendErrorf(diags, "updating RDS Cluster (%s): %s", d.Id(), err) } @@ -1589,52 +1592,43 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange("global_cluster_identifier") { - oRaw, nRaw := d.GetChange("global_cluster_identifier") - o := oRaw.(string) - n := nRaw.(string) + o, n := d.GetChange("global_cluster_identifier") + os, ns := o.(string), n.(string) - if o == "" { + if os == "" { return sdkdiag.AppendErrorf(diags, "existing RDS Clusters cannot be added to an existing RDS Global Cluster") } - if n != "" { + if ns != "" { return sdkdiag.AppendErrorf(diags, "existing RDS Clusters cannot be migrated between existing RDS Global Clusters") } clusterARN := d.Get(names.AttrARN).(string) input := &rds.RemoveFromGlobalClusterInput{ DbClusterIdentifier: aws.String(clusterARN), - GlobalClusterIdentifier: aws.String(o), + GlobalClusterIdentifier: aws.String(os), } - log.Printf("[DEBUG] Removing RDS Cluster (%s) from RDS Global Cluster: %s", clusterARN, o) - _, err := conn.RemoveFromGlobalClusterWithContext(ctx, input) + _, err := conn.RemoveFromGlobalCluster(ctx, input) - if err != nil && !tfawserr.ErrCodeEquals(err, rds.ErrCodeGlobalClusterNotFoundFault) && !tfawserr.ErrMessageContains(err, "InvalidParameterValue", "is not found in global cluster") { + if err != nil && !errs.IsA[*types.GlobalClusterNotFoundFault](err) && !tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "is not found in global cluster") { return sdkdiag.AppendErrorf(diags, "removing RDS Cluster (%s) from RDS Global Cluster: %s", d.Id(), err) } } if d.HasChange("iam_roles") { - oraw, nraw := d.GetChange("iam_roles") - if oraw == nil { - oraw = new(schema.Set) - } - if nraw == nil { - nraw = new(schema.Set) - } - os := oraw.(*schema.Set) - ns := nraw.(*schema.Set) + o, n := d.GetChange("iam_roles") + os, ns := o.(*schema.Set), n.(*schema.Set) for _, v := range ns.Difference(os).List() { if err := addIAMRoleToCluster(ctx, conn, d.Id(), v.(string)); err != nil { - return sdkdiag.AppendErrorf(diags, "adding IAM Role (%s) to RDS Cluster (%s): %s", v, d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } } for _, v := range os.Difference(ns).List() { if err := removeIAMRoleFromCluster(ctx, conn, d.Id(), v.(string)); err != nil { - return sdkdiag.AppendErrorf(diags, "removing IAM Role (%s) from RDS Cluster (%s): %s", v, d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } } } @@ -1643,7 +1637,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { - conn := meta.(*conns.AWSClient).RDSConn(ctx) + conn := meta.(*conns.AWSClient).RDSClient(ctx) // Automatically remove from global cluster to bypass this error on deletion: // InvalidDBClusterStateFault: This cluster is a part of a global cluster, please remove it from globalcluster first @@ -1655,10 +1649,9 @@ func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta int GlobalClusterIdentifier: aws.String(globalClusterID), } - log.Printf("[DEBUG] Removing RDS Cluster (%s) from RDS Global Cluster: %s", clusterARN, globalClusterID) - _, err := conn.RemoveFromGlobalClusterWithContext(ctx, input) + _, err := conn.RemoveFromGlobalCluster(ctx, input) - if err != nil && !tfawserr.ErrCodeEquals(err, rds.ErrCodeGlobalClusterNotFoundFault) && !tfawserr.ErrMessageContains(err, "InvalidParameterValue", "is not found in global cluster") { + if err != nil && !errs.IsA[*types.GlobalClusterNotFoundFault](err) && !tfawserr.ErrMessageContains(err, errCodeInvalidParameterValue, "is not found in global cluster") { return sdkdiag.AppendErrorf(diags, "removing RDS Cluster (%s) from RDS Global Cluster (%s): %s", d.Id(), globalClusterID, err) } } @@ -1679,16 +1672,19 @@ func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta int } log.Printf("[DEBUG] Deleting RDS Cluster: %s", d.Id()) - _, err := tfresource.RetryWhen(ctx, clusterTimeoutDelete, + const ( + timeout = 2 * time.Minute + ) + _, err := tfresource.RetryWhen(ctx, timeout, func() (interface{}, error) { - return conn.DeleteDBClusterWithContext(ctx, input) + return conn.DeleteDBCluster(ctx, input) }, func(err error) (bool, error) { - if tfawserr.ErrMessageContains(err, "InvalidParameterCombination", "disable deletion pro") { + if tfawserr.ErrMessageContains(err, errCodeInvalidParameterCombination, "disable deletion pro") { if v, ok := d.GetOk(names.AttrDeletionProtection); (!ok || !v.(bool)) && d.Get(names.AttrApplyImmediately).(bool) { _, err := tfresource.RetryWhen(ctx, d.Timeout(schema.TimeoutDelete), func() (interface{}, error) { - return conn.ModifyDBClusterWithContext(ctx, &rds.ModifyDBClusterInput{ + return conn.ModifyDBCluster(ctx, &rds.ModifyDBClusterInput{ ApplyImmediately: aws.Bool(true), DBClusterIdentifier: aws.String(d.Id()), DeletionProtection: aws.Bool(false), @@ -1699,13 +1695,14 @@ func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta int return true, err } - if tfawserr.ErrCodeEquals(err, rds.ErrCodeInvalidDBClusterStateFault) { + if errs.IsA[*types.InvalidDBClusterStateFault](err) { return true, err } return false, err }, ) + if err != nil { return false, fmt.Errorf("modifying RDS Cluster (%s) DeletionProtection=false: %s", d.Id(), err) } @@ -1718,11 +1715,11 @@ func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta int return true, err } - if tfawserr.ErrMessageContains(err, rds.ErrCodeInvalidDBClusterStateFault, "is not currently in the available state") { + if errs.IsAErrorMessageContains[*types.InvalidDBClusterStateFault](err, "is not currently in the available state") { return true, err } - if tfawserr.ErrMessageContains(err, rds.ErrCodeInvalidDBClusterStateFault, "cluster is a part of a global cluster") { + if errs.IsAErrorMessageContains[*types.InvalidDBClusterStateFault](err, "cluster is a part of a global cluster") { return true, err } @@ -1730,7 +1727,7 @@ func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta int }, ) - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterNotFoundFault) { + if errs.IsA[*types.DBClusterNotFoundFault](err) { return diags } @@ -1754,49 +1751,51 @@ func resourceClusterImport(_ context.Context, d *schema.ResourceData, meta inter return []*schema.ResourceData{d}, nil } -func addIAMRoleToCluster(ctx context.Context, conn *rds.RDS, clusterID, roleARN string) error { +func addIAMRoleToCluster(ctx context.Context, conn *rds.Client, clusterID, roleARN string) error { input := &rds.AddRoleToDBClusterInput{ DBClusterIdentifier: aws.String(clusterID), RoleArn: aws.String(roleARN), } - _, err := conn.AddRoleToDBClusterWithContext(ctx, input) + _, err := conn.AddRoleToDBCluster(ctx, input) + if err != nil { - return fmt.Errorf("adding IAM Role (%s) to RDS Cluster (%s): %s", roleARN, clusterID, err) + return fmt.Errorf("adding IAM Role (%s) to RDS Cluster (%s): %w", roleARN, clusterID, err) } return nil } -func removeIAMRoleFromCluster(ctx context.Context, conn *rds.RDS, clusterID, roleARN string) error { +func removeIAMRoleFromCluster(ctx context.Context, conn *rds.Client, clusterID, roleARN string) error { input := &rds.RemoveRoleFromDBClusterInput{ DBClusterIdentifier: aws.String(clusterID), RoleArn: aws.String(roleARN), } - _, err := conn.RemoveRoleFromDBClusterWithContext(ctx, input) + _, err := conn.RemoveRoleFromDBCluster(ctx, input) + if err != nil { - return fmt.Errorf("removing IAM Role (%s) from RDS Cluster (%s): %s", roleARN, clusterID, err) + return fmt.Errorf("removing IAM Role (%s) from RDS Cluster (%s): %w", roleARN, clusterID, err) } return err } -func clusterSetResourceDataEngineVersionFromCluster(d *schema.ResourceData, c *rds.DBCluster) { +func clusterSetResourceDataEngineVersionFromCluster(d *schema.ResourceData, c *types.DBCluster) { oldVersion := d.Get(names.AttrEngineVersion).(string) - newVersion := aws.StringValue(c.EngineVersion) + newVersion := aws.ToString(c.EngineVersion) var pendingVersion string if c.PendingModifiedValues != nil && c.PendingModifiedValues.EngineVersion != nil { - pendingVersion = aws.StringValue(c.PendingModifiedValues.EngineVersion) + pendingVersion = aws.ToString(c.PendingModifiedValues.EngineVersion) } compareActualEngineVersion(d, oldVersion, newVersion, pendingVersion) } -func FindDBClusterByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBCluster, error) { +func findDBClusterByID(ctx context.Context, conn *rds.Client, id string, optFns ...func(*rds.Options)) (*types.DBCluster, error) { input := &rds.DescribeDBClustersInput{ DBClusterIdentifier: aws.String(id), } - output, err := findDBCluster(ctx, conn, input, tfslices.PredicateTrue[*rds.DBCluster]()) + output, err := findDBCluster(ctx, conn, input, tfslices.PredicateTrue[*types.DBCluster](), optFns...) if err != nil { return nil, err @@ -1804,12 +1803,12 @@ func FindDBClusterByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBCl // Eventual consistency check. if arn.IsARN(id) { - if aws.StringValue(output.DBClusterArn) != id { + if aws.ToString(output.DBClusterArn) != id { return nil, &retry.NotFoundError{ LastRequest: input, } } - } else if aws.StringValue(output.DBClusterIdentifier) != id { + } else if aws.ToString(output.DBClusterIdentifier) != id { return nil, &retry.NotFoundError{ LastRequest: input, } @@ -1818,50 +1817,47 @@ func FindDBClusterByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBCl return output, nil } -func findDBCluster(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*rds.DBCluster]) (*rds.DBCluster, error) { - output, err := findDBClusters(ctx, conn, input, filter) +func findDBCluster(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*types.DBCluster], optFns ...func(*rds.Options)) (*types.DBCluster, error) { + output, err := findDBClusters(ctx, conn, input, filter, optFns...) if err != nil { return nil, err } - return tfresource.AssertSinglePtrResult(output) + return tfresource.AssertSingleValueResult(output) } -func findDBClusters(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*rds.DBCluster]) ([]*rds.DBCluster, error) { - var output []*rds.DBCluster +func findDBClusters(ctx context.Context, conn *rds.Client, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*types.DBCluster], optFns ...func(*rds.Options)) ([]types.DBCluster, error) { + var output []types.DBCluster - err := conn.DescribeDBClustersPagesWithContext(ctx, input, func(page *rds.DescribeDBClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + pages := rds.NewDescribeDBClustersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx, optFns...) - for _, v := range page.DBClusters { - if v != nil && filter(v) { - output = append(output, v) + if errs.IsA[*types.DBClusterNotFoundFault](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, } } - return !lastPage - }) - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterNotFoundFault) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, + if err != nil { + return nil, err } - } - if err != nil { - return nil, err + for _, v := range page.DBClusters { + if filter(&v) { + output = append(output, v) + } + } } return output, nil } -func statusDBCluster(ctx context.Context, conn *rds.RDS, id string, waitNoPendingModifiedValues bool) retry.StateRefreshFunc { +func statusDBCluster(ctx context.Context, conn *rds.Client, id string, waitNoPendingModifiedValues bool, optFns ...func(*rds.Options)) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindDBClusterByID(ctx, conn, id) + output, err := findDBClusterByID(ctx, conn, id, optFns...) if tfresource.NotFound(err) { return nil, "", nil @@ -1871,7 +1867,7 @@ func statusDBCluster(ctx context.Context, conn *rds.RDS, id string, waitNoPendin return nil, "", err } - status := aws.StringValue(output.Status) + status := aws.ToString(output.Status) if status == clusterStatusAvailable && waitNoPendingModifiedValues && !itypes.IsZero(output.PendingModifiedValues) { status = clusterStatusAvailableWithPendingModifiedValues @@ -1881,7 +1877,7 @@ func statusDBCluster(ctx context.Context, conn *rds.RDS, id string, waitNoPendin } } -func waitDBClusterCreated(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBCluster, error) { +func waitDBClusterCreated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.DBCluster, error) { stateConf := &retry.StateChangeConf{ Pending: []string{ clusterStatusBackingUp, @@ -1901,14 +1897,14 @@ func waitDBClusterCreated(ctx context.Context, conn *rds.RDS, id string, timeout outputRaw, err := stateConf.WaitForStateContext(ctx) - if output, ok := outputRaw.(*rds.DBCluster); ok { + if output, ok := outputRaw.(*types.DBCluster); ok { return output, err } return nil, err } -func waitDBClusterUpdated(ctx context.Context, conn *rds.RDS, id string, waitNoPendingModifiedValues bool, timeout time.Duration) (*rds.DBCluster, error) { //nolint:unparam +func waitDBClusterUpdated(ctx context.Context, conn *rds.Client, id string, waitNoPendingModifiedValues bool, timeout time.Duration) (*types.DBCluster, error) { //nolint:unparam pendingStatuses := []string{ clusterStatusBackingUp, clusterStatusConfiguringIAMDatabaseAuth, @@ -1934,14 +1930,14 @@ func waitDBClusterUpdated(ctx context.Context, conn *rds.RDS, id string, waitNoP outputRaw, err := stateConf.WaitForStateContext(ctx) - if output, ok := outputRaw.(*rds.DBCluster); ok { + if output, ok := outputRaw.(*types.DBCluster); ok { return output, err } return nil, err } -func waitDBClusterDeleted(ctx context.Context, conn *rds.RDS, id string, timeout time.Duration) (*rds.DBCluster, error) { +func waitDBClusterDeleted(ctx context.Context, conn *rds.Client, id string, timeout time.Duration) (*types.DBCluster, error) { stateConf := &retry.StateChangeConf{ Pending: []string{ clusterStatusAvailable, @@ -1960,38 +1956,38 @@ func waitDBClusterDeleted(ctx context.Context, conn *rds.RDS, id string, timeout outputRaw, err := stateConf.WaitForStateContext(ctx) - if output, ok := outputRaw.(*rds.DBCluster); ok { + if output, ok := outputRaw.(*types.DBCluster); ok { return output, err } return nil, err } -func expandScalingConfiguration(tfMap map[string]interface{}) *rds.ScalingConfiguration { +func expandScalingConfiguration(tfMap map[string]interface{}) *types.ScalingConfiguration { if tfMap == nil { return nil } - apiObject := &rds.ScalingConfiguration{} + apiObject := &types.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)) + apiObject.MaxCapacity = aws.Int32(int32(v)) } if v, ok := tfMap["min_capacity"].(int); ok { - apiObject.MinCapacity = aws.Int64(int64(v)) + apiObject.MinCapacity = aws.Int32(int32(v)) } if v, ok := tfMap["seconds_before_timeout"].(int); ok { - apiObject.SecondsBeforeTimeout = aws.Int64(int64(v)) + apiObject.SecondsBeforeTimeout = aws.Int32(int32(v)) } if v, ok := tfMap["seconds_until_auto_pause"].(int); ok { - apiObject.SecondsUntilAutoPause = aws.Int64(int64(v)) + apiObject.SecondsUntilAutoPause = aws.Int32(int32(v)) } if v, ok := tfMap["timeout_action"].(string); ok && v != "" { @@ -2001,7 +1997,7 @@ func expandScalingConfiguration(tfMap map[string]interface{}) *rds.ScalingConfig return apiObject } -func flattenScalingConfigurationInfo(apiObject *rds.ScalingConfigurationInfo) map[string]interface{} { +func flattenScalingConfigurationInfo(apiObject *types.ScalingConfigurationInfo) map[string]interface{} { if apiObject == nil { return nil } @@ -2009,42 +2005,42 @@ func flattenScalingConfigurationInfo(apiObject *rds.ScalingConfigurationInfo) ma tfMap := map[string]interface{}{} if v := apiObject.AutoPause; v != nil { - tfMap["auto_pause"] = aws.BoolValue(v) + tfMap["auto_pause"] = aws.ToBool(v) } if v := apiObject.MaxCapacity; v != nil { - tfMap[names.AttrMaxCapacity] = aws.Int64Value(v) + tfMap[names.AttrMaxCapacity] = aws.ToInt32(v) } if v := apiObject.MaxCapacity; v != nil { - tfMap[names.AttrMaxCapacity] = aws.Int64Value(v) + tfMap[names.AttrMaxCapacity] = aws.ToInt32(v) } if v := apiObject.MinCapacity; v != nil { - tfMap["min_capacity"] = aws.Int64Value(v) + tfMap["min_capacity"] = aws.ToInt32(v) } if v := apiObject.SecondsBeforeTimeout; v != nil { - tfMap["seconds_before_timeout"] = aws.Int64Value(v) + tfMap["seconds_before_timeout"] = aws.ToInt32(v) } if v := apiObject.SecondsUntilAutoPause; v != nil { - tfMap["seconds_until_auto_pause"] = aws.Int64Value(v) + tfMap["seconds_until_auto_pause"] = aws.ToInt32(v) } if v := apiObject.TimeoutAction; v != nil { - tfMap["timeout_action"] = aws.StringValue(v) + tfMap["timeout_action"] = aws.ToString(v) } return tfMap } -func expandServerlessV2ScalingConfiguration(tfMap map[string]interface{}) *rds.ServerlessV2ScalingConfiguration { +func expandServerlessV2ScalingConfiguration(tfMap map[string]interface{}) *types.ServerlessV2ScalingConfiguration { if tfMap == nil { return nil } - apiObject := &rds.ServerlessV2ScalingConfiguration{} + apiObject := &types.ServerlessV2ScalingConfiguration{} if v, ok := tfMap[names.AttrMaxCapacity].(float64); ok && v != 0.0 { apiObject.MaxCapacity = aws.Float64(v) @@ -2057,7 +2053,7 @@ func expandServerlessV2ScalingConfiguration(tfMap map[string]interface{}) *rds.S return apiObject } -func flattenServerlessV2ScalingConfigurationInfo(apiObject *rds.ServerlessV2ScalingConfigurationInfo) map[string]interface{} { +func flattenServerlessV2ScalingConfigurationInfo(apiObject *types.ServerlessV2ScalingConfigurationInfo) map[string]interface{} { if apiObject == nil { return nil } @@ -2065,31 +2061,11 @@ func flattenServerlessV2ScalingConfigurationInfo(apiObject *rds.ServerlessV2Scal tfMap := map[string]interface{}{} if v := apiObject.MaxCapacity; v != nil { - tfMap[names.AttrMaxCapacity] = aws.Float64Value(v) + tfMap[names.AttrMaxCapacity] = aws.ToFloat64(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) + tfMap["min_capacity"] = aws.ToFloat64(v) } return tfMap diff --git a/internal/service/rds/cluster_activity_stream.go b/internal/service/rds/cluster_activity_stream.go index 909a34222a55..0938b013c5a6 100644 --- a/internal/service/rds/cluster_activity_stream.go +++ b/internal/service/rds/cluster_activity_stream.go @@ -144,7 +144,7 @@ func resourceClusterActivityStreamDelete(ctx context.Context, d *schema.Resource } func findDBClusterWithActivityStream(ctx context.Context, conn *rds.Client, arn string) (*types.DBCluster, error) { - output, err := findDBClusterByIDV2(ctx, conn, arn) + output, err := findDBClusterByID(ctx, conn, arn) if err != nil { return nil, err diff --git a/internal/service/rds/cluster_data_source.go b/internal/service/rds/cluster_data_source.go index 60685dc8a5a7..0db67df43803 100644 --- a/internal/service/rds/cluster_data_source.go +++ b/internal/service/rds/cluster_data_source.go @@ -180,7 +180,7 @@ func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta int conn := meta.(*conns.AWSClient).RDSClient(ctx) dbClusterID := d.Get(names.AttrClusterIdentifier).(string) - dbc, err := findDBClusterByIDV2(ctx, conn, dbClusterID) + dbc, err := findDBClusterByID(ctx, conn, dbClusterID) if err != nil { return sdkdiag.AppendErrorf(diags, "reading RDS Cluster (%s): %s", dbClusterID, err) diff --git a/internal/service/rds/cluster_instance.go b/internal/service/rds/cluster_instance.go index 9a1339d966fe..f61b26e82917 100644 --- a/internal/service/rds/cluster_instance.go +++ b/internal/service/rds/cluster_instance.go @@ -12,6 +12,7 @@ import ( "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -21,6 +22,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" + 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" @@ -376,7 +378,7 @@ func resourceClusterInstanceRead(ctx context.Context, d *schema.ResourceData, me return sdkdiag.AppendErrorf(diags, "DBClusterIdentifier is missing from RDS Cluster Instance (%s). The aws_db_instance resource should be used for non-Aurora instances", d.Id()) } - dbc, err := FindDBClusterByID(ctx, conn, dbClusterID) + dbc, err := findDBClusterByIDV1(ctx, conn, dbClusterID) if err != nil { return sdkdiag.AppendErrorf(diags, "reading RDS Cluster (%s): %s", dbClusterID, err) } @@ -652,3 +654,71 @@ func clusterSetResourceDataEngineVersionFromClusterInstance(d *schema.ResourceDa } compareActualEngineVersion(d, oldVersion, newVersion, pendingVersion) } + +// TODO Remove once migrate to AWS SDK for Go v2. +func findDBClusterByIDV1(ctx context.Context, conn *rds.RDS, id string) (*rds.DBCluster, error) { + input := &rds.DescribeDBClustersInput{ + DBClusterIdentifier: aws.String(id), + } + output, err := findDBClusterV1(ctx, conn, input, tfslices.PredicateTrue[*rds.DBCluster]()) + + if err != nil { + return nil, err + } + + // Eventual consistency check. + if arn.IsARN(id) { + if aws.StringValue(output.DBClusterArn) != id { + return nil, &retry.NotFoundError{ + LastRequest: input, + } + } + } else if aws.StringValue(output.DBClusterIdentifier) != id { + return nil, &retry.NotFoundError{ + LastRequest: input, + } + } + + return output, nil +} + +func findDBClusterV1(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*rds.DBCluster]) (*rds.DBCluster, error) { + output, err := findDBClustersV1(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSinglePtrResult(output) +} + +func findDBClustersV1(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBClustersInput, filter tfslices.Predicate[*rds.DBCluster]) ([]*rds.DBCluster, error) { + var output []*rds.DBCluster + + err := conn.DescribeDBClustersPagesWithContext(ctx, input, func(page *rds.DescribeDBClustersOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.DBClusters { + if v != nil && filter(v) { + output = append(output, v) + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterNotFoundFault) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} diff --git a/internal/service/rds/cluster_role_association.go b/internal/service/rds/cluster_role_association.go index ee063ee3c530..c9477700f9e8 100644 --- a/internal/service/rds/cluster_role_association.go +++ b/internal/service/rds/cluster_role_association.go @@ -11,7 +11,6 @@ import ( "time" "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" @@ -172,7 +171,7 @@ func clusterRoleAssociationParseResourceID(id string) (string, string, error) { } func findDBClusterRoleByTwoPartKey(ctx context.Context, conn *rds.Client, dbClusterID, roleARN string) (*types.DBClusterRole, error) { - dbCluster, err := findDBClusterByIDV2(ctx, conn, dbClusterID) + dbCluster, err := findDBClusterByID(ctx, conn, dbClusterID) if err != nil { return nil, err @@ -248,68 +247,3 @@ func waitDBClusterRoleAssociationDeleted(ctx context.Context, conn *rds.Client, return nil, err } - -// TODO Remove once aws_rds_cluster is migrated? -func findDBClusterByIDV2(ctx context.Context, conn *rds.Client, id string, optFns ...func(*rds.Options)) (*types.DBCluster, error) { - input := &rds.DescribeDBClustersInput{ - DBClusterIdentifier: aws.String(id), - } - output, err := findDBClusterV2(ctx, conn, input, tfslices.PredicateTrue[*types.DBCluster](), optFns...) - - 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], optFns ...func(*rds.Options)) (*types.DBCluster, error) { - output, err := findDBClustersV2(ctx, conn, input, filter, optFns...) - - 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], optFns ...func(*rds.Options)) ([]types.DBCluster, error) { - var output []types.DBCluster - - pages := rds.NewDescribeDBClustersPaginator(conn, input) - for pages.HasMorePages() { - page, err := pages.NextPage(ctx, optFns...) - - 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_test.go b/internal/service/rds/cluster_test.go index 9832c2c7f02b..9abb9992652e 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -12,10 +12,9 @@ import ( "time" "github.com/YakDriver/regexache" - 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/terraform-plugin-sdk/v2/helper/schema" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -64,7 +63,7 @@ func testAccErrorCheckSkip(t *testing.T) resource.ErrorCheckFunc { func TestAccRDSCluster_basic(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -110,7 +109,7 @@ func TestAccRDSCluster_basic(t *testing.T) { func TestAccRDSCluster_disappears(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -134,7 +133,7 @@ func TestAccRDSCluster_disappears(t *testing.T) { func TestAccRDSCluster_identifierGenerated(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster resourceName := "aws_rds_cluster.test" resource.ParallelTest(t, resource.TestCase{ @@ -158,7 +157,7 @@ func TestAccRDSCluster_identifierGenerated(t *testing.T) { func TestAccRDSCluster_identifierPrefix(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster resourceName := "aws_rds_cluster.test" resource.ParallelTest(t, resource.TestCase{ @@ -182,7 +181,7 @@ func TestAccRDSCluster_identifierPrefix(t *testing.T) { func TestAccRDSCluster_tags(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1, dbCluster2, dbCluster3 rds.DBCluster + var dbCluster1, dbCluster2, dbCluster3 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -228,7 +227,7 @@ func TestAccRDSCluster_allowMajorVersionUpgrade(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" dataSourceName := "data.aws_rds_engine_version.test" @@ -269,7 +268,7 @@ func TestAccRDSCluster_allowMajorVersionUpgradeNoApplyImmediately(t *testing.T) t.Skip("skipping long-running test in short mode") } - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" dataSourceName := "data.aws_rds_engine_version.test" @@ -310,7 +309,7 @@ func TestAccRDSCluster_allowMajorVersionUpgradeWithCustomParametersApplyImm(t *t t.Skip("skipping long-running test in short mode") } - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -348,7 +347,7 @@ func TestAccRDSCluster_allowMajorVersionUpgradeWithCustomParameters(t *testing.T t.Skip("skipping long-running test in short mode") } - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -387,7 +386,7 @@ func TestAccRDSCluster_onlyMajorVersion(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" dataSourceName := "data.aws_rds_engine_version.test" @@ -434,7 +433,7 @@ func TestAccRDSCluster_minorVersion(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -466,7 +465,7 @@ func TestAccRDSCluster_minorVersion(t *testing.T) { func TestAccRDSCluster_availabilityZones(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -488,7 +487,7 @@ func TestAccRDSCluster_availabilityZones(t *testing.T) { func TestAccRDSCluster_availabilityZones_caCertificateIdentifier(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -516,7 +515,7 @@ func TestAccRDSCluster_storageTypeIo1(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -543,7 +542,7 @@ func TestAccRDSCluster_storageTypeIo2(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -571,7 +570,7 @@ func TestAccRDSCluster_storageTypeAuroraReturnsBlank(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) storageTypeAurora := "aurora" storageTypeEmpty := "" @@ -600,7 +599,7 @@ func TestAccRDSCluster_storageTypeAuroraIopt1(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) storageType := "aurora-iopt1" resourceName := "aws_rds_cluster.test" @@ -628,7 +627,7 @@ func TestAccRDSCluster_storageTypeAuroraUpdateAuroraIopt1(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) storageTypeEmpty := "" storageTypeAuroraIOPT1 := "aurora-iopt1" @@ -665,7 +664,7 @@ func TestAccRDSCluster_allocatedStorage(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -693,7 +692,7 @@ func TestAccRDSCluster_storageTypeAuroraIopt1UpdateAurora(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) storageTypeAuroraIOPT1 := "aurora-iopt1" storageTypeAurora := "aurora" @@ -731,7 +730,7 @@ func TestAccRDSCluster_iops(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -758,7 +757,7 @@ func TestAccRDSCluster_dbClusterInstanceClass(t *testing.T) { } ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -788,7 +787,7 @@ func TestAccRDSCluster_dbClusterInstanceClass(t *testing.T) { func TestAccRDSCluster_backtrackWindow(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster resourceName := "aws_rds_cluster.test" resource.ParallelTest(t, resource.TestCase{ @@ -817,7 +816,7 @@ func TestAccRDSCluster_backtrackWindow(t *testing.T) { func TestAccRDSCluster_dbSubnetGroupName(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -843,7 +842,7 @@ func TestAccRDSCluster_pointInTimeRestore(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var sourceDBCluster, dbCluster rds.DBCluster + var sourceDBCluster, dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceResourceName := "aws_rds_cluster.test" resourceName := "aws_rds_cluster.restore" @@ -872,7 +871,7 @@ func TestAccRDSCluster_pointInTimeRestoreViaResourceID(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var sourceDBCluster, dbCluster rds.DBCluster + var sourceDBCluster, dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceResourceName := "aws_rds_cluster.test" resourceName := "aws_rds_cluster.restore" @@ -901,7 +900,7 @@ func TestAccRDSCluster_PointInTimeRestore_enabledCloudWatchLogsExports(t *testin t.Skip("skipping long-running test in short mode") } - var sourceDBCluster, dbCluster rds.DBCluster + var sourceDBCluster, dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) sourceResourceName := "aws_rds_cluster.test" resourceName := "aws_rds_cluster.restore" @@ -927,7 +926,7 @@ func TestAccRDSCluster_PointInTimeRestore_enabledCloudWatchLogsExports(t *testin func TestAccRDSCluster_takeFinalSnapshot(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -972,7 +971,7 @@ func TestAccRDSCluster_domain(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -998,7 +997,7 @@ func TestAccRDSCluster_domain(t *testing.T) { func TestAccRDSCluster_EnabledCloudWatchLogsExports_mySQL(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1, dbCluster2, dbCluster3 rds.DBCluster + var dbCluster1, dbCluster2, dbCluster3 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1043,7 +1042,7 @@ func TestAccRDSCluster_EnabledCloudWatchLogsExports_postgresql(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1076,7 +1075,7 @@ func TestAccRDSCluster_EnabledCloudWatchLogsExports_postgresql(t *testing.T) { func TestAccRDSCluster_updateIAMRoles(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster ri := sdkacctest.RandInt() resourceName := "aws_rds_cluster.test" @@ -1114,7 +1113,7 @@ func TestAccRDSCluster_updateIAMRoles(t *testing.T) { func TestAccRDSCluster_kmsKey(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster kmsKeyResourceName := "aws_kms_key.foo" resourceName := "aws_rds_cluster.test" @@ -1141,7 +1140,7 @@ func TestAccRDSCluster_networkType(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1171,7 +1170,7 @@ func TestAccRDSCluster_networkType(t *testing.T) { func TestAccRDSCluster_encrypted(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster resourceName := "aws_rds_cluster.test" resource.ParallelTest(t, resource.TestCase{ @@ -1193,7 +1192,7 @@ func TestAccRDSCluster_encrypted(t *testing.T) { func TestAccRDSCluster_copyTagsToSnapshot(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster rInt := sdkacctest.RandInt() resourceName := "aws_rds_cluster.test" @@ -1230,7 +1229,7 @@ func TestAccRDSCluster_copyTagsToSnapshot(t *testing.T) { func TestAccRDSCluster_copyTagsToSnapshot_restorePointInTime(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster rInt := sdkacctest.RandInt() resourceName := "aws_rds_cluster.test" resourceName2 := "aws_rds_cluster.clone" @@ -1267,8 +1266,8 @@ func TestAccRDSCluster_ReplicationSourceIdentifier_kmsKeyID(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var primaryCluster rds.DBCluster - var replicaCluster rds.DBCluster + var primaryCluster types.DBCluster + var replicaCluster types.DBCluster resourceName := "aws_rds_cluster.test" resourceName2 := "aws_rds_cluster.alternate" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -1299,7 +1298,7 @@ func TestAccRDSCluster_ReplicationSourceIdentifier_kmsKeyID(t *testing.T) { func TestAccRDSCluster_backupsUpdate(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster resourceName := "aws_rds_cluster.test" ri := sdkacctest.RandInt() @@ -1339,7 +1338,7 @@ func TestAccRDSCluster_backupsUpdate(t *testing.T) { func TestAccRDSCluster_iamAuth(t *testing.T) { ctx := acctest.Context(t) - var v rds.DBCluster + var v types.DBCluster resourceName := "aws_rds_cluster.test" resource.ParallelTest(t, resource.TestCase{ @@ -1362,7 +1361,7 @@ func TestAccRDSCluster_iamAuth(t *testing.T) { func TestAccRDSCluster_deletionProtection(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1396,7 +1395,7 @@ func TestAccRDSCluster_engineMode(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1436,7 +1435,7 @@ func TestAccRDSCluster_engineVersion(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" dataSourceName := "data.aws_rds_engine_version.test" @@ -1470,7 +1469,7 @@ func TestAccRDSCluster_engineVersionWithPrimaryInstance(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" dataSourceName := "data.aws_rds_engine_version.test" @@ -1504,7 +1503,7 @@ func TestAccRDSCluster_engineVersionWithPrimaryInstance(t *testing.T) { func TestAccRDSCluster_GlobalClusterIdentifierEngineMode_global(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) globalClusterResourceName := "aws_rds_global_cluster.test" @@ -1529,7 +1528,7 @@ func TestAccRDSCluster_GlobalClusterIdentifierEngineMode_global(t *testing.T) { func TestAccRDSCluster_GlobalClusterIdentifierEngineModeGlobal_add(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1557,7 +1556,7 @@ func TestAccRDSCluster_GlobalClusterIdentifierEngineModeGlobal_add(t *testing.T) func TestAccRDSCluster_GlobalClusterIdentifierEngineModeGlobal_remove(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) globalClusterResourceName := "aws_rds_global_cluster.test" @@ -1589,7 +1588,7 @@ func TestAccRDSCluster_GlobalClusterIdentifierEngineModeGlobal_remove(t *testing func TestAccRDSCluster_GlobalClusterIdentifierEngineModeGlobal_update(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) globalClusterResourceName1 := "aws_rds_global_cluster.test.0" @@ -1619,7 +1618,7 @@ func TestAccRDSCluster_GlobalClusterIdentifierEngineModeGlobal_update(t *testing func TestAccRDSCluster_GlobalClusterIdentifierEngineMode_provisioned(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1 rds.DBCluster + var dbCluster1 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) globalClusterResourceName := "aws_rds_global_cluster.test" @@ -1650,7 +1649,7 @@ func TestAccRDSCluster_GlobalClusterIdentifier_primarySecondaryClusters(t *testi } var providers []*schema.Provider - var primaryDbCluster, secondaryDbCluster rds.DBCluster + var primaryDbCluster, secondaryDbCluster types.DBCluster rNameGlobal := sdkacctest.RandomWithPrefix("tf-acc-test-global") rNamePrimary := sdkacctest.RandomWithPrefix("tf-acc-test-primary") @@ -1688,7 +1687,7 @@ func TestAccRDSCluster_GlobalClusterIdentifier_replicationSourceIdentifier(t *te } var providers []*schema.Provider - var primaryDbCluster, secondaryDbCluster rds.DBCluster + var primaryDbCluster, secondaryDbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceNamePrimary := "aws_rds_cluster.primary" @@ -1723,7 +1722,7 @@ func TestAccRDSCluster_GlobalClusterIdentifier_secondaryClustersWriteForwarding( } var providers []*schema.Provider - var primaryDbCluster, secondaryDbCluster rds.DBCluster + var primaryDbCluster, secondaryDbCluster types.DBCluster rNameGlobal := sdkacctest.RandomWithPrefix("tf-acc-test-global") rNamePrimary := sdkacctest.RandomWithPrefix("tf-acc-test-primary") @@ -1756,7 +1755,7 @@ func TestAccRDSCluster_GlobalClusterIdentifier_secondaryClustersWriteForwarding( func TestAccRDSCluster_ManagedMasterPassword_managed(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1788,7 +1787,7 @@ func TestAccRDSCluster_ManagedMasterPassword_managed(t *testing.T) { func TestAccRDSCluster_ManagedMasterPassword_managedSpecificKMSKey(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1824,7 +1823,7 @@ func TestAccRDSCluster_ManagedMasterPassword_convertToManaged(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1860,7 +1859,7 @@ func TestAccRDSCluster_ManagedMasterPassword_convertToManaged(t *testing.T) { func TestAccRDSCluster_port(t *testing.T) { ctx := acctest.Context(t) - var dbCluster1, dbCluster2 rds.DBCluster + var dbCluster1, dbCluster2 types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1894,7 +1893,7 @@ func TestAccRDSCluster_scaling(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1941,7 +1940,7 @@ func TestAccRDSCluster_serverlessV2ScalingConfiguration(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -1977,7 +1976,7 @@ func TestAccRDSCluster_serverlessV2ScalingConfiguration(t *testing.T) { // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/11698 func TestAccRDSCluster_Scaling_defaultMinCapacity(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -2010,7 +2009,7 @@ func TestAccRDSCluster_snapshotIdentifier(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2042,7 +2041,7 @@ func TestAccRDSCluster_SnapshotIdentifier_deletionProtection(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2085,7 +2084,7 @@ func TestAccRDSCluster_SnapshotIdentifierEngineMode_provisioned(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2119,7 +2118,7 @@ func TestAccRDSCluster_SnapshotIdentifierEngineMode_serverless(t *testing.T) { ctx := acctest.Context(t) - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2153,7 +2152,7 @@ func TestAccRDSCluster_SnapshotIdentifierEngineVersion_different(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2188,7 +2187,7 @@ func TestAccRDSCluster_SnapshotIdentifierEngineVersion_equal(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2222,7 +2221,7 @@ func TestAccRDSCluster_SnapshotIdentifier_kmsKeyID(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2256,7 +2255,7 @@ func TestAccRDSCluster_SnapshotIdentifier_masterPassword(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2289,7 +2288,7 @@ func TestAccRDSCluster_SnapshotIdentifier_masterUsername(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2324,7 +2323,7 @@ func TestAccRDSCluster_SnapshotIdentifier_preferredBackupWindow(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2357,7 +2356,7 @@ func TestAccRDSCluster_SnapshotIdentifier_preferredMaintenanceWindow(t *testing. t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot // This config is version agnostic. Use it as a model for fixing version errors @@ -2393,7 +2392,7 @@ func TestAccRDSCluster_SnapshotIdentifier_tags(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2427,7 +2426,7 @@ func TestAccRDSCluster_SnapshotIdentifier_vpcSecurityGroupIDs(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2463,7 +2462,7 @@ func TestAccRDSCluster_SnapshotIdentifierVPCSecurityGroupIDs_tags(t *testing.T) t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2497,7 +2496,7 @@ func TestAccRDSCluster_SnapshotIdentifier_encryptedRestore(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster, sourceDbCluster rds.DBCluster + var dbCluster, sourceDbCluster types.DBCluster var dbClusterSnapshot types.DBClusterSnapshot rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2532,7 +2531,7 @@ func TestAccRDSCluster_enableHTTPEndpoint(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -2564,7 +2563,7 @@ func TestAccRDSCluster_enableHTTPEndpoint(t *testing.T) { func TestAccRDSCluster_enableHTTPEndpointProvisioned(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -2588,7 +2587,7 @@ func TestAccRDSCluster_enableHTTPEndpointProvisioned(t *testing.T) { func TestAccRDSCluster_password(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -2618,7 +2617,7 @@ func TestAccRDSCluster_password(t *testing.T) { func TestAccRDSCluster_NoDeleteAutomatedBackups(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -2645,14 +2644,14 @@ func TestAccRDSCluster_NoDeleteAutomatedBackups(t *testing.T) { case <-time.After(backupWindowLength): return nil case <-ticker.C: - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) - output, _ := conn.DescribeDBClusterSnapshotsWithContext(ctx, &rds.DescribeDBClusterSnapshotsInput{ + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) + output, _ := conn.DescribeDBClusterSnapshots(ctx, &rds.DescribeDBClusterSnapshotsInput{ DBClusterIdentifier: aws.String(rName), SnapshotType: aws.String("automated"), }) if output != nil && len(output.DBClusterSnapshots) > 0 { snapshot := output.DBClusterSnapshots[0] - if aws.StringValue(snapshot.Status) == "available" { + if aws.ToString(snapshot.Status) == "available" { return nil } } @@ -2679,7 +2678,7 @@ func TestAccRDSCluster_NoDeleteAutomatedBackups(t *testing.T) { func TestAccRDSCluster_engineLifecycleSupport_disabled(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -2711,7 +2710,7 @@ func TestAccRDSCluster_performanceInsightsEnabled(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -2745,7 +2744,7 @@ func TestAccRDSCluster_performanceInsightsKMSKeyID(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" kmsKeyResourceName := "aws_kms_key.test" @@ -2773,7 +2772,7 @@ func TestAccRDSCluster_performanceInsightsRetentionPeriod(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" @@ -2802,13 +2801,13 @@ func testAccCheckClusterDestroy(ctx context.Context) resource.TestCheckFunc { func testAccCheckClusterAutomatedBackupsDelete(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" { continue } - describeOutput, err := conn.DescribeDBClusterSnapshotsWithContext(ctx, &rds.DescribeDBClusterSnapshotsInput{ + describeOutput, err := conn.DescribeDBClusterSnapshots(ctx, &rds.DescribeDBClusterSnapshotsInput{ DBClusterIdentifier: aws.String(rs.Primary.Attributes[names.AttrClusterIdentifier]), SnapshotType: aws.String("automated"), }) @@ -2820,7 +2819,7 @@ func testAccCheckClusterAutomatedBackupsDelete(ctx context.Context) resource.Tes return fmt.Errorf("Automated backup for %s not found", rs.Primary.Attributes[names.AttrClusterIdentifier]) } - _, err = conn.DeleteDBClusterAutomatedBackupWithContext(ctx, &rds.DeleteDBClusterAutomatedBackupInput{ + _, err = conn.DeleteDBClusterAutomatedBackup(ctx, &rds.DeleteDBClusterAutomatedBackupInput{ DbClusterResourceId: aws.String(rs.Primary.Attributes["cluster_resource_id"]), }) if err != nil { @@ -2834,7 +2833,7 @@ func testAccCheckClusterAutomatedBackupsDelete(ctx context.Context) resource.Tes func testAccCheckClusterDestroyWithProvider(ctx context.Context) acctest.TestCheckWithProviderFunc { return func(s *terraform.State, provider *schema.Provider) error { - conn := provider.Meta().(*conns.AWSClient).RDSConn(ctx) + conn := provider.Meta().(*conns.AWSClient).RDSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_rds_cluster" { @@ -2860,8 +2859,7 @@ func testAccCheckClusterDestroyWithProvider(ctx context.Context) acctest.TestChe func testAccCheckClusterDestroyWithFinalSnapshot(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn1 := acctest.Provider.Meta().(*conns.AWSClient).RDSConn(ctx) - conn2 := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).RDSClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_rds_cluster" { @@ -2869,12 +2867,12 @@ func testAccCheckClusterDestroyWithFinalSnapshot(ctx context.Context) resource.T } finalSnapshotID := rs.Primary.Attributes[names.AttrFinalSnapshotIdentifier] - _, err := tfrds.FindDBClusterSnapshotByID(ctx, conn2, finalSnapshotID) + _, err := tfrds.FindDBClusterSnapshotByID(ctx, conn, finalSnapshotID) if err != nil { return err } - _, err = conn2.DeleteDBClusterSnapshot(ctx, &rds_sdkv2.DeleteDBClusterSnapshotInput{ + _, err = conn.DeleteDBClusterSnapshot(ctx, &rds.DeleteDBClusterSnapshotInput{ DBClusterSnapshotIdentifier: aws.String(finalSnapshotID), }) @@ -2882,7 +2880,7 @@ func testAccCheckClusterDestroyWithFinalSnapshot(ctx context.Context) resource.T return err } - _, err = tfrds.FindDBClusterByID(ctx, conn1, rs.Primary.ID) + _, err = tfrds.FindDBClusterByID(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { continue @@ -2899,22 +2897,18 @@ func testAccCheckClusterDestroyWithFinalSnapshot(ctx context.Context) resource.T } } -func testAccCheckClusterExists(ctx context.Context, n string, v *rds.DBCluster) resource.TestCheckFunc { +func testAccCheckClusterExists(ctx context.Context, n string, v *types.DBCluster) resource.TestCheckFunc { return testAccCheckClusterExistsWithProvider(ctx, n, v, func() *schema.Provider { return acctest.Provider }) } -func testAccCheckClusterExistsWithProvider(ctx context.Context, n string, v *rds.DBCluster, providerF func() *schema.Provider) resource.TestCheckFunc { +func testAccCheckClusterExistsWithProvider(ctx context.Context, n string, v *types.DBCluster, providerF func() *schema.Provider) 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 ID is set") - } - - conn := providerF().Meta().(*conns.AWSClient).RDSConn(ctx) + conn := providerF().Meta().(*conns.AWSClient).RDSClient(ctx) output, err := tfrds.FindDBClusterByID(ctx, conn, rs.Primary.ID) if err != nil { @@ -2927,9 +2921,9 @@ func testAccCheckClusterExistsWithProvider(ctx context.Context, n string, v *rds } } -func testAccCheckClusterRecreated(i, j *rds.DBCluster) resource.TestCheckFunc { +func testAccCheckClusterRecreated(i, j *types.DBCluster) resource.TestCheckFunc { return func(s *terraform.State) error { - if aws.TimeValue(i.ClusterCreateTime).Equal(aws.TimeValue(j.ClusterCreateTime)) { + if aws.ToTime(i.ClusterCreateTime).Equal(aws.ToTime(j.ClusterCreateTime)) { return errors.New("RDS Cluster was not recreated") } @@ -2937,9 +2931,9 @@ func testAccCheckClusterRecreated(i, j *rds.DBCluster) resource.TestCheckFunc { } } -func testAccCheckClusterNotRecreated(i, j *rds.DBCluster) resource.TestCheckFunc { +func testAccCheckClusterNotRecreated(i, j *types.DBCluster) resource.TestCheckFunc { return func(s *terraform.State) error { - if !aws.TimeValue(i.ClusterCreateTime).Equal(aws.TimeValue(j.ClusterCreateTime)) { + if !aws.ToTime(i.ClusterCreateTime).Equal(aws.ToTime(j.ClusterCreateTime)) { return errors.New("RDS Cluster was recreated") } @@ -2949,7 +2943,7 @@ func testAccCheckClusterNotRecreated(i, j *rds.DBCluster) resource.TestCheckFunc func TestAccRDSCluster_localWriteForwarding(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster.test" diff --git a/internal/service/rds/clusters_data_source_test.go b/internal/service/rds/clusters_data_source_test.go index 99562b55559f..ac669688eda1 100644 --- a/internal/service/rds/clusters_data_source_test.go +++ b/internal/service/rds/clusters_data_source_test.go @@ -7,7 +7,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-provider-aws/internal/acctest" @@ -16,7 +16,7 @@ import ( func TestAccRDSClustersDataSource_filter(t *testing.T) { ctx := acctest.Context(t) - var dbCluster rds.DBCluster + var dbCluster types.DBCluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceName := "data.aws_rds_clusters.test" resourceName := "aws_rds_cluster.test" diff --git a/internal/service/rds/exports_test.go b/internal/service/rds/exports_test.go index ced3ac141906..f57240368c1b 100644 --- a/internal/service/rds/exports_test.go +++ b/internal/service/rds/exports_test.go @@ -31,6 +31,7 @@ var ( ClusterIDAndRegionFromARN = clusterIDAndRegionFromARN FindCustomDBEngineVersionByTwoPartKey = findCustomDBEngineVersionByTwoPartKey + FindDBClusterByID = findDBClusterByID FindDBClusterEndpointByID = findDBClusterEndpointByID FindDBClusterParameterGroupByName = findDBClusterParameterGroupByName FindDBClusterRoleByTwoPartKey = findDBClusterRoleByTwoPartKey diff --git a/internal/service/rds/flex.go b/internal/service/rds/flex.go index bef9051f62fb..9a01388bfc50 100644 --- a/internal/service/rds/flex.go +++ b/internal/service/rds/flex.go @@ -11,6 +11,28 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) +func flattenManagedMasterUserSecret(apiObject *types.MasterUserSecret) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.KmsKeyId; v != nil { + tfMap[names.AttrKMSKeyID] = aws.ToString(v) + } + + if v := apiObject.SecretArn; v != nil { + tfMap["secret_arn"] = aws.ToString(v) + } + + if v := apiObject.SecretStatus; v != nil { + tfMap["secret_status"] = aws.ToString(v) + } + + return tfMap +} + func expandParameters(tfList []interface{}) []types.Parameter { var apiObjects []types.Parameter diff --git a/internal/service/rds/global_cluster.go b/internal/service/rds/global_cluster.go index d8228120a3bf..4434722d9b33 100644 --- a/internal/service/rds/global_cluster.go +++ b/internal/service/rds/global_cluster.go @@ -746,23 +746,6 @@ func clusterIDAndRegionFromARN(clusterARN string) (string, string, error) { return dbi, parsedARN.Region, nil } -// TODO Remove once aws_rds_cluster is migrated? -func statusDBClusterV2(ctx context.Context, conn *rds.Client, id string, optFns ...func(*rds.Options)) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := findDBClusterByIDV2(ctx, conn, id, optFns...) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, aws.ToString(output.Status), nil - } -} - func waitGlobalClusterMemberUpdated(ctx context.Context, conn *rds.Client, id string, timeout time.Duration, optFns ...func(*rds.Options)) (*types.DBCluster, error) { //nolint:unparam stateConf := &retry.StateChangeConf{ Pending: []string{ @@ -774,7 +757,7 @@ func waitGlobalClusterMemberUpdated(ctx context.Context, conn *rds.Client, id st clusterStatusUpgrading, }, Target: []string{clusterStatusAvailable}, - Refresh: statusDBClusterV2(ctx, conn, id, optFns...), + Refresh: statusDBCluster(ctx, conn, id, false, optFns...), Timeout: timeout, MinTimeout: 10 * time.Second, Delay: 30 * time.Second, diff --git a/internal/service/rds/instance.go b/internal/service/rds/instance.go index 7d73b705fb5f..bd645450a8b0 100644 --- a/internal/service/rds/instance.go +++ b/internal/service/rds/instance.go @@ -1942,7 +1942,7 @@ func resourceInstanceRead(ctx context.Context, d *schema.ResourceData, meta inte // Expose the MasterUserSecret structure as a computed attribute // https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/create-db-cluster.html#:~:text=for%20future%20use.-,MasterUserSecret,-%2D%3E%20(structure) if v.MasterUserSecret != nil { - if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecret(v.MasterUserSecret)}); err != nil { + if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecretV1(v.MasterUserSecret)}); err != nil { return sdkdiag.AppendErrorf(diags, "setting master_user_secret: %s", err) } } else { @@ -3093,3 +3093,23 @@ func flattenEndpoint(apiObject *rds.Endpoint) map[string]interface{} { return tfMap } + +// TODO Remove once migrated to AWS SDK for Go v2. +func flattenManagedMasterUserSecretV1(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/instance_data_source.go b/internal/service/rds/instance_data_source.go index b9d3445aa51f..b335c1a19375 100644 --- a/internal/service/rds/instance_data_source.go +++ b/internal/service/rds/instance_data_source.go @@ -280,7 +280,7 @@ func dataSourceInstanceRead(ctx context.Context, d *schema.ResourceData, meta in d.Set("license_model", instance.LicenseModel) d.Set("master_username", instance.MasterUsername) if instance.MasterUserSecret != nil { - if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecret(instance.MasterUserSecret)}); err != nil { + if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecretV1(instance.MasterUserSecret)}); err != nil { return sdkdiag.AppendErrorf(diags, "setting master_user_secret: %s", err) } } From 2d15e90c4db4e55b8346ec21718fa35c0554052a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 7 Aug 2024 14:41:41 -0400 Subject: [PATCH 15/16] d/aws_rds_clusters: Migrate to AWS SDK for Go v2. --- internal/service/rds/clusters_data_source.go | 51 ++++++++------------ internal/service/rds/service_package_gen.go | 3 +- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/internal/service/rds/clusters_data_source.go b/internal/service/rds/clusters_data_source.go index c8cde3ae3b9e..f7fc4f92ade4 100644 --- a/internal/service/rds/clusters_data_source.go +++ b/internal/service/rds/clusters_data_source.go @@ -6,19 +6,21 @@ package rds import ( "context" - "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" - "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" - namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_rds_clusters") -func DataSourceClusters() *schema.Resource { +// @SDKDataSource("aws_rds_clusters", name="Clusters") +func dataSourceClusters() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceClustersRead, @@ -38,45 +40,32 @@ func DataSourceClusters() *schema.Resource { } } -const ( - DSNameClusters = "Clusters Data Source" -) - func dataSourceClustersRead(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.DescribeDBClustersInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).RDSFilters() + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).RDSFilters() } - var clusterArns []string - var clusterIdentifiers []string - - err := conn.DescribeDBClustersPagesWithContext(ctx, input, func(page *rds.DescribeDBClustersOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + clusters, err := findDBClusters(ctx, conn, input, tfslices.PredicateTrue[*types.DBCluster]()) - for _, dbCluster := range page.DBClusters { - if dbCluster == nil { - continue - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading RDS Clusters: %s", err) + } - clusterArns = append(clusterArns, aws.StringValue(dbCluster.DBClusterArn)) - clusterIdentifiers = append(clusterIdentifiers, aws.StringValue(dbCluster.DBClusterIdentifier)) - } + var clusterARNs []string + var clusterIdentifiers []string - return !lastPage - }) - if err != nil { - return create.AppendDiagError(diags, names.RDS, create.ErrActionReading, DSNameClusters, "", err) + for _, cluster := range clusters { + clusterARNs = append(clusterARNs, aws.ToString(cluster.DBClusterArn)) + clusterIdentifiers = append(clusterIdentifiers, aws.ToString(cluster.DBClusterIdentifier)) } d.SetId(meta.(*conns.AWSClient).Region) - d.Set("cluster_arns", clusterArns) + d.Set("cluster_arns", clusterARNs) d.Set("cluster_identifiers", clusterIdentifiers) return diags diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index f4ba61f6bfc3..ca95eb8051cf 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -93,8 +93,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceClusters, + Factory: dataSourceClusters, TypeName: "aws_rds_clusters", + Name: "Clusters", }, { Factory: dataSourceEngineVersion, From ced9672c03b09b55c6ee03f964b7749018c514dc Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 8 Aug 2024 07:57:32 -0400 Subject: [PATCH 16/16] Fix 'TestAccRDSClustersDataSource_filter'. --- internal/service/rds/clusters_data_source_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/service/rds/clusters_data_source_test.go b/internal/service/rds/clusters_data_source_test.go index ac669688eda1..980eb3cb7715 100644 --- a/internal/service/rds/clusters_data_source_test.go +++ b/internal/service/rds/clusters_data_source_test.go @@ -11,6 +11,7 @@ import ( sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -46,6 +47,7 @@ func testAccClustersDataSourceConfig_filter(rName string) string { resource "aws_rds_cluster" "test" { cluster_identifier = %[1]q database_name = "test" + engine = %[2]q master_username = "tfacctest" master_password = "avoid-plaintext-passwords" skip_final_snapshot = true @@ -54,6 +56,7 @@ resource "aws_rds_cluster" "test" { resource "aws_rds_cluster" "wrong" { cluster_identifier = "wrong-%[1]s" database_name = "test" + engine = %[2]q master_username = "tfacctest" master_password = "avoid-plaintext-passwords" skip_final_snapshot = true @@ -64,6 +67,8 @@ data "aws_rds_clusters" "test" { name = "db-cluster-id" values = [aws_rds_cluster.test.cluster_identifier] } + + depends_on = [aws_rds_cluster.wrong] } -`, rName) +`, rName, tfrds.ClusterEngineAuroraMySQL) }