From de5d53a53b8cf551e69ee3d22c59f1bebb2da1f3 Mon Sep 17 00:00:00 2001 From: Niklas Beinghaus Date: Fri, 22 Apr 2022 16:51:27 +0200 Subject: [PATCH 01/14] Add support for Aurora Serverless v2 --- go.mod | 2 +- go.sum | 2 + internal/service/rds/cluster.go | 60 ++++++-- internal/service/rds/cluster_instance_test.go | 135 ++++++++++++++++++ internal/service/rds/cluster_test.go | 96 +++++++++++++ internal/service/rds/flex.go | 28 ++++ internal/service/rds/flex_test.go | 44 ++++++ website/docs/r/rds_cluster.html.markdown | 19 +++ 8 files changed, 370 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 2f395cb1b31..923ba1df596 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 - github.com/aws/aws-sdk-go v1.43.40 + github.com/aws/aws-sdk-go v1.43.44 github.com/aws/aws-sdk-go-v2 v1.16.2 github.com/aws/aws-sdk-go-v2/service/route53domains v1.12.3 github.com/beevik/etree v1.1.0 diff --git a/go.sum b/go.sum index db7397a6fa7..48f2a0d02c7 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/aws/aws-sdk-go v1.42.18/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm github.com/aws/aws-sdk-go v1.42.52/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aws/aws-sdk-go v1.43.40 h1:xeymFmt2atvG7C9nTjYR1PUt3QZC2sCKvySu/UNdXhM= github.com/aws/aws-sdk-go v1.43.40/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.43.44 h1:t+97cY4ScE/czlNlK5iikUGi7w1fC0uop1OUalDIRT4= +github.com/aws/aws-sdk-go v1.43.44/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v1.15.0/go.mod h1:lJYcuZZEHWNIb6ugJjbQY1fykdoobWbOS7kJYb4APoI= github.com/aws/aws-sdk-go-v2 v1.16.2 h1:fqlCk6Iy3bnCumtrLz9r3mJ/2gUT0pJ0wLFVIdWh+JA= github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index 71283cfd69f..9dcc77870a0 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -236,6 +236,27 @@ func ResourceCluster() *schema.Resource { }, }, + "serverlessv2_scaling_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_capacity": { + Type: schema.TypeFloat, + Optional: true, + Default: clusterScalingConfiguration_DefaultMaxCapacity, + }, + "min_capacity": { + Type: schema.TypeFloat, + Optional: true, + Default: clusterScalingConfiguration_DefaultMinCapacity, + }, + }, + }, + }, + "allocated_storage": { Type: schema.TypeInt, Optional: true, @@ -541,14 +562,15 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error { if _, ok := d.GetOk("snapshot_identifier"); ok { opts := rds.RestoreDBClusterFromSnapshotInput{ - CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), - DBClusterIdentifier: aws.String(identifier), - DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), - Engine: aws.String(d.Get("engine").(string)), - EngineMode: aws.String(d.Get("engine_mode").(string)), - ScalingConfiguration: ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})), - SnapshotIdentifier: aws.String(d.Get("snapshot_identifier").(string)), - Tags: Tags(tags.IgnoreAWS()), + CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), + DBClusterIdentifier: aws.String(identifier), + DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), + Engine: aws.String(d.Get("engine").(string)), + EngineMode: aws.String(d.Get("engine_mode").(string)), + ScalingConfiguration: ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})), + ServerlessV2ScalingConfiguration: ExpandClusterServerlessV2ScalingConfiguration(d.Get("serverlessv2_scaling_configuration").([]interface{})), + SnapshotIdentifier: aws.String(d.Get("snapshot_identifier").(string)), + Tags: Tags(tags.IgnoreAWS()), } if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 { @@ -831,6 +853,8 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error { modifyDbClusterInput.PreferredMaintenanceWindow = aws.String(val.(string)) case "scaling_configuration": modifyDbClusterInput.ScalingConfiguration = ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})) + case "serverlessv2_scaling_configuration": + modifyDbClusterInput.ServerlessV2ScalingConfiguration = ExpandClusterServerlessV2ScalingConfiguration(d.Get("serverlessv2_scaling_configuration").([]interface{})) } } } @@ -847,13 +871,14 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error { } else { createOpts := &rds.CreateDBClusterInput{ - CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), - DBClusterIdentifier: aws.String(identifier), - DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), - Engine: aws.String(d.Get("engine").(string)), - EngineMode: aws.String(d.Get("engine_mode").(string)), - ScalingConfiguration: ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})), - Tags: Tags(tags.IgnoreAWS()), + CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), + DBClusterIdentifier: aws.String(identifier), + DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), + Engine: aws.String(d.Get("engine").(string)), + EngineMode: aws.String(d.Get("engine_mode").(string)), + ScalingConfiguration: ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})), + ServerlessV2ScalingConfiguration: ExpandClusterServerlessV2ScalingConfiguration(d.Get("serverlessv2_scaling_configuration").([]interface{})), + Tags: Tags(tags.IgnoreAWS()), } // Note: Username and password credentials are required and valid @@ -1313,6 +1338,11 @@ func resourceClusterUpdate(d *schema.ResourceData, meta interface{}) error { requestUpdate = true } + if d.HasChange("serverlessv2_scaling_configuration") { + req.ServerlessV2ScalingConfiguration = ExpandClusterServerlessV2ScalingConfiguration(d.Get("serverlessv2_scaling_configuration").([]interface{})) + requestUpdate = true + } + if d.HasChange("enable_http_endpoint") { req.EnableHttpEndpoint = aws.Bool(d.Get("enable_http_endpoint").(bool)) requestUpdate = true diff --git a/internal/service/rds/cluster_instance_test.go b/internal/service/rds/cluster_instance_test.go index a2a18595f16..62f5f4d14d7 100644 --- a/internal/service/rds/cluster_instance_test.go +++ b/internal/service/rds/cluster_instance_test.go @@ -69,6 +69,58 @@ func TestAccRDSClusterInstance_basic(t *testing.T) { }) } +func TestAccRDSClusterInstance_AuroraServerlessV2(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var v rds.DBInstance + resourceName := "aws_rds_cluster_instance.cluster_instances" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckClusterInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClusterInstanceConfigAuroraServerlessV2(sdkacctest.RandInt()), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterInstanceExists(resourceName, &v), + testAccCheckClusterInstanceAttributes(&v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(`db:.+`)), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "true"), + resource.TestCheckResourceAttr(resourceName, "copy_tags_to_snapshot", "false"), + resource.TestCheckResourceAttrSet(resourceName, "preferred_maintenance_window"), + resource.TestCheckResourceAttrSet(resourceName, "preferred_backup_window"), + resource.TestCheckResourceAttrSet(resourceName, "dbi_resource_id"), + resource.TestCheckResourceAttrSet(resourceName, "availability_zone"), + resource.TestCheckResourceAttrSet(resourceName, "engine_version"), + resource.TestCheckResourceAttr(resourceName, "engine", "aurora-mysql"), + resource.TestCheckResourceAttr(resourceName, "instance_class", "db.serverless"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "apply_immediately", + "identifier_prefix", + }, + }, + { + Config: testAccClusterInstanceModifiedConfig(sdkacctest.RandInt()), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterInstanceExists(resourceName, &v), + testAccCheckClusterInstanceAttributes(&v), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + ), + }, + }, + }) +} + func TestAccRDSClusterInstance_isAlreadyBeingDeleted(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -1185,6 +1237,89 @@ resource "aws_db_parameter_group" "bar" { `, n)) } +func testAccClusterInstanceConfigAuroraServerlessV2(n int) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` +data "aws_rds_orderable_db_instance" "test" { + engine = "aurora-mysql" + preferred_instance_classes = ["db.serverless"] +} + +data "aws_rds_engine_version" "default" { + engine = data.aws_rds_orderable_db_instance.test.engine + version = data.aws_rds_orderable_db_instance.test.engine_version +} + +resource "aws_rds_cluster" "default" { + cluster_identifier = "tf-aurora-cluster-test-%[1]d" + availability_zones = [ + data.aws_availability_zones.available.names[0], + data.aws_availability_zones.available.names[1], + data.aws_availability_zones.available.names[2] + ] + serverlessv2_scaling_configuration { + max_capacity = 8.0 + } + engine = data.aws_rds_engine_version.default.engine + engine_version = data.aws_rds_engine_version.default.version + database_name = "mydb" + master_username = "foo" + master_password = "mustbeeightcharacters" + skip_final_snapshot = true +} + +resource "aws_rds_cluster_instance" "cluster_instances" { + identifier = "tf-cluster-instance-%[1]d" + cluster_identifier = aws_rds_cluster.default.id + engine = data.aws_rds_engine_version.default.engine + engine_version = data.aws_rds_engine_version.default.version + instance_class = data.aws_rds_orderable_db_instance.test.instance_class + db_parameter_group_name = "default.${data.aws_rds_engine_version.default.parameter_group_family}" + promotion_tier = "3" +} +`, n)) +} + +func testAccClusterInstanceModifiedConfigAuroraServerlessV2(n int) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptInExclude("af-south-1", "ap-northeast-3", "me-south-1"), fmt.Sprintf(` +data "aws_rds_orderable_db_instance" "test" { + engine = "aurora-mysql" + preferred_instance_classes = ["db.serverless"] +} + +data "aws_rds_engine_version" "default" { + engine = data.aws_rds_orderable_db_instance.test.engine + version = data.aws_rds_orderable_db_instance.test.engine_version +} + +resource "aws_rds_cluster" "default" { + cluster_identifier = "tf-aurora-cluster-test-%[1]d" + availability_zones = [ + data.aws_availability_zones.available.names[0], + data.aws_availability_zones.available.names[1], + data.aws_availability_zones.available.names[2] + ] + serverlessv2_scaling_configuration { + max_capacity = 8.0 + } + database_name = "mydb" + master_username = "foo" + master_password = "mustbeeightcharacters" + skip_final_snapshot = true +} + +resource "aws_rds_cluster_instance" "cluster_instances" { + identifier = "tf-cluster-instance-%[1]d" + cluster_identifier = aws_rds_cluster.default.id + engine = data.aws_rds_engine_version.default.engine + engine_version = data.aws_rds_engine_version.default.version + instance_class = data.aws_rds_orderable_db_instance.test.instance_class + db_parameter_group_name = "default.${data.aws_rds_engine_version.default.parameter_group_family}" + promotion_tier = "3" + auto_minor_version_upgrade = false +} +`, n)) +} + func testAccClusterInstanceConfig_az(n int) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_rds_cluster" "default" { diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index 96c19ae0a15..ea754f3997a 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -987,6 +987,7 @@ func TestAccRDSCluster_engineMode(t *testing.T) { testAccCheckClusterExists(resourceName, &dbCluster1), resource.TestCheckResourceAttr(resourceName, "engine_mode", "serverless"), resource.TestCheckResourceAttr(resourceName, "scaling_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.#", "0"), ), }, { @@ -996,6 +997,7 @@ func TestAccRDSCluster_engineMode(t *testing.T) { testAccCheckClusterRecreated(&dbCluster1, &dbCluster2), resource.TestCheckResourceAttr(resourceName, "engine_mode", "provisioned"), resource.TestCheckResourceAttr(resourceName, "scaling_configuration.#", "0"), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.#", "0"), ), }, }, @@ -1457,6 +1459,44 @@ func TestAccRDSCluster_scaling(t *testing.T) { }) } +func TestAccRDSCluster_scalingv2(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var dbCluster rds.DBCluster + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_rds_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClusterConfig_serverlessv2_ScalingConfiguration(rName, 64.0, 0.5), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(resourceName, &dbCluster), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.0.max_capacity", "64"), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.0.min_capacity", "0.5"), + ), + }, + { + Config: testAccClusterConfig_serverlessv2_ScalingConfiguration(rName, 128.0, 8.5), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(resourceName, &dbCluster), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.0.max_capacity", "128"), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.0.min_capacity", "8.5"), + ), + }, + }, + }) +} + // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/11698 func TestAccRDSCluster_Scaling_defaultMinCapacity(t *testing.T) { var dbCluster rds.DBCluster @@ -1486,6 +1526,31 @@ func TestAccRDSCluster_Scaling_defaultMinCapacity(t *testing.T) { }) } +func TestAccRDSCluster_serverlessv2_Scaling_defaultMinCapacity(t *testing.T) { + var dbCluster rds.DBCluster + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_rds_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClusterConfig_serverlessv2_ScalingConfiguration_DefaultMinCapacity(rName, 128.0), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(resourceName, &dbCluster), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.0.max_capacity", "128"), + resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.0.min_capacity", "1"), + ), + }, + }, + }) +} + func TestAccRDSCluster_snapshotIdentifier(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -3797,6 +3862,22 @@ resource "aws_rds_cluster" "test" { `, rName, autoPause, maxCapacity, minCapacity, secondsUntilAutoPause, timeoutAction) } +func testAccClusterConfig_serverlessv2_ScalingConfiguration(rName string, maxCapacity float64, minCapacity float64) string { + return fmt.Sprintf(` +resource "aws_rds_cluster" "test" { + cluster_identifier = %q + master_password = "barbarbarbar" + master_username = "foo" + skip_final_snapshot = true + + serverlessv2_scaling_configuration { + max_capacity = %f + min_capacity = %f + } +} +`, rName, maxCapacity, minCapacity) +} + func testAccClusterConfig_ScalingConfiguration_DefaultMinCapacity(rName string, autoPause bool, maxCapacity, secondsUntilAutoPause int, timeoutAction string) string { return fmt.Sprintf(` resource "aws_rds_cluster" "test" { @@ -3816,6 +3897,21 @@ resource "aws_rds_cluster" "test" { `, rName, autoPause, maxCapacity, secondsUntilAutoPause, timeoutAction) } +func testAccClusterConfig_serverlessv2_ScalingConfiguration_DefaultMinCapacity(rName string, maxCapacity float64) string { + return fmt.Sprintf(` +resource "aws_rds_cluster" "test" { + cluster_identifier = %q + master_password = "barbarbarbar" + master_username = "foo" + skip_final_snapshot = true + + serverlessv2_scaling_configuration { + max_capacity = %f + } +} +`, rName, maxCapacity) +} + func testAccClusterConfig_SnapshotIdentifier(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster" "source" { diff --git a/internal/service/rds/flex.go b/internal/service/rds/flex.go index c8a6175a817..f76412df81b 100644 --- a/internal/service/rds/flex.go +++ b/internal/service/rds/flex.go @@ -46,6 +46,34 @@ func flattenRDSScalingConfigurationInfo(scalingConfigurationInfo *rds.ScalingCon return []interface{}{m} } +func ExpandClusterServerlessV2ScalingConfiguration(l []interface{}) *rds.ServerlessV2ScalingConfiguration { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + scalingConfiguration := &rds.ServerlessV2ScalingConfiguration{ + MaxCapacity: aws.Float64(float64(m["max_capacity"].(float64))), + MinCapacity: aws.Float64(float64(m["min_capacity"].(float64))), + } + + return scalingConfiguration +} + +func flattenRDSServerlessV2ScalingConfigurationInfo(scalingConfigurationInfo *rds.ServerlessV2ScalingConfigurationInfo) []interface{} { + if scalingConfigurationInfo == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "max_capacity": aws.Float64Value(scalingConfigurationInfo.MaxCapacity), + "min_capacity": aws.Float64Value(scalingConfigurationInfo.MinCapacity), + } + + return []interface{}{m} +} + func expandOptionConfiguration(configured []interface{}) []*rds.OptionConfiguration { var option []*rds.OptionConfiguration diff --git a/internal/service/rds/flex_test.go b/internal/service/rds/flex_test.go index dc787da503e..e7eb95bc7ce 100644 --- a/internal/service/rds/flex_test.go +++ b/internal/service/rds/flex_test.go @@ -127,3 +127,47 @@ func TestExpandClusterScalingConfiguration_basic(t *testing.T) { } } } + +func TestExpandClusterServerlessV2ScalingConfiguration_basic(t *testing.T) { + type testCase struct { + EngineMode string + Input []interface{} + ExpectNil bool + } + cases := []testCase{} + + // RDS Cluster Scaling Configuration v2 is , but we're relying on AWS errors. + // If Terraform adds whole-resource validation, we can do our own validation at plan time. + for _, engineMode := range []string{"global", "multimaster", "parallelquery", "provisioned", "serverless"} { + cases = append(cases, []testCase{ + { + EngineMode: engineMode, + Input: []interface{}{ + map[string]interface{}{ + "max_capacity": 32.0, + "min_capacity": 4.0, + }, + }, + ExpectNil: false, + }, + { + EngineMode: engineMode, + Input: []interface{}{}, + ExpectNil: true, + }, { + EngineMode: engineMode, + Input: []interface{}{ + nil, + }, + ExpectNil: true, + }, + }...) + } + + for _, tc := range cases { + output := ExpandClusterServerlessV2ScalingConfiguration(tc.Input) + if tc.ExpectNil != (output == nil) { + t.Errorf("EngineMode %q: Expected nil: %t, Got: %v", tc.EngineMode, tc.ExpectNil, output) + } + } +} diff --git a/website/docs/r/rds_cluster.html.markdown b/website/docs/r/rds_cluster.html.markdown index 527144a51a9..c944caea236 100644 --- a/website/docs/r/rds_cluster.html.markdown +++ b/website/docs/r/rds_cluster.html.markdown @@ -158,6 +158,7 @@ The following arguments are supported: * `replication_source_identifier` - (Optional) ARN of a source DB cluster or DB instance if this DB cluster is to be created as a Read Replica. If DB Cluster is part of a Global Cluster, use the [`lifecycle` configuration block `ignore_changes` argument](https://www.terraform.io/docs/configuration/meta-arguments/lifecycle.html#ignore_changes) to prevent Terraform from showing differences for this argument instead of configuring this value. * `restore_to_point_in_time` - (Optional) Nested attribute for [point in time restore](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_PIT.html). More details below. * `scaling_configuration` - (Optional) Nested attribute with scaling properties. Only valid when `engine_mode` is set to `serverless`. More details below. +* `serverlessv2_scaling_configuration`- (Optional) Nested attribute with scaling properties. More details below. * `skip_final_snapshot` - (Optional) Determines whether a final DB snapshot is created before the DB cluster is deleted. If true is specified, no DB snapshot is created. If false is specified, a DB snapshot is created before the DB cluster is deleted, using the value from `final_snapshot_identifier`. Default is `false`. * `snapshot_identifier` - (Optional) Specifies whether or not to create this cluster from a snapshot. You can use either the name or ARN when specifying a DB cluster snapshot, or the ARN when specifying a DB snapshot. * `source_region` - (Optional) The source region for an encrypted replica DB cluster. @@ -248,6 +249,24 @@ resource "aws_rds_cluster" "example" { * `seconds_until_auto_pause` - (Optional) The time, in seconds, before an Aurora DB cluster in serverless mode is paused. Valid values are `300` through `86400`. Defaults to `300`. * `timeout_action` - (Optional) The action to take when the timeout is reached. Valid values: `ForceApplyCapacityChange`, `RollbackCapacityChange`. Defaults to `RollbackCapacityChange`. See [documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless.how-it-works.html#aurora-serverless.how-it-works.timeout-action). +### serverlessv2_scaling_configuration Argument Reference + +Example: + +```terraform +resource "aws_rds_cluster" "example" { + # ... other configuration ... + + serverlessv2_scaling_configuration { + max_capacity = 128.0 + min_capacity = 0.5 + } +} +``` + +* `max_capacity` - (Optional) The maximum capacity for an Aurora DB cluster in `serverlessv2` DB engine mode. The maximum capacity must be greater than or equal to the minimum capacity. Valid capacity values are in a range of `0.5` up to `128` in steps of `0.5`. +* `min_capacity` - (Optional) The minimum capacity for an Aurora DB cluster in `serverlessv2` DB engine mode. The minimum capacity must be lesser than or equal to the maximum capacity. Valid capacity values are in a range of `0.5` up to `128` in steps of `0.5`. Default: `1.0`. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: From 11ec5edfadf79daa767565d6bd0f6e53d34a5ad7 Mon Sep 17 00:00:00 2001 From: Niklas Beinghaus Date: Fri, 22 Apr 2022 20:23:38 +0200 Subject: [PATCH 02/14] Add changelog entries --- .changelog/24363.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/24363.txt diff --git a/.changelog/24363.txt b/.changelog/24363.txt new file mode 100644 index 00000000000..9b246ff7ec8 --- /dev/null +++ b/.changelog/24363.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_rds_cluster: Add `serverlessv2_scaling_configuration` argument +``` + +```release-note:enhancement +resource/aws_rds_cluster_instance: Add tests for instance_type `db.serverless` (i.e. provide tests for Aurora Serverless v2) +``` \ No newline at end of file From 39e34b7dd1fc04afdb197d0313970bbf4a90c4d4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 08:13:32 -0400 Subject: [PATCH 03/14] Tweak CHANGELOG entry. --- .changelog/24363.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.changelog/24363.txt b/.changelog/24363.txt index 9b246ff7ec8..0c4a3e4c51a 100644 --- a/.changelog/24363.txt +++ b/.changelog/24363.txt @@ -1,7 +1,3 @@ ```release-note:enhancement -resource/aws_rds_cluster: Add `serverlessv2_scaling_configuration` argument +resource/aws_rds_cluster: Add `serverlessv2_scaling_configuration` argument to support [Aurora Serverless v2](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.html) ``` - -```release-note:enhancement -resource/aws_rds_cluster_instance: Add tests for instance_type `db.serverless` (i.e. provide tests for Aurora Serverless v2) -``` \ No newline at end of file From 010a2134c2f7e8cf1c060a6557133c557a2e86a2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 08:27:47 -0400 Subject: [PATCH 04/14] Correct handling of nested TypeFloat values. --- docs/contributing/data-handling-and-conversion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/data-handling-and-conversion.md b/docs/contributing/data-handling-and-conversion.md index 0f97e5a5a2e..3ecde06eb76 100644 --- a/docs/contributing/data-handling-and-conversion.md +++ b/docs/contributing/data-handling-and-conversion.md @@ -529,7 +529,7 @@ To read: func expandStructure(tfMap map[string]interface{}) *service.Structure { // ... - if v, ok := tfMap["nested_attribute_name"].(int); ok && v != 0.0 { + if v, ok := tfMap["nested_attribute_name"].(float64); ok && v != 0.0 { apiObject.NestedAttributeName = aws.Float64(float64(v)) } From e3f853e3dcac6297362dc0be2c2501f08312ca7c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 08:39:47 -0400 Subject: [PATCH 05/14] r/aws_rds_cluster: Tidy up getting and setting of 'serverlessv2_scaling_configuration'. --- internal/service/rds/cluster.go | 60 +++++++++++++++++++------------ internal/service/rds/flex.go | 36 +++++++++++-------- internal/service/rds/flex_test.go | 44 ----------------------- 3 files changed, 60 insertions(+), 80 deletions(-) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index 9dcc77870a0..3aa68e5605d 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -246,12 +246,10 @@ func ResourceCluster() *schema.Resource { "max_capacity": { Type: schema.TypeFloat, Optional: true, - Default: clusterScalingConfiguration_DefaultMaxCapacity, }, "min_capacity": { Type: schema.TypeFloat, Optional: true, - Default: clusterScalingConfiguration_DefaultMinCapacity, }, }, }, @@ -562,15 +560,14 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error { if _, ok := d.GetOk("snapshot_identifier"); ok { opts := rds.RestoreDBClusterFromSnapshotInput{ - CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), - DBClusterIdentifier: aws.String(identifier), - DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), - Engine: aws.String(d.Get("engine").(string)), - EngineMode: aws.String(d.Get("engine_mode").(string)), - ScalingConfiguration: ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})), - ServerlessV2ScalingConfiguration: ExpandClusterServerlessV2ScalingConfiguration(d.Get("serverlessv2_scaling_configuration").([]interface{})), - SnapshotIdentifier: aws.String(d.Get("snapshot_identifier").(string)), - Tags: Tags(tags.IgnoreAWS()), + CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), + DBClusterIdentifier: aws.String(identifier), + DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), + Engine: aws.String(d.Get("engine").(string)), + EngineMode: aws.String(d.Get("engine_mode").(string)), + ScalingConfiguration: ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})), + SnapshotIdentifier: aws.String(d.Get("snapshot_identifier").(string)), + Tags: Tags(tags.IgnoreAWS()), } if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 { @@ -633,6 +630,10 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error { requiresModifyDbCluster = true } + if v, ok := d.GetOk("serverlessv2_scaling_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + modifyDbClusterInput.ServerlessV2ScalingConfiguration = expandServerlessV2ScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) + } + if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 { opts.VpcSecurityGroupIds = flex.ExpandStringSet(attr) } @@ -854,7 +855,9 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error { case "scaling_configuration": modifyDbClusterInput.ScalingConfiguration = ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})) case "serverlessv2_scaling_configuration": - modifyDbClusterInput.ServerlessV2ScalingConfiguration = ExpandClusterServerlessV2ScalingConfiguration(d.Get("serverlessv2_scaling_configuration").([]interface{})) + if len(val.([]interface{})) > 0 && val.([]interface{})[0] != nil { + modifyDbClusterInput.ServerlessV2ScalingConfiguration = expandServerlessV2ScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) + } } } } @@ -871,14 +874,13 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error { } else { createOpts := &rds.CreateDBClusterInput{ - CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), - DBClusterIdentifier: aws.String(identifier), - DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), - Engine: aws.String(d.Get("engine").(string)), - EngineMode: aws.String(d.Get("engine_mode").(string)), - ScalingConfiguration: ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})), - ServerlessV2ScalingConfiguration: ExpandClusterServerlessV2ScalingConfiguration(d.Get("serverlessv2_scaling_configuration").([]interface{})), - Tags: Tags(tags.IgnoreAWS()), + CopyTagsToSnapshot: aws.Bool(d.Get("copy_tags_to_snapshot").(bool)), + DBClusterIdentifier: aws.String(identifier), + DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), + Engine: aws.String(d.Get("engine").(string)), + EngineMode: aws.String(d.Get("engine_mode").(string)), + ScalingConfiguration: ExpandClusterScalingConfiguration(d.Get("scaling_configuration").([]interface{})), + Tags: Tags(tags.IgnoreAWS()), } // Note: Username and password credentials are required and valid @@ -985,6 +987,10 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error { createOpts.Iops = aws.Int64(int64(attr.(int))) } + if v, ok := d.GetOk("serverlessv2_scaling_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + createOpts.ServerlessV2ScalingConfiguration = expandServerlessV2ScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) + } + if attr, ok := d.GetOkExists("storage_encrypted"); ok { createOpts.StorageEncrypted = aws.Bool(attr.(bool)) } @@ -1171,6 +1177,14 @@ func resourceClusterRead(d *schema.ResourceData, meta interface{}) error { d.Set("iops", dbc.Iops) d.Set("storage_encrypted", dbc.StorageEncrypted) + if dbc.ServerlessV2ScalingConfiguration != nil { + if err := d.Set("serverlessv2_scaling_configuration", []interface{}{flattenServerlessV2ScalingConfigurationInfo(dbc.ServerlessV2ScalingConfiguration)}); err != nil { + return fmt.Errorf("error setting serverlessv2_scaling_configuration: %w", err) + } + } else { + d.Set("serverlessv2_scaling_configuration", nil) + } + d.Set("enable_http_endpoint", dbc.HttpEndpointEnabled) var vpcg []string @@ -1339,8 +1353,10 @@ func resourceClusterUpdate(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("serverlessv2_scaling_configuration") { - req.ServerlessV2ScalingConfiguration = ExpandClusterServerlessV2ScalingConfiguration(d.Get("serverlessv2_scaling_configuration").([]interface{})) - requestUpdate = true + if v, ok := d.GetOk("serverlessv2_scaling_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + req.ServerlessV2ScalingConfiguration = expandServerlessV2ScalingConfiguration(v.([]interface{})[0].(map[string]interface{})) + requestUpdate = true + } } if d.HasChange("enable_http_endpoint") { diff --git a/internal/service/rds/flex.go b/internal/service/rds/flex.go index f76412df81b..b46c05bc01b 100644 --- a/internal/service/rds/flex.go +++ b/internal/service/rds/flex.go @@ -46,32 +46,40 @@ func flattenRDSScalingConfigurationInfo(scalingConfigurationInfo *rds.ScalingCon return []interface{}{m} } -func ExpandClusterServerlessV2ScalingConfiguration(l []interface{}) *rds.ServerlessV2ScalingConfiguration { - if len(l) == 0 || l[0] == nil { +func expandServerlessV2ScalingConfiguration(tfMap map[string]interface{}) *rds.ServerlessV2ScalingConfiguration { + if tfMap == nil { return nil } - m := l[0].(map[string]interface{}) + apiObject := &rds.ServerlessV2ScalingConfiguration{} - scalingConfiguration := &rds.ServerlessV2ScalingConfiguration{ - MaxCapacity: aws.Float64(float64(m["max_capacity"].(float64))), - MinCapacity: aws.Float64(float64(m["min_capacity"].(float64))), + if v, ok := tfMap["max_capacity"].(int); ok && v != 0.0 { + apiObject.MaxCapacity = aws.Float64(float64(v)) } - return scalingConfiguration + if v, ok := tfMap["min_capacity"].(int); ok && v != 0.0 { + apiObject.MinCapacity = aws.Float64(float64(v)) + } + + return apiObject } -func flattenRDSServerlessV2ScalingConfigurationInfo(scalingConfigurationInfo *rds.ServerlessV2ScalingConfigurationInfo) []interface{} { - if scalingConfigurationInfo == nil { - return []interface{}{} +func flattenServerlessV2ScalingConfigurationInfo(apiObject *rds.ServerlessV2ScalingConfigurationInfo) map[string]interface{} { + if apiObject == nil { + return nil } - m := map[string]interface{}{ - "max_capacity": aws.Float64Value(scalingConfigurationInfo.MaxCapacity), - "min_capacity": aws.Float64Value(scalingConfigurationInfo.MinCapacity), + tfMap := map[string]interface{}{} + + if v := apiObject.MaxCapacity; v != nil { + tfMap["max_capacity"] = aws.Float64Value(v) } - return []interface{}{m} + if v := apiObject.MinCapacity; v != nil { + tfMap["min_capacity"] = aws.Float64Value(v) + } + + return tfMap } func expandOptionConfiguration(configured []interface{}) []*rds.OptionConfiguration { diff --git a/internal/service/rds/flex_test.go b/internal/service/rds/flex_test.go index e7eb95bc7ce..dc787da503e 100644 --- a/internal/service/rds/flex_test.go +++ b/internal/service/rds/flex_test.go @@ -127,47 +127,3 @@ func TestExpandClusterScalingConfiguration_basic(t *testing.T) { } } } - -func TestExpandClusterServerlessV2ScalingConfiguration_basic(t *testing.T) { - type testCase struct { - EngineMode string - Input []interface{} - ExpectNil bool - } - cases := []testCase{} - - // RDS Cluster Scaling Configuration v2 is , but we're relying on AWS errors. - // If Terraform adds whole-resource validation, we can do our own validation at plan time. - for _, engineMode := range []string{"global", "multimaster", "parallelquery", "provisioned", "serverless"} { - cases = append(cases, []testCase{ - { - EngineMode: engineMode, - Input: []interface{}{ - map[string]interface{}{ - "max_capacity": 32.0, - "min_capacity": 4.0, - }, - }, - ExpectNil: false, - }, - { - EngineMode: engineMode, - Input: []interface{}{}, - ExpectNil: true, - }, { - EngineMode: engineMode, - Input: []interface{}{ - nil, - }, - ExpectNil: true, - }, - }...) - } - - for _, tc := range cases { - output := ExpandClusterServerlessV2ScalingConfiguration(tc.Input) - if tc.ExpectNil != (output == nil) { - t.Errorf("EngineMode %q: Expected nil: %t, Got: %v", tc.EngineMode, tc.ExpectNil, output) - } - } -} From 2bbf43249b7228dca26bbc712595309b358177eb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 12:12:45 -0400 Subject: [PATCH 06/14] Add and use 'EngineMode_Values()'. --- internal/service/rds/cluster.go | 16 +++++----------- internal/service/rds/consts.go | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index 3aa68e5605d..261b61d7b9a 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -171,17 +171,11 @@ func ResourceCluster() *schema.Resource { }, "engine_mode": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: "provisioned", - ValidateFunc: validation.StringInSlice([]string{ - "global", - "multimaster", - "parallelquery", - "provisioned", - "serverless", - }, false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: EngineModeProvisioned, + ValidateFunc: validation.StringInSlice(EngineMode_Values(), false), }, "engine_version": { diff --git a/internal/service/rds/consts.go b/internal/service/rds/consts.go index add45745ed4..864ae7ed733 100644 --- a/internal/service/rds/consts.go +++ b/internal/service/rds/consts.go @@ -50,6 +50,24 @@ const ( EventSubscriptionStatusModifying = "modifying" ) +const ( + EngineModeGlobal = "global" + EngineModeMultiMaster = "multimaster" + EngineModeParallelQuery = "parallelquery" + EngineModeProvisioned = "provisioned" + EngineModeServerless = "serverless" +) + +func EngineMode_Values() []string { + return []string{ + EngineModeGlobal, + EngineModeMultiMaster, + EngineModeParallelQuery, + EngineModeProvisioned, + EngineModeServerless, + } +} + const ( ExportableLogTypeAgent = "agent" ExportableLogTypeAlert = "alert" From 8481662ba9b01ae42e974240b23484c5a77a3325 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 12:13:41 -0400 Subject: [PATCH 07/14] Fix 'expandServerlessV2ScalingConfiguration'. --- internal/service/rds/flex.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/rds/flex.go b/internal/service/rds/flex.go index b46c05bc01b..708d4ca21c2 100644 --- a/internal/service/rds/flex.go +++ b/internal/service/rds/flex.go @@ -53,11 +53,11 @@ func expandServerlessV2ScalingConfiguration(tfMap map[string]interface{}) *rds.S apiObject := &rds.ServerlessV2ScalingConfiguration{} - if v, ok := tfMap["max_capacity"].(int); ok && v != 0.0 { + if v, ok := tfMap["max_capacity"].(float64); ok && v != 0.0 { apiObject.MaxCapacity = aws.Float64(float64(v)) } - if v, ok := tfMap["min_capacity"].(int); ok && v != 0.0 { + if v, ok := tfMap["min_capacity"].(float64); ok && v != 0.0 { apiObject.MinCapacity = aws.Float64(float64(v)) } From 3e6a85596425a4f1445f36289d93e962bbaa445a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 12:50:29 -0400 Subject: [PATCH 08/14] r/aws_rds_cluster: 'serverlessv2_scaling_configuration.max_capacity' and 'serverlessv2_scaling_configuration.min_capacity' are Required. Acceptance test output: % make testacc TESTS=TestAccRDSCluster_serverlessV2ScalingConfiguration PKG=rds ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/rds/... -v -count 1 -parallel 20 -run='TestAccRDSCluster_serverlessV2ScalingConfiguration' -timeout 180m === RUN TestAccRDSCluster_serverlessV2ScalingConfiguration === PAUSE TestAccRDSCluster_serverlessV2ScalingConfiguration === CONT TestAccRDSCluster_serverlessV2ScalingConfiguration --- PASS: TestAccRDSCluster_serverlessV2ScalingConfiguration (235.67s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/rds 245.624s --- internal/service/rds/cluster.go | 10 ++-- internal/service/rds/cluster_test.go | 61 ++++++------------------ website/docs/r/rds_cluster.html.markdown | 4 +- 3 files changed, 22 insertions(+), 53 deletions(-) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index 261b61d7b9a..abc20227234 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -238,12 +238,14 @@ func ResourceCluster() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "max_capacity": { - Type: schema.TypeFloat, - Optional: true, + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.FloatBetween(0.5, 128), }, "min_capacity": { - Type: schema.TypeFloat, - Optional: true, + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.FloatBetween(0.5, 128), }, }, }, diff --git a/internal/service/rds/cluster_test.go b/internal/service/rds/cluster_test.go index ea754f3997a..4c8308bfca8 100644 --- a/internal/service/rds/cluster_test.go +++ b/internal/service/rds/cluster_test.go @@ -1459,7 +1459,7 @@ func TestAccRDSCluster_scaling(t *testing.T) { }) } -func TestAccRDSCluster_scalingv2(t *testing.T) { +func TestAccRDSCluster_serverlessV2ScalingConfiguration(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") } @@ -1476,7 +1476,7 @@ func TestAccRDSCluster_scalingv2(t *testing.T) { CheckDestroy: testAccCheckClusterDestroy, Steps: []resource.TestStep{ { - Config: testAccClusterConfig_serverlessv2_ScalingConfiguration(rName, 64.0, 0.5), + Config: testAccClusterServerlessV2ScalingConfigurationConfig(rName, 64.0, 0.5), Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(resourceName, &dbCluster), resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.#", "1"), @@ -1485,7 +1485,7 @@ func TestAccRDSCluster_scalingv2(t *testing.T) { ), }, { - Config: testAccClusterConfig_serverlessv2_ScalingConfiguration(rName, 128.0, 8.5), + Config: testAccClusterServerlessV2ScalingConfigurationConfig(rName, 128.0, 8.5), Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(resourceName, &dbCluster), resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.#", "1"), @@ -1526,31 +1526,6 @@ func TestAccRDSCluster_Scaling_defaultMinCapacity(t *testing.T) { }) } -func TestAccRDSCluster_serverlessv2_Scaling_defaultMinCapacity(t *testing.T) { - var dbCluster rds.DBCluster - - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_rds_cluster.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), - Providers: acctest.Providers, - CheckDestroy: testAccCheckClusterDestroy, - Steps: []resource.TestStep{ - { - Config: testAccClusterConfig_serverlessv2_ScalingConfiguration_DefaultMinCapacity(rName, 128.0), - Check: resource.ComposeTestCheckFunc( - testAccCheckClusterExists(resourceName, &dbCluster), - resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.0.max_capacity", "128"), - resource.TestCheckResourceAttr(resourceName, "serverlessv2_scaling_configuration.0.min_capacity", "1"), - ), - }, - }, - }) -} - func TestAccRDSCluster_snapshotIdentifier(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -3862,17 +3837,24 @@ resource "aws_rds_cluster" "test" { `, rName, autoPause, maxCapacity, minCapacity, secondsUntilAutoPause, timeoutAction) } -func testAccClusterConfig_serverlessv2_ScalingConfiguration(rName string, maxCapacity float64, minCapacity float64) string { +func testAccClusterServerlessV2ScalingConfigurationConfig(rName string, maxCapacity, minCapacity float64) string { return fmt.Sprintf(` +data "aws_rds_engine_version" "test" { + engine = "aurora-postgresql" + preferred_versions = ["13.6"] +} + resource "aws_rds_cluster" "test" { - cluster_identifier = %q + cluster_identifier = %[1]q master_password = "barbarbarbar" master_username = "foo" skip_final_snapshot = true + engine = data.aws_rds_engine_version.test.engine + engine_version = data.aws_rds_engine_version.test.version serverlessv2_scaling_configuration { - max_capacity = %f - min_capacity = %f + max_capacity = %[2]f + min_capacity = %[3]f } } `, rName, maxCapacity, minCapacity) @@ -3897,21 +3879,6 @@ resource "aws_rds_cluster" "test" { `, rName, autoPause, maxCapacity, secondsUntilAutoPause, timeoutAction) } -func testAccClusterConfig_serverlessv2_ScalingConfiguration_DefaultMinCapacity(rName string, maxCapacity float64) string { - return fmt.Sprintf(` -resource "aws_rds_cluster" "test" { - cluster_identifier = %q - master_password = "barbarbarbar" - master_username = "foo" - skip_final_snapshot = true - - serverlessv2_scaling_configuration { - max_capacity = %f - } -} -`, rName, maxCapacity) -} - func testAccClusterConfig_SnapshotIdentifier(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster" "source" { diff --git a/website/docs/r/rds_cluster.html.markdown b/website/docs/r/rds_cluster.html.markdown index c944caea236..85e8a2057e1 100644 --- a/website/docs/r/rds_cluster.html.markdown +++ b/website/docs/r/rds_cluster.html.markdown @@ -264,8 +264,8 @@ resource "aws_rds_cluster" "example" { } ``` -* `max_capacity` - (Optional) The maximum capacity for an Aurora DB cluster in `serverlessv2` DB engine mode. The maximum capacity must be greater than or equal to the minimum capacity. Valid capacity values are in a range of `0.5` up to `128` in steps of `0.5`. -* `min_capacity` - (Optional) The minimum capacity for an Aurora DB cluster in `serverlessv2` DB engine mode. The minimum capacity must be lesser than or equal to the maximum capacity. Valid capacity values are in a range of `0.5` up to `128` in steps of `0.5`. Default: `1.0`. +* `max_capacity` - (Required) The maximum capacity for an Aurora DB cluster in `serverlessv2` DB engine mode. The maximum capacity must be greater than or equal to the minimum capacity. Valid capacity values are in a range of `0.5` up to `128` in steps of `0.5`. +* `min_capacity` - (Required) The minimum capacity for an Aurora DB cluster in `serverlessv2` DB engine mode. The minimum capacity must be lesser than or equal to the maximum capacity. Valid capacity values are in a range of `0.5` up to `128` in steps of `0.5`. ## Attributes Reference From 64ba146407c603fa494ed9af83a47c5150827a60 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 13:53:14 -0400 Subject: [PATCH 09/14] Remove 'TestAccRDSClusterInstance_AuroraServerlessV2'. --- internal/service/rds/cluster_instance_test.go | 135 ------------------ 1 file changed, 135 deletions(-) diff --git a/internal/service/rds/cluster_instance_test.go b/internal/service/rds/cluster_instance_test.go index 62f5f4d14d7..a2a18595f16 100644 --- a/internal/service/rds/cluster_instance_test.go +++ b/internal/service/rds/cluster_instance_test.go @@ -69,58 +69,6 @@ func TestAccRDSClusterInstance_basic(t *testing.T) { }) } -func TestAccRDSClusterInstance_AuroraServerlessV2(t *testing.T) { - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - - var v rds.DBInstance - resourceName := "aws_rds_cluster_instance.cluster_instances" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), - Providers: acctest.Providers, - CheckDestroy: testAccCheckClusterInstanceDestroy, - Steps: []resource.TestStep{ - { - Config: testAccClusterInstanceConfigAuroraServerlessV2(sdkacctest.RandInt()), - Check: resource.ComposeTestCheckFunc( - testAccCheckClusterInstanceExists(resourceName, &v), - testAccCheckClusterInstanceAttributes(&v), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(`db:.+`)), - resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "true"), - resource.TestCheckResourceAttr(resourceName, "copy_tags_to_snapshot", "false"), - resource.TestCheckResourceAttrSet(resourceName, "preferred_maintenance_window"), - resource.TestCheckResourceAttrSet(resourceName, "preferred_backup_window"), - resource.TestCheckResourceAttrSet(resourceName, "dbi_resource_id"), - resource.TestCheckResourceAttrSet(resourceName, "availability_zone"), - resource.TestCheckResourceAttrSet(resourceName, "engine_version"), - resource.TestCheckResourceAttr(resourceName, "engine", "aurora-mysql"), - resource.TestCheckResourceAttr(resourceName, "instance_class", "db.serverless"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{ - "apply_immediately", - "identifier_prefix", - }, - }, - { - Config: testAccClusterInstanceModifiedConfig(sdkacctest.RandInt()), - Check: resource.ComposeTestCheckFunc( - testAccCheckClusterInstanceExists(resourceName, &v), - testAccCheckClusterInstanceAttributes(&v), - resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), - ), - }, - }, - }) -} - func TestAccRDSClusterInstance_isAlreadyBeingDeleted(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -1237,89 +1185,6 @@ resource "aws_db_parameter_group" "bar" { `, n)) } -func testAccClusterInstanceConfigAuroraServerlessV2(n int) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -data "aws_rds_orderable_db_instance" "test" { - engine = "aurora-mysql" - preferred_instance_classes = ["db.serverless"] -} - -data "aws_rds_engine_version" "default" { - engine = data.aws_rds_orderable_db_instance.test.engine - version = data.aws_rds_orderable_db_instance.test.engine_version -} - -resource "aws_rds_cluster" "default" { - cluster_identifier = "tf-aurora-cluster-test-%[1]d" - availability_zones = [ - data.aws_availability_zones.available.names[0], - data.aws_availability_zones.available.names[1], - data.aws_availability_zones.available.names[2] - ] - serverlessv2_scaling_configuration { - max_capacity = 8.0 - } - engine = data.aws_rds_engine_version.default.engine - engine_version = data.aws_rds_engine_version.default.version - database_name = "mydb" - master_username = "foo" - master_password = "mustbeeightcharacters" - skip_final_snapshot = true -} - -resource "aws_rds_cluster_instance" "cluster_instances" { - identifier = "tf-cluster-instance-%[1]d" - cluster_identifier = aws_rds_cluster.default.id - engine = data.aws_rds_engine_version.default.engine - engine_version = data.aws_rds_engine_version.default.version - instance_class = data.aws_rds_orderable_db_instance.test.instance_class - db_parameter_group_name = "default.${data.aws_rds_engine_version.default.parameter_group_family}" - promotion_tier = "3" -} -`, n)) -} - -func testAccClusterInstanceModifiedConfigAuroraServerlessV2(n int) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptInExclude("af-south-1", "ap-northeast-3", "me-south-1"), fmt.Sprintf(` -data "aws_rds_orderable_db_instance" "test" { - engine = "aurora-mysql" - preferred_instance_classes = ["db.serverless"] -} - -data "aws_rds_engine_version" "default" { - engine = data.aws_rds_orderable_db_instance.test.engine - version = data.aws_rds_orderable_db_instance.test.engine_version -} - -resource "aws_rds_cluster" "default" { - cluster_identifier = "tf-aurora-cluster-test-%[1]d" - availability_zones = [ - data.aws_availability_zones.available.names[0], - data.aws_availability_zones.available.names[1], - data.aws_availability_zones.available.names[2] - ] - serverlessv2_scaling_configuration { - max_capacity = 8.0 - } - database_name = "mydb" - master_username = "foo" - master_password = "mustbeeightcharacters" - skip_final_snapshot = true -} - -resource "aws_rds_cluster_instance" "cluster_instances" { - identifier = "tf-cluster-instance-%[1]d" - cluster_identifier = aws_rds_cluster.default.id - engine = data.aws_rds_engine_version.default.engine - engine_version = data.aws_rds_engine_version.default.version - instance_class = data.aws_rds_orderable_db_instance.test.instance_class - db_parameter_group_name = "default.${data.aws_rds_engine_version.default.parameter_group_family}" - promotion_tier = "3" - auto_minor_version_upgrade = false -} -`, n)) -} - func testAccClusterInstanceConfig_az(n int) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_rds_cluster" "default" { From 6a4d5d119929d55dacde1ee700223dafd88ca8a3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 13:56:56 -0400 Subject: [PATCH 10/14] Fix terrafmt error. --- website/docs/r/rds_cluster.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/rds_cluster.html.markdown b/website/docs/r/rds_cluster.html.markdown index 85e8a2057e1..20b86cf795b 100644 --- a/website/docs/r/rds_cluster.html.markdown +++ b/website/docs/r/rds_cluster.html.markdown @@ -258,8 +258,8 @@ resource "aws_rds_cluster" "example" { # ... other configuration ... serverlessv2_scaling_configuration { - max_capacity = 128.0 - min_capacity = 0.5 + max_capacity = 128.0 + min_capacity = 0.5 } } ``` From 26c8c9c217d878b666f32edc7df80db929bf5074 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:11:00 -0400 Subject: [PATCH 11/14] r/aws_rds_cluster: Alphabetize attributes. --- internal/service/rds/cluster.go | 493 ++++++++++++++------------------ 1 file changed, 219 insertions(+), 274 deletions(-) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index abc20227234..60db6068a15 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -34,6 +34,7 @@ func ResourceCluster() *schema.Resource { Read: resourceClusterRead, Update: resourceClusterUpdate, Delete: resourceClusterDelete, + Importer: &schema.ResourceImporter{ State: resourceClusterImport, }, @@ -45,131 +46,139 @@ func ResourceCluster() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, + "allocated_storage": { + Type: schema.TypeInt, + Optional: true, Computed: true, }, - "allow_major_version_upgrade": { Type: schema.TypeBool, Optional: true, }, - + // apply_immediately is used to determine when the update modifications take place. + // See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html + "apply_immediately": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, "availability_zones": { Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, ForceNew: true, Computed: true, - Set: schema.HashString, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "backup_retention_period": { + Type: schema.TypeInt, + Optional: true, + Default: 1, + ValidateFunc: validation.IntAtMost(35), }, - "backtrack_window": { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntBetween(0, 259200), }, - "cluster_identifier": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, - ConflictsWith: []string{"cluster_identifier_prefix"}, ValidateFunc: validIdentifier, + ConflictsWith: []string{"cluster_identifier_prefix"}, }, "cluster_identifier_prefix": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, - ConflictsWith: []string{"cluster_identifier"}, ValidateFunc: validIdentifierPrefix, + ConflictsWith: []string{"cluster_identifier"}, }, - "cluster_members": { Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, Computed: true, - Set: schema.HashString, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "cluster_resource_id": { + Type: schema.TypeString, + Computed: true, }, - "copy_tags_to_snapshot": { Type: schema.TypeBool, Optional: true, Default: false, }, - "database_name": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, - - "db_subnet_group_name": { + "db_cluster_instance_class": { Type: schema.TypeString, Optional: true, - ForceNew: true, - Computed: true, }, - "db_cluster_parameter_group_name": { Type: schema.TypeString, Optional: true, Computed: true, }, - "db_instance_parameter_group_name": { Type: schema.TypeString, Optional: true, }, - - "deletion_protection": { - Type: schema.TypeBool, - Optional: true, - }, - - "endpoint": { + "db_subnet_group_name": { Type: schema.TypeString, + Optional: true, + ForceNew: true, Computed: true, }, - - "global_cluster_identifier": { - Type: schema.TypeString, + "deletion_protection": { + Type: schema.TypeBool, Optional: true, }, - "enable_global_write_forwarding": { Type: schema.TypeBool, Optional: true, Default: false, }, - - "reader_endpoint": { - Type: schema.TypeString, - Computed: true, + "enabled_cloudwatch_logs_exports": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "audit", + "error", + "general", + "slowquery", + "postgresql", + }, false), + }, }, - - "hosted_zone_id": { - Type: schema.TypeString, - Computed: true, + "enable_http_endpoint": { + Type: schema.TypeBool, + Optional: true, + Default: false, }, - - "db_cluster_instance_class": { + "endpoint": { Type: schema.TypeString, - Optional: true, + Computed: true, }, - "engine": { Type: schema.TypeString, Optional: true, - Default: "aurora", ForceNew: true, + Default: "aurora", ValidateFunc: validEngine(), }, - "engine_mode": { Type: schema.TypeString, Optional: true, @@ -177,125 +186,118 @@ func ResourceCluster() *schema.Resource { Default: EngineModeProvisioned, ValidateFunc: validation.StringInSlice(EngineMode_Values(), false), }, - "engine_version": { Type: schema.TypeString, Optional: true, Computed: true, }, - "engine_version_actual": { Type: schema.TypeString, Computed: true, }, - - "scaling_configuration": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "auto_pause": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "max_capacity": { - Type: schema.TypeInt, - Optional: true, - Default: clusterScalingConfiguration_DefaultMaxCapacity, - }, - "min_capacity": { - Type: schema.TypeInt, - Optional: true, - Default: clusterScalingConfiguration_DefaultMinCapacity, - }, - "seconds_until_auto_pause": { - Type: schema.TypeInt, - Optional: true, - Default: 300, - ValidateFunc: validation.IntBetween(300, 86400), - }, - "timeout_action": { - Type: schema.TypeString, - Optional: true, - Default: "RollbackCapacityChange", - ValidateFunc: validation.StringInSlice([]string{ - "ForceApplyCapacityChange", - "RollbackCapacityChange", - }, false), - }, - }, + "final_snapshot_identifier": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(string) + if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { + es = append(es, fmt.Errorf( + "only alphanumeric characters and hyphens allowed in %q", k)) + } + if regexp.MustCompile(`--`).MatchString(value) { + es = append(es, fmt.Errorf("%q cannot contain two consecutive hyphens", k)) + } + if regexp.MustCompile(`-$`).MatchString(value) { + es = append(es, fmt.Errorf("%q cannot end in a hyphen", k)) + } + return }, }, - - "serverlessv2_scaling_configuration": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "max_capacity": { - Type: schema.TypeFloat, - Required: true, - ValidateFunc: validation.FloatBetween(0.5, 128), - }, - "min_capacity": { - Type: schema.TypeFloat, - Required: true, - ValidateFunc: validation.FloatBetween(0.5, 128), - }, - }, - }, + "global_cluster_identifier": { + Type: schema.TypeString, + Optional: true, }, - - "allocated_storage": { - Type: schema.TypeInt, + "hosted_zone_id": { + Type: schema.TypeString, + Computed: true, + }, + "iam_database_authentication_enabled": { + Type: schema.TypeBool, + Optional: true, + }, + "iam_roles": { + Type: schema.TypeSet, Optional: true, Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, }, - - "storage_type": { + "iops": { + Type: schema.TypeInt, + Optional: true, + }, + "kms_key_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: verify.ValidARN, + }, + "master_password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "master_username": { Type: schema.TypeString, + Computed: true, Optional: true, ForceNew: true, }, - - "iops": { + "port": { Type: schema.TypeInt, Optional: true, + Computed: true, }, - - "storage_encrypted": { - Type: schema.TypeBool, + "preferred_backup_window": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: verify.ValidOnceADayWindowFormat, + }, + "preferred_maintenance_window": { + Type: schema.TypeString, Optional: true, Computed: true, - ForceNew: true, + StateFunc: func(val interface{}) string { + if val == nil { + return "" + } + return strings.ToLower(val.(string)) + }, + ValidateFunc: verify.ValidOnceAWeekWindowFormat, + }, + "reader_endpoint": { + Type: schema.TypeString, + Computed: true, + }, + "replication_source_identifier": { + Type: schema.TypeString, + Optional: true, }, - "restore_to_point_in_time": { Type: schema.TypeList, Optional: true, ForceNew: true, MaxItems: 1, - ConflictsWith: []string{ - "s3_import", - "snapshot_identifier", - }, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "source_cluster_identifier": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.Any( - verify.ValidARN, - validIdentifier, - ), + "restore_to_time": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: verify.ValidUTCTimestamp, + ConflictsWith: []string{"restore_to_point_in_time.0.use_latest_restorable_time"}, }, - "restore_type": { Type: schema.TypeString, Optional: true, @@ -305,33 +307,32 @@ func ResourceCluster() *schema.Resource { "copy-on-write", }, false), }, - + "source_cluster_identifier": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.Any( + verify.ValidARN, + validIdentifier, + ), + }, "use_latest_restorable_time": { Type: schema.TypeBool, Optional: true, ForceNew: true, ConflictsWith: []string{"restore_to_point_in_time.0.restore_to_time"}, }, - - "restore_to_time": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: verify.ValidUTCTimestamp, - ConflictsWith: []string{"restore_to_point_in_time.0.use_latest_restorable_time"}, - }, }, }, + ConflictsWith: []string{ + "s3_import", + "snapshot_identifier", + }, }, - "s3_import": { Type: schema.TypeList, Optional: true, MaxItems: 1, - ConflictsWith: []string{ - "snapshot_identifier", - "restore_to_point_in_time", - }, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "bucket_name": { @@ -361,160 +362,104 @@ func ResourceCluster() *schema.Resource { }, }, }, + ConflictsWith: []string{ + "snapshot_identifier", + "restore_to_point_in_time", + }, }, - - "final_snapshot_identifier": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - value := v.(string) - if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { - es = append(es, fmt.Errorf( - "only alphanumeric characters and hyphens allowed in %q", k)) - } - if regexp.MustCompile(`--`).MatchString(value) { - es = append(es, fmt.Errorf("%q cannot contain two consecutive hyphens", k)) - } - if regexp.MustCompile(`-$`).MatchString(value) { - es = append(es, fmt.Errorf("%q cannot end in a hyphen", k)) - } - return + "scaling_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auto_pause": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "max_capacity": { + Type: schema.TypeInt, + Optional: true, + Default: clusterScalingConfiguration_DefaultMaxCapacity, + }, + "min_capacity": { + Type: schema.TypeInt, + Optional: true, + Default: clusterScalingConfiguration_DefaultMinCapacity, + }, + "seconds_until_auto_pause": { + Type: schema.TypeInt, + Optional: true, + Default: 300, + ValidateFunc: validation.IntBetween(300, 86400), + }, + "timeout_action": { + Type: schema.TypeString, + Optional: true, + Default: "RollbackCapacityChange", + ValidateFunc: validation.StringInSlice([]string{ + "ForceApplyCapacityChange", + "RollbackCapacityChange", + }, false), + }, + }, + }, + }, + "serverlessv2_scaling_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_capacity": { + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.FloatBetween(0.5, 128), + }, + "min_capacity": { + Type: schema.TypeFloat, + Required: true, + ValidateFunc: validation.FloatBetween(0.5, 128), + }, + }, }, }, - "skip_final_snapshot": { Type: schema.TypeBool, Optional: true, Default: false, }, - - "master_username": { - Type: schema.TypeString, - Computed: true, - Optional: true, - ForceNew: true, - }, - - "master_password": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "snapshot_identifier": { Type: schema.TypeString, Optional: true, }, - - "port": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - }, - - // apply_immediately is used to determine when the update modifications - // take place. - // See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html - "apply_immediately": { - Type: schema.TypeBool, - Optional: true, - Computed: true, - }, - - "vpc_security_group_ids": { - Type: schema.TypeSet, - Optional: true, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, - - "preferred_backup_window": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: verify.ValidOnceADayWindowFormat, - }, - - "preferred_maintenance_window": { - Type: schema.TypeString, - Optional: true, - Computed: true, - StateFunc: func(val interface{}) string { - if val == nil { - return "" - } - return strings.ToLower(val.(string)) - }, - ValidateFunc: verify.ValidOnceAWeekWindowFormat, - }, - - "backup_retention_period": { - Type: schema.TypeInt, - Optional: true, - Default: 1, - ValidateFunc: validation.IntAtMost(35), - }, - - "kms_key_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: verify.ValidARN, - }, - - "replication_source_identifier": { + "source_region": { Type: schema.TypeString, Optional: true, + ForceNew: true, }, - - "iam_roles": { - Type: schema.TypeSet, - Optional: true, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, - - "iam_database_authentication_enabled": { + "storage_encrypted": { Type: schema.TypeBool, Optional: true, - }, - - "cluster_resource_id": { - Type: schema.TypeString, Computed: true, + ForceNew: true, }, - - "source_region": { + "storage_type": { Type: schema.TypeString, Optional: true, ForceNew: true, }, - - "enabled_cloudwatch_logs_exports": { + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), + "vpc_security_group_ids": { Type: schema.TypeSet, Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{ - "audit", - "error", - "general", - "slowquery", - "postgresql", - }, false), - }, - }, - "enable_http_endpoint": { - Type: schema.TypeBool, - Optional: true, - Default: false, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, }, - - "tags": tftags.TagsSchema(), - "tags_all": tftags.TagsSchemaComputed(), }, CustomizeDiff: verify.SetTagsDiff, From 472c129cad6f3282cc02723748f32976c836d1f1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:24:38 -0400 Subject: [PATCH 12/14] Add '_Values()' function to 'consts.go'. --- internal/service/rds/cluster.go | 34 +++++++------------- internal/service/rds/consts.go | 54 +++++++++++++++++++++++++++++++- internal/service/rds/instance.go | 2 +- internal/service/rds/validate.go | 8 +---- 4 files changed, 66 insertions(+), 32 deletions(-) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index 60db6068a15..7a0bd96cfd2 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -153,14 +153,8 @@ func ResourceCluster() *schema.Resource { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{ - "audit", - "error", - "general", - "slowquery", - "postgresql", - }, false), + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice(ClusterExportableLogType_Values(), false), }, }, "enable_http_endpoint": { @@ -176,7 +170,7 @@ func ResourceCluster() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Default: "aurora", + Default: EngineAurora, ValidateFunc: validEngine(), }, "engine_mode": { @@ -299,13 +293,10 @@ func ResourceCluster() *schema.Resource { ConflictsWith: []string{"restore_to_point_in_time.0.use_latest_restorable_time"}, }, "restore_type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - "full-copy", - "copy-on-write", - }, false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(RestoreType_Values(), false), }, "source_cluster_identifier": { Type: schema.TypeString, @@ -396,13 +387,10 @@ func ResourceCluster() *schema.Resource { ValidateFunc: validation.IntBetween(300, 86400), }, "timeout_action": { - Type: schema.TypeString, - Optional: true, - Default: "RollbackCapacityChange", - ValidateFunc: validation.StringInSlice([]string{ - "ForceApplyCapacityChange", - "RollbackCapacityChange", - }, false), + Type: schema.TypeString, + Optional: true, + Default: TimeoutActionRollbackCapacityChange, + ValidateFunc: validation.StringInSlice(TimeoutAction_Values(), false), }, }, }, diff --git a/internal/service/rds/consts.go b/internal/service/rds/consts.go index 864ae7ed733..3f60848c6d6 100644 --- a/internal/service/rds/consts.go +++ b/internal/service/rds/consts.go @@ -50,6 +50,24 @@ const ( EventSubscriptionStatusModifying = "modifying" ) +const ( + EngineAurora = "aurora" + EngineAuroraMySQL = "aurora-mysql" + EngineAuroraPostgreSQL = "aurora-postgresql" + EngineMySQL = "mysql" + EnginePostgres = "postgres" +) + +func Engine_Values() []string { + return []string{ + EngineAurora, + EngineAuroraMySQL, + EngineAuroraPostgreSQL, + EngineMySQL, + EnginePostgres, + } +} + const ( EngineModeGlobal = "global" EngineModeMultiMaster = "multimaster" @@ -82,7 +100,17 @@ const ( ExportableLogTypeUpgrade = "upgrade" ) -func ExportableLogType_Values() []string { +func ClusterExportableLogType_Values() []string { + return []string{ + ExportableLogTypeAudit, + ExportableLogTypeError, + ExportableLogTypeGeneral, + ExportableLogTypePostgreSQL, + ExportableLogTypeSlowQuery, + } +} + +func InstanceExportableLogType_Values() []string { return []string{ ExportableLogTypeAgent, ExportableLogTypeAlert, @@ -97,3 +125,27 @@ func ExportableLogType_Values() []string { ExportableLogTypeUpgrade, } } + +const ( + RestoreTypeCopyOnWrite = "copy-on-write" + RestoreTypeFullCopy = "full-copy" +) + +func RestoreType_Values() []string { + return []string{ + RestoreTypeCopyOnWrite, + RestoreTypeFullCopy, + } +} + +const ( + TimeoutActionForceApplyCapacityChange = "ForceApplyCapacityChange" + TimeoutActionRollbackCapacityChange = "RollbackCapacityChange" +) + +func TimeoutAction_Values() []string { + return []string{ + TimeoutActionForceApplyCapacityChange, + TimeoutActionRollbackCapacityChange, + } +} diff --git a/internal/service/rds/instance.go b/internal/service/rds/instance.go index 99ed536cdab..609eed845c4 100644 --- a/internal/service/rds/instance.go +++ b/internal/service/rds/instance.go @@ -176,7 +176,7 @@ func ResourceInstance() *schema.Resource { Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validation.StringInSlice(ExportableLogType_Values(), false), + ValidateFunc: validation.StringInSlice(InstanceExportableLogType_Values(), false), }, }, "endpoint": { diff --git a/internal/service/rds/validate.go b/internal/service/rds/validate.go index 6e3da6ad67e..bb597866e3b 100644 --- a/internal/service/rds/validate.go +++ b/internal/service/rds/validate.go @@ -144,13 +144,7 @@ func validSubnetGroupNamePrefix(v interface{}, k string) (ws []string, errors [] } func validEngine() schema.SchemaValidateFunc { - return validation.StringInSlice([]string{ - "aurora", - "aurora-mysql", - "aurora-postgresql", - "postgres", - "mysql", - }, false) + return validation.StringInSlice(Engine_Values(), false) } func validIdentifier(v interface{}, k string) (ws []string, errors []error) { From a71db5f664b05aab2208ed13639ebc537d463037 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 14:28:45 -0400 Subject: [PATCH 13/14] r/aws_ds_cluster: USe 'FindDBClusterByID'. --- internal/service/rds/cluster.go | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/internal/service/rds/cluster.go b/internal/service/rds/cluster.go index 7a0bd96cfd2..a142cde3349 100644 --- a/internal/service/rds/cluster.go +++ b/internal/service/rds/cluster.go @@ -1001,39 +1001,16 @@ func resourceClusterRead(d *schema.ResourceData, meta interface{}) error { defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - input := &rds.DescribeDBClustersInput{ - DBClusterIdentifier: aws.String(d.Id()), - } + dbc, err := FindDBClusterByID(conn, d.Id()) - log.Printf("[DEBUG] Describing RDS Cluster: %s", input) - resp, err := conn.DescribeDBClusters(input) - - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterNotFoundFault) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS Cluster (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error describing RDS Cluster (%s): %s", d.Id(), err) - } - - if resp == nil { - return fmt.Errorf("Error retrieving RDS cluster: empty response for: %s", input) - } - - var dbc *rds.DBCluster - for _, c := range resp.DBClusters { - if aws.StringValue(c.DBClusterIdentifier) == d.Id() { - dbc = c - break - } - } - - if dbc == nil { - log.Printf("[WARN] RDS Cluster (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil + return fmt.Errorf("error reading RDS Cluster (%s): %w", d.Id(), err) } if err := d.Set("availability_zones", aws.StringValueSlice(dbc.AvailabilityZones)); err != nil { From dcda4bb7712a9442124bccd3d94f95a42e8aed2d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Apr 2022 18:00:36 -0400 Subject: [PATCH 14/14] Fix golangci-lint error 'unnecessary conversion (unconvert)'. --- docs/contributing/data-handling-and-conversion.md | 2 +- internal/service/rds/flex.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contributing/data-handling-and-conversion.md b/docs/contributing/data-handling-and-conversion.md index 3ecde06eb76..a8d2d410483 100644 --- a/docs/contributing/data-handling-and-conversion.md +++ b/docs/contributing/data-handling-and-conversion.md @@ -530,7 +530,7 @@ func expandStructure(tfMap map[string]interface{}) *service.Structure { // ... if v, ok := tfMap["nested_attribute_name"].(float64); ok && v != 0.0 { - apiObject.NestedAttributeName = aws.Float64(float64(v)) + apiObject.NestedAttributeName = aws.Float64(v) } // ... diff --git a/internal/service/rds/flex.go b/internal/service/rds/flex.go index 708d4ca21c2..96ff7efbfc0 100644 --- a/internal/service/rds/flex.go +++ b/internal/service/rds/flex.go @@ -54,11 +54,11 @@ func expandServerlessV2ScalingConfiguration(tfMap map[string]interface{}) *rds.S apiObject := &rds.ServerlessV2ScalingConfiguration{} if v, ok := tfMap["max_capacity"].(float64); ok && v != 0.0 { - apiObject.MaxCapacity = aws.Float64(float64(v)) + apiObject.MaxCapacity = aws.Float64(v) } if v, ok := tfMap["min_capacity"].(float64); ok && v != 0.0 { - apiObject.MinCapacity = aws.Float64(float64(v)) + apiObject.MinCapacity = aws.Float64(v) } return apiObject