From 5b7890e7d5879baff72218ab31d04a17e8390152 Mon Sep 17 00:00:00 2001 From: Evan Cleary Date: Tue, 20 Aug 2024 09:19:33 -0400 Subject: [PATCH 1/2] r/aws_appautoscaling_target: Add suspended_state argument --- .changelog/38942.txt | 3 + internal/service/appautoscaling/target.go | 79 +++++++- .../service/appautoscaling/target_test.go | 169 ++++++++++++++++++ .../r/appautoscaling_target.html.markdown | 9 + 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 .changelog/38942.txt diff --git a/.changelog/38942.txt b/.changelog/38942.txt new file mode 100644 index 000000000000..12002d0b3476 --- /dev/null +++ b/.changelog/38942.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_appautoscaling_target: Add `suspended_state` argument +``` diff --git a/internal/service/appautoscaling/target.go b/internal/service/appautoscaling/target.go index 16aad0070b2c..70f477d88177 100644 --- a/internal/service/appautoscaling/target.go +++ b/internal/service/appautoscaling/target.go @@ -74,6 +74,31 @@ func resourceTarget() *schema.Resource { Required: true, ForceNew: true, }, + "suspended_state": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dynamic_scaling_in_suspended": { + Type: schema.TypeBool, + Default: false, + Optional: true, + }, + "dynamic_scaling_out_suspended": { + Type: schema.TypeBool, + Default: false, + Optional: true, + }, + "scheduled_scaling_suspended": { + Type: schema.TypeBool, + Default: false, + Optional: true, + }, + }, + }, + }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), }, @@ -100,6 +125,10 @@ func resourceTargetCreate(ctx context.Context, d *schema.ResourceData, meta inte input.RoleARN = aws.String(v.(string)) } + if v, ok := d.GetOk("suspended_state"); ok { + input.SuspendedState = expandSuspendedState(v.([]interface{})) + } + err := registerScalableTarget(ctx, conn, input) if err != nil { @@ -141,7 +170,9 @@ func resourceTargetRead(ctx context.Context, d *schema.ResourceData, meta interf d.Set(names.AttrRoleARN, t.RoleARN) d.Set("scalable_dimension", t.ScalableDimension) d.Set("service_namespace", t.ServiceNamespace) - + if err := d.Set("suspended_state", flattenSuspendedState(t.SuspendedState)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting suspended_state: %s", err) + } return diags } @@ -162,6 +193,10 @@ func resourceTargetUpdate(ctx context.Context, d *schema.ResourceData, meta inte input.RoleARN = aws.String(v.(string)) } + if v, ok := d.GetOk("suspended_state"); ok { + input.SuspendedState = expandSuspendedState(v.([]interface{})) + } + err := registerScalableTarget(ctx, conn, input) if err != nil { @@ -282,3 +317,45 @@ func registerScalableTarget(ctx context.Context, conn *applicationautoscaling.Cl return err } + +func expandSuspendedState(cfg []interface{}) *awstypes.SuspendedState { + if len(cfg) == 0 || cfg[0] == nil { + return nil + } + + out := &awstypes.SuspendedState{} + + m := cfg[0].(map[string]interface{}) + + if v, ok := m["dynamic_scaling_in_suspended"]; ok { + out.DynamicScalingInSuspended = aws.Bool(v.(bool)) + } + if v, ok := m["dynamic_scaling_out_suspended"]; ok { + out.DynamicScalingOutSuspended = aws.Bool(v.(bool)) + } + if v, ok := m["scheduled_scaling_suspended"]; ok { + out.ScheduledScalingSuspended = aws.Bool(v.(bool)) + } + + return out +} + +func flattenSuspendedState(cfg *awstypes.SuspendedState) []interface{} { + if cfg == nil { + return []interface{}{} + } + + m := make(map[string]interface{}) + + if v := cfg.DynamicScalingInSuspended; v != nil { + m["dynamic_scaling_in_suspended"] = aws.ToBool(v) + } + if v := cfg.DynamicScalingOutSuspended; v != nil { + m["dynamic_scaling_out_suspended"] = aws.ToBool(v) + } + if v := cfg.ScheduledScalingSuspended; v != nil { + m["scheduled_scaling_suspended"] = aws.ToBool(v) + } + + return []interface{}{m} +} diff --git a/internal/service/appautoscaling/target_test.go b/internal/service/appautoscaling/target_test.go index 624d94c4f23d..79b3688cd099 100644 --- a/internal/service/appautoscaling/target_test.go +++ b/internal/service/appautoscaling/target_test.go @@ -208,6 +208,95 @@ func TestAccAppAutoScalingTarget_optionalRoleARN(t *testing.T) { }) } +func TestAccAppAutoScalingTarget_suspendedState(t *testing.T) { + ctx := acctest.Context(t) + var readTarget awstypes.ScalableTarget + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_appautoscaling_target.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.AppAutoScalingServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckTargetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccTargetConfig_suspendedState(rName, true, true, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckTargetExists(ctx, resourceName, &readTarget), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_in_suspended", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_out_suspended", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.scheduled_scaling_suspended", acctest.CtTrue), + ), + }, + { + Config: testAccTargetConfig_suspendedStateDefault(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTargetExists(ctx, resourceName, &readTarget), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_in_suspended", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_out_suspended", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.scheduled_scaling_suspended", acctest.CtFalse), + ), + }, + }, + }) +} + +func TestAccAppAutoScalingTarget_suspendedState_maintainsExisting(t *testing.T) { + ctx := acctest.Context(t) + var readTarget awstypes.ScalableTarget + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_appautoscaling_target.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.AppAutoScalingServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckTargetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccTargetConfig_suspendedState(rName, false, false, true), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccTargetImportStateIdFunc(resourceName), + }, + { + Config: testAccTargetConfig_sansSuspendedState(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTargetExists(ctx, resourceName, &readTarget), + resource.TestCheckResourceAttr(resourceName, "suspended_state.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_in_suspended", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_out_suspended", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.scheduled_scaling_suspended", acctest.CtTrue), + ), + }, + { + Config: testAccTargetConfig_suspendedState(rName, false, true, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckTargetExists(ctx, resourceName, &readTarget), + resource.TestCheckResourceAttr(resourceName, "suspended_state.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_in_suspended", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_out_suspended", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.scheduled_scaling_suspended", acctest.CtFalse), + ), + }, + { + Config: testAccTargetConfig_sansSuspendedState(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTargetExists(ctx, resourceName, &readTarget), + resource.TestCheckResourceAttr(resourceName, "suspended_state.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_in_suspended", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.dynamic_scaling_out_suspended", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "suspended_state.0.scheduled_scaling_suspended", acctest.CtFalse), + ), + }, + }, + }) +} + func testAccCheckTargetDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).AppAutoScalingClient(ctx) @@ -695,6 +784,86 @@ resource "aws_appautoscaling_target" "test" { `, rName) } +func testAccTargetConfig_suspendedState(rName string, dsis, dsos, sss bool) string { + return fmt.Sprintf(` +resource "aws_dynamodb_table" "test" { + name = %[1]q + read_capacity = 5 + write_capacity = 5 + hash_key = "TestKey" + + attribute { + name = "TestKey" + type = "S" + } +} + +resource "aws_appautoscaling_target" "test" { + service_namespace = "dynamodb" + resource_id = "table/${aws_dynamodb_table.test.name}" + scalable_dimension = "dynamodb:table:ReadCapacityUnits" + min_capacity = 2 + max_capacity = 15 + + suspended_state { + dynamic_scaling_in_suspended = %[2]t + dynamic_scaling_out_suspended = %[3]t + scheduled_scaling_suspended = %[4]t + } +} +`, rName, dsis, dsos, sss) +} + +func testAccTargetConfig_suspendedStateDefault(rName string) string { + return fmt.Sprintf(` +resource "aws_dynamodb_table" "test" { + name = %[1]q + read_capacity = 5 + write_capacity = 5 + hash_key = "TestKey" + + attribute { + name = "TestKey" + type = "S" + } +} + +resource "aws_appautoscaling_target" "test" { + service_namespace = "dynamodb" + resource_id = "table/${aws_dynamodb_table.test.name}" + scalable_dimension = "dynamodb:table:ReadCapacityUnits" + min_capacity = 2 + max_capacity = 15 + + suspended_state {} +} +`, rName) +} + +func testAccTargetConfig_sansSuspendedState(rName string) string { + return fmt.Sprintf(` +resource "aws_dynamodb_table" "test" { + name = %[1]q + read_capacity = 5 + write_capacity = 5 + hash_key = "TestKey" + + attribute { + name = "TestKey" + type = "S" + } +} + +resource "aws_appautoscaling_target" "test" { + service_namespace = "dynamodb" + resource_id = "table/${aws_dynamodb_table.test.name}" + scalable_dimension = "dynamodb:table:ReadCapacityUnits" + min_capacity = 2 + max_capacity = 15 +} +`, rName) +} + func testAccTargetImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { rs, ok := s.RootModule().Resources[resourceName] diff --git a/website/docs/r/appautoscaling_target.html.markdown b/website/docs/r/appautoscaling_target.html.markdown index c8899c14fe2c..18b00b2d4a9f 100644 --- a/website/docs/r/appautoscaling_target.html.markdown +++ b/website/docs/r/appautoscaling_target.html.markdown @@ -92,8 +92,17 @@ This resource supports the following arguments: * `role_arn` - (Optional) ARN of the IAM role that allows Application AutoScaling to modify your scalable target on your behalf. This defaults to an IAM Service-Linked Role for most services and custom IAM Roles are ignored by the API for those namespaces. See the [AWS Application Auto Scaling documentation](https://docs.aws.amazon.com/autoscaling/application/userguide/security_iam_service-with-iam.html#security_iam_service-with-iam-roles) for more information about how this service interacts with IAM. * `scalable_dimension` - (Required) Scalable dimension of the scalable target. Documentation can be found in the `ScalableDimension` parameter at: [AWS Application Auto Scaling API Reference](https://docs.aws.amazon.com/autoscaling/application/APIReference/API_RegisterScalableTarget.html#API_RegisterScalableTarget_RequestParameters) * `service_namespace` - (Required) AWS service namespace of the scalable target. Documentation can be found in the `ServiceNamespace` parameter at: [AWS Application Auto Scaling API Reference](https://docs.aws.amazon.com/autoscaling/application/APIReference/API_RegisterScalableTarget.html#API_RegisterScalableTarget_RequestParameters) +* `suspended_state` - (Optional) Specifies whether the scaling activities for a scalable target are in a suspended state. * `tags` - (Optional) Map of tags to assign to the scalable target. 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. +### suspended_state + +The `suspended_state` configuration block supports the following arguments: + +* `dynamic_scaling_in_suspended` (Optional) Whether scale in by a target tracking scaling policy or a step scaling policy is suspended. Default is `false`. +* `dynamic_scaling_out_suspended` (Optional) Whether scale out by a target tracking scaling policy or a step scaling policy is suspended. Default is `false`. +* `scheduled_scaling_suspended` (Optional) Whether scheduled scaling is suspended. Default is `false`. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: From 9d4ff2c9145d80a9d7a300f8e93af291e1d6419f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 21 Aug 2024 10:36:25 -0400 Subject: [PATCH 2/2] Cosmetics. --- .changelog/38942.txt | 2 +- internal/service/appautoscaling/target.go | 44 +++++++++++------------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.changelog/38942.txt b/.changelog/38942.txt index 12002d0b3476..c31ef201d457 100644 --- a/.changelog/38942.txt +++ b/.changelog/38942.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_appautoscaling_target: Add `suspended_state` argument +resource/aws_appautoscaling_target: Add `suspended_state` configuration block ``` diff --git a/internal/service/appautoscaling/target.go b/internal/service/appautoscaling/target.go index 70f477d88177..69095e7786d7 100644 --- a/internal/service/appautoscaling/target.go +++ b/internal/service/appautoscaling/target.go @@ -173,6 +173,7 @@ func resourceTargetRead(ctx context.Context, d *schema.ResourceData, meta interf if err := d.Set("suspended_state", flattenSuspendedState(t.SuspendedState)); err != nil { return sdkdiag.AppendErrorf(diags, "setting suspended_state: %s", err) } + return diags } @@ -318,44 +319,43 @@ func registerScalableTarget(ctx context.Context, conn *applicationautoscaling.Cl return err } -func expandSuspendedState(cfg []interface{}) *awstypes.SuspendedState { - if len(cfg) == 0 || cfg[0] == nil { +func expandSuspendedState(tfList []interface{}) *awstypes.SuspendedState { + if len(tfList) == 0 || tfList[0] == nil { return nil } - out := &awstypes.SuspendedState{} - - m := cfg[0].(map[string]interface{}) + apiObject := &awstypes.SuspendedState{} + tfMap := tfList[0].(map[string]interface{}) - if v, ok := m["dynamic_scaling_in_suspended"]; ok { - out.DynamicScalingInSuspended = aws.Bool(v.(bool)) + if v, ok := tfMap["dynamic_scaling_in_suspended"]; ok { + apiObject.DynamicScalingInSuspended = aws.Bool(v.(bool)) } - if v, ok := m["dynamic_scaling_out_suspended"]; ok { - out.DynamicScalingOutSuspended = aws.Bool(v.(bool)) + if v, ok := tfMap["dynamic_scaling_out_suspended"]; ok { + apiObject.DynamicScalingOutSuspended = aws.Bool(v.(bool)) } - if v, ok := m["scheduled_scaling_suspended"]; ok { - out.ScheduledScalingSuspended = aws.Bool(v.(bool)) + if v, ok := tfMap["scheduled_scaling_suspended"]; ok { + apiObject.ScheduledScalingSuspended = aws.Bool(v.(bool)) } - return out + return apiObject } -func flattenSuspendedState(cfg *awstypes.SuspendedState) []interface{} { - if cfg == nil { +func flattenSuspendedState(apiObject *awstypes.SuspendedState) []interface{} { + if apiObject == nil { return []interface{}{} } - m := make(map[string]interface{}) + tfMap := make(map[string]interface{}) - if v := cfg.DynamicScalingInSuspended; v != nil { - m["dynamic_scaling_in_suspended"] = aws.ToBool(v) + if v := apiObject.DynamicScalingInSuspended; v != nil { + tfMap["dynamic_scaling_in_suspended"] = aws.ToBool(v) } - if v := cfg.DynamicScalingOutSuspended; v != nil { - m["dynamic_scaling_out_suspended"] = aws.ToBool(v) + if v := apiObject.DynamicScalingOutSuspended; v != nil { + tfMap["dynamic_scaling_out_suspended"] = aws.ToBool(v) } - if v := cfg.ScheduledScalingSuspended; v != nil { - m["scheduled_scaling_suspended"] = aws.ToBool(v) + if v := apiObject.ScheduledScalingSuspended; v != nil { + tfMap["scheduled_scaling_suspended"] = aws.ToBool(v) } - return []interface{}{m} + return []interface{}{tfMap} }