From 13d6a44eb1a3fae899ea4692ea28dc9132cb4f8e Mon Sep 17 00:00:00 2001 From: shuheiktgw Date: Fri, 19 Feb 2021 07:45:21 +0900 Subject: [PATCH 1/4] Add support for ECS capacity provider update --- aws/internal/service/ecs/waiter/status.go | 22 ++++ aws/internal/service/ecs/waiter/waiter.go | 21 +++ aws/resource_aws_ecs_capacity_provider.go | 123 +++++++++++++----- ...resource_aws_ecs_capacity_provider_test.go | 38 ++++-- 4 files changed, 160 insertions(+), 44 deletions(-) diff --git a/aws/internal/service/ecs/waiter/status.go b/aws/internal/service/ecs/waiter/status.go index 14d54c83e19..d4e56981585 100644 --- a/aws/internal/service/ecs/waiter/status.go +++ b/aws/internal/service/ecs/waiter/status.go @@ -1,6 +1,7 @@ package waiter import ( + "fmt" "log" "github.com/aws/aws-sdk-go/aws" @@ -95,3 +96,24 @@ func ClusterStatus(conn *ecs.ECS, arn string) resource.StateRefreshFunc { return output, aws.StringValue(output.Clusters[0].Status), err } } + +// CapacityProviderUpdateStatus fetches the Capacity Provider and its Update Status +func CapacityProviderUpdateStatus(conn *ecs.ECS, capacityProvider string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := conn.DescribeCapacityProviders(&ecs.DescribeCapacityProvidersInput{ + CapacityProviders: []*string{aws.String(capacityProvider)}, + }) + + if err != nil { + return nil, "", err + } + + if len(output.CapacityProviders) == 0 { + return nil, "", fmt.Errorf("ECS Capacity Provider %q missing", capacityProvider) + } + + c := output.CapacityProviders[0] + + return c, aws.StringValue(c.UpdateStatus), nil + } +} diff --git a/aws/internal/service/ecs/waiter/waiter.go b/aws/internal/service/ecs/waiter/waiter.go index a7135e7b3fa..6048ed4d2ff 100644 --- a/aws/internal/service/ecs/waiter/waiter.go +++ b/aws/internal/service/ecs/waiter/waiter.go @@ -12,6 +12,9 @@ const ( // Maximum amount of time to wait for a Capacity Provider to return INACTIVE CapacityProviderInactiveTimeout = 20 * time.Minute + // Maximum amount of time to wait for a Capacity Provider to return UPDATE_COMPLETE or UPDATE_FAILED + CapacityProviderUpdateTimeout = 10 * time.Minute + ServiceCreateTimeout = 2 * time.Minute ServiceInactiveTimeout = 10 * time.Minute ServiceInactiveTimeoutMin = 1 * time.Second @@ -137,3 +140,21 @@ func ClusterDeleted(conn *ecs.ECS, arn string) (*ecs.Cluster, error) { return nil, err } + +// CapacityProviderUpdate waits for a Capacity Provider to return UPDATE_COMPLETE or UPDATE_FAILED +func CapacityProviderUpdate(conn *ecs.ECS, capacityProvider string) (*ecs.CapacityProvider, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ecs.CapacityProviderUpdateStatusUpdateInProgress}, + Target: []string{ecs.CapacityProviderUpdateStatusUpdateComplete}, + Refresh: CapacityProviderUpdateStatus(conn, capacityProvider), + Timeout: CapacityProviderUpdateTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*ecs.CapacityProvider); ok { + return v, err + } + + return nil, err +} diff --git a/aws/resource_aws_ecs_capacity_provider.go b/aws/resource_aws_ecs_capacity_provider.go index 0e63f6aeb3b..8bd6f84f365 100644 --- a/aws/resource_aws_ecs_capacity_provider.go +++ b/aws/resource_aws_ecs_capacity_provider.go @@ -3,16 +3,22 @@ package aws import ( "fmt" "log" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/ecs" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/waiter" ) +const ( + ecsCapacityProviderTimeoutUpdate = 10 * time.Minute +) + func resourceAwsEcsCapacityProvider() *schema.Resource { return &schema.Resource{ Create: resourceAwsEcsCapacityProviderCreate, @@ -51,7 +57,6 @@ func resourceAwsEcsCapacityProvider() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ ecs.ManagedTerminationProtectionEnabled, ecs.ManagedTerminationProtectionDisabled, @@ -62,35 +67,30 @@ func resourceAwsEcsCapacityProvider() *schema.Resource { MaxItems: 1, Optional: true, Computed: true, - ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "instance_warmup_period": { Type: schema.TypeInt, Optional: true, Computed: true, - ForceNew: true, ValidateFunc: validation.IntBetween(1, 10000), }, "maximum_scaling_step_size": { Type: schema.TypeInt, Optional: true, Computed: true, - ForceNew: true, ValidateFunc: validation.IntBetween(1, 10000), }, "minimum_scaling_step_size": { Type: schema.TypeInt, Optional: true, Computed: true, - ForceNew: true, ValidateFunc: validation.IntBetween(1, 10000), }, "status": { Type: schema.TypeString, Optional: true, Computed: true, - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ ecs.ManagedScalingStatusEnabled, ecs.ManagedScalingStatusDisabled, @@ -99,7 +99,6 @@ func resourceAwsEcsCapacityProvider() *schema.Resource { Type: schema.TypeInt, Optional: true, Computed: true, - ForceNew: true, ValidateFunc: validation.IntBetween(1, 100), }, }, @@ -121,7 +120,7 @@ func resourceAwsEcsCapacityProviderCreate(d *schema.ResourceData, meta interface input := ecs.CreateCapacityProviderInput{ Name: aws.String(d.Get("name").(string)), - AutoScalingGroupProvider: expandAutoScalingGroupProvider(d.Get("auto_scaling_group_provider")), + AutoScalingGroupProvider: expandAutoScalingGroupProviderCreate(d.Get("auto_scaling_group_provider")), } // `CreateCapacityProviderInput` does not accept an empty array of tags @@ -132,7 +131,7 @@ func resourceAwsEcsCapacityProviderCreate(d *schema.ResourceData, meta interface out, err := conn.CreateCapacityProvider(&input) if err != nil { - return fmt.Errorf("error creating capacity provider: %s", err) + return fmt.Errorf("error creating ECS Capacity Provider: %s", err) } provider := *out.CapacityProvider @@ -203,15 +202,44 @@ func resourceAwsEcsCapacityProviderRead(d *schema.ResourceData, meta interface{} func resourceAwsEcsCapacityProviderUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ecsconn + input := &ecs.UpdateCapacityProviderInput{ + Name: aws.String(d.Get("name").(string)), + } + + if d.HasChange("auto_scaling_group_provider") { + input.AutoScalingGroupProvider = expandAutoScalingGroupProviderUpdate(d.Get("auto_scaling_group_provider")) + + err := resource.Retry(ecsCapacityProviderTimeoutUpdate, func() *resource.RetryError { + _, err := conn.UpdateCapacityProvider(input) + if err != nil { + if isAWSErr(err, ecs.ErrCodeUpdateInProgressException, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + if isResourceTimeoutError(err) { + _, err = conn.UpdateCapacityProvider(input) + } + if err != nil { + return fmt.Errorf("error updating ECS Capacity Provider (%s): %s", d.Id(), err) + } + + if _, err = waiter.CapacityProviderUpdate(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for ECS Capacity Provider (%s) update: %s", d.Id(), err) + } + } + if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") if err := keyvaluetags.EcsUpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating ECS Cluster (%s) tags: %s", d.Id(), err) + return fmt.Errorf("error updating ECS Capacity Provider (%s) tags: %s", d.Id(), err) } } - return nil + return resourceAwsEcsCapacityProviderRead(d, meta) } func resourceAwsEcsCapacityProviderDelete(d *schema.ResourceData, meta interface{}) error { @@ -246,7 +274,7 @@ func resourceAwsEcsCapacityProviderImport(d *schema.ResourceData, meta interface return []*schema.ResourceData{d}, nil } -func expandAutoScalingGroupProvider(configured interface{}) *ecs.AutoScalingGroupProvider { +func expandAutoScalingGroupProviderCreate(configured interface{}) *ecs.AutoScalingGroupProvider { if configured == nil { return nil } @@ -264,31 +292,64 @@ func expandAutoScalingGroupProvider(configured interface{}) *ecs.AutoScalingGrou prov.ManagedTerminationProtection = aws.String(mtp) } - if v := p["managed_scaling"].([]interface{}); len(v) > 0 && v[0].(map[string]interface{}) != nil { - ms := v[0].(map[string]interface{}) - managedScaling := ecs.ManagedScaling{} + prov.ManagedScaling = expandManagedScaling(p["managed_scaling"]) - if val, ok := ms["instance_warmup_period"].(int); ok && val != 0 { - managedScaling.InstanceWarmupPeriod = aws.Int64(int64(val)) - } - if val, ok := ms["maximum_scaling_step_size"].(int); ok && val != 0 { - managedScaling.MaximumScalingStepSize = aws.Int64(int64(val)) - } - if val, ok := ms["minimum_scaling_step_size"].(int); ok && val != 0 { - managedScaling.MinimumScalingStepSize = aws.Int64(int64(val)) - } - if val, ok := ms["status"].(string); ok && len(val) > 0 { - managedScaling.Status = aws.String(val) - } - if val, ok := ms["target_capacity"].(int); ok && val != 0 { - managedScaling.TargetCapacity = aws.Int64(int64(val)) - } - prov.ManagedScaling = &managedScaling + return &prov +} + +func expandAutoScalingGroupProviderUpdate(configured interface{}) *ecs.AutoScalingGroupProviderUpdate { + if configured == nil { + return nil + } + + if configured.([]interface{}) == nil || len(configured.([]interface{})) == 0 { + return nil } + prov := ecs.AutoScalingGroupProviderUpdate{} + p := configured.([]interface{})[0].(map[string]interface{}) + + if mtp := p["managed_termination_protection"].(string); len(mtp) > 0 { + prov.ManagedTerminationProtection = aws.String(mtp) + } + + prov.ManagedScaling = expandManagedScaling(p["managed_scaling"]) + return &prov } +func expandManagedScaling(configured interface{}) *ecs.ManagedScaling { + if configured == nil { + return nil + } + + if configured.([]interface{}) == nil || len(configured.([]interface{})) == 0 { + return nil + } + + p := configured.([]interface{})[0].(map[string]interface{}) + + managedScaling := ecs.ManagedScaling{} + + if val, ok := p["instance_warmup_period"].(int); ok && val != 0 { + managedScaling.InstanceWarmupPeriod = aws.Int64(int64(val)) + } + if val, ok := p["maximum_scaling_step_size"].(int); ok && val != 0 { + managedScaling.MaximumScalingStepSize = aws.Int64(int64(val)) + } + if val, ok := p["minimum_scaling_step_size"].(int); ok && val != 0 { + managedScaling.MinimumScalingStepSize = aws.Int64(int64(val)) + } + if val, ok := p["status"].(string); ok && len(val) > 0 { + managedScaling.Status = aws.String(val) + } + if val, ok := p["target_capacity"].(int); ok && val != 0 { + managedScaling.TargetCapacity = aws.Int64(int64(val)) + } + + return &managedScaling +} + func flattenAutoScalingGroupProvider(provider *ecs.AutoScalingGroupProvider) []map[string]interface{} { if provider == nil { return nil diff --git a/aws/resource_aws_ecs_capacity_provider_test.go b/aws/resource_aws_ecs_capacity_provider_test.go index baea10c4027..bf740f330a7 100644 --- a/aws/resource_aws_ecs_capacity_provider_test.go +++ b/aws/resource_aws_ecs_capacity_provider_test.go @@ -170,14 +170,14 @@ func TestAccAWSEcsCapacityProvider_ManagedScaling(t *testing.T) { CheckDestroy: testAccCheckAWSEcsCapacityProviderDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEcsCapacityProviderConfigManagedScaling(rName), + Config: testAccAWSEcsCapacityProviderConfigManagedScaling(rName, ecs.ManagedScalingStatusEnabled, 300, 10, 1, 50), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEcsCapacityProviderExists(resourceName, &provider), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttrPair(resourceName, "auto_scaling_group_provider.0.auto_scaling_group_arn", "aws_autoscaling_group.test", "arn"), resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_termination_protection", "DISABLED"), - resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.instance_warmup_period", "400"), - resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.minimum_scaling_step_size", "2"), + resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.instance_warmup_period", "300"), + resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.minimum_scaling_step_size", "1"), resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.maximum_scaling_step_size", "10"), resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.status", "ENABLED"), resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.target_capacity", "50"), @@ -189,6 +189,20 @@ func TestAccAWSEcsCapacityProvider_ManagedScaling(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccAWSEcsCapacityProviderConfigManagedScaling(rName, ecs.ManagedScalingStatusDisabled, 400, 100, 10, 100), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsCapacityProviderExists(resourceName, &provider), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrPair(resourceName, "auto_scaling_group_provider.0.auto_scaling_group_arn", "aws_autoscaling_group.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_termination_protection", "DISABLED"), + resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.instance_warmup_period", "400"), + resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.minimum_scaling_step_size", "10"), + resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.maximum_scaling_step_size", "100"), + resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.status", "DISABLED"), + resource.TestCheckResourceAttr(resourceName, "auto_scaling_group_provider.0.managed_scaling.0.target_capacity", "100"), + ), + }, }, }) } @@ -274,8 +288,6 @@ func TestAccAWSEcsCapacityProvider_Tags(t *testing.T) { }) } -// TODO add an update test config - Reference: https://github.com/aws/containers-roadmap/issues/633 - func testAccCheckAWSEcsCapacityProviderDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ecsconn @@ -396,24 +408,24 @@ resource "aws_ecs_capacity_provider" "test" { `, rName) } -func testAccAWSEcsCapacityProviderConfigManagedScaling(rName string) string { +func testAccAWSEcsCapacityProviderConfigManagedScaling(rName, status string, warmup, max, min, cap int) string { return testAccAWSEcsCapacityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_ecs_capacity_provider" "test" { name = %q auto_scaling_group_provider { - auto_scaling_group_arn = aws_autoscaling_group.test.arn + auto_scaling_group_arn = aws_autoscaling_group.test.arn managed_scaling { - instance_warmup_period = 400 - maximum_scaling_step_size = 10 - minimum_scaling_step_size = 2 - status = "ENABLED" - target_capacity = 50 + instance_warmup_period = %v + maximum_scaling_step_size = %v + minimum_scaling_step_size = %v + status = %q + target_capacity = %v } } } -`, rName) +`, rName, warmup, max, min, status, cap) } func testAccAWSEcsCapacityProviderConfigManagedScalingPartial(rName string) string { From 47652d5c0c200ac7f80f48fa58c321af430721c3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 23 Jun 2021 16:58:30 -0400 Subject: [PATCH 2/4] r/aws_ecs_capacity_provider: Use internal finder package. --- aws/internal/service/ecs/finder/finder.go | 31 +++++ aws/internal/service/ecs/lister/list.go | 3 + .../service/ecs/lister/list_pages_gen.go | 31 +++++ aws/internal/service/ecs/waiter/status.go | 60 ++++------ aws/internal/service/ecs/waiter/waiter.go | 48 ++++---- aws/resource_aws_ecs_capacity_provider.go | 108 ++++++++---------- ...resource_aws_ecs_capacity_provider_test.go | 107 ++++++----------- 7 files changed, 196 insertions(+), 192 deletions(-) create mode 100644 aws/internal/service/ecs/lister/list.go create mode 100644 aws/internal/service/ecs/lister/list_pages_gen.go diff --git a/aws/internal/service/ecs/finder/finder.go b/aws/internal/service/ecs/finder/finder.go index cfb22460a00..987d2c732c3 100644 --- a/aws/internal/service/ecs/finder/finder.go +++ b/aws/internal/service/ecs/finder/finder.go @@ -6,6 +6,37 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) +func CapacityProviderByARN(conn *ecs.ECS, arn string) (*ecs.CapacityProvider, error) { + input := &ecs.DescribeCapacityProvidersInput{ + CapacityProviders: aws.StringSlice([]string{arn}), + Include: aws.StringSlice([]string{ecs.CapacityProviderFieldTags}), + } + + output, err := conn.DescribeCapacityProviders(input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.CapacityProviders) == 0 || output.CapacityProviders[0] == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + capacityProvider := output.CapacityProviders[0] + + if status := aws.StringValue(capacityProvider.Status); status == ecs.CapacityProviderStatusInactive { + return nil, &resource.NotFoundError{ + Message: status, + LastRequest: input, + } + } + + return capacityProvider, nil +} + func ClusterByARN(conn *ecs.ECS, arn string) (*ecs.DescribeClustersOutput, error) { input := &ecs.DescribeClustersInput{ Clusters: []*string{aws.String(arn)}, diff --git a/aws/internal/service/ecs/lister/list.go b/aws/internal/service/ecs/lister/list.go new file mode 100644 index 00000000000..d57d33e2bde --- /dev/null +++ b/aws/internal/service/ecs/lister/list.go @@ -0,0 +1,3 @@ +//go:generate go run ../../../generators/listpages/main.go -function=DescribeCapacityProviders -paginator=NextToken github.com/aws/aws-sdk-go/service/ecs + +package lister diff --git a/aws/internal/service/ecs/lister/list_pages_gen.go b/aws/internal/service/ecs/lister/list_pages_gen.go new file mode 100644 index 00000000000..60deeacadad --- /dev/null +++ b/aws/internal/service/ecs/lister/list_pages_gen.go @@ -0,0 +1,31 @@ +// Code generated by "aws/internal/generators/listpages/main.go -function=DescribeCapacityProviders -paginator=NextToken github.com/aws/aws-sdk-go/service/ecs"; DO NOT EDIT. + +package lister + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ecs" +) + +func DescribeCapacityProvidersPages(conn *ecs.ECS, input *ecs.DescribeCapacityProvidersInput, fn func(*ecs.DescribeCapacityProvidersOutput, bool) bool) error { + return DescribeCapacityProvidersPagesWithContext(context.Background(), conn, input, fn) +} + +func DescribeCapacityProvidersPagesWithContext(ctx context.Context, conn *ecs.ECS, input *ecs.DescribeCapacityProvidersInput, fn func(*ecs.DescribeCapacityProvidersOutput, bool) bool) error { + for { + output, err := conn.DescribeCapacityProvidersWithContext(ctx, input) + if err != nil { + return err + } + + lastPage := aws.StringValue(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} diff --git a/aws/internal/service/ecs/waiter/status.go b/aws/internal/service/ecs/waiter/status.go index d4e56981585..db1dd693756 100644 --- a/aws/internal/service/ecs/waiter/status.go +++ b/aws/internal/service/ecs/waiter/status.go @@ -9,15 +9,10 @@ import ( "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) const ( - // EventSubscription NotFound - CapacityProviderStatusNotFound = "NotFound" - - // EventSubscription Unknown - CapacityProviderStatusUnknown = "Unknown" - // AWS will likely add consts for these at some point ServiceStatusInactive = "INACTIVE" ServiceStatusActive = "ACTIVE" @@ -30,24 +25,40 @@ const ( ClusterStatusNone = "NONE" ) -// CapacityProviderStatus fetches the Capacity Provider and its Status -func CapacityProviderStatus(conn *ecs.ECS, capacityProvider string) resource.StateRefreshFunc { +func CapacityProviderStatus(conn *ecs.ECS, arn string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - input := &ecs.DescribeCapacityProvidersInput{ - CapacityProviders: aws.StringSlice([]string{capacityProvider}), + output, err := finder.CapacityProviderByARN(conn, arn) + + if tfresource.NotFound(err) { + return nil, "", nil } - output, err := conn.DescribeCapacityProviders(input) + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Status), nil + } +} + +// CapacityProviderUpdateStatus fetches the Capacity Provider and its Update Status +func CapacityProviderUpdateStatus(conn *ecs.ECS, capacityProvider string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := conn.DescribeCapacityProviders(&ecs.DescribeCapacityProvidersInput{ + CapacityProviders: []*string{aws.String(capacityProvider)}, + }) if err != nil { - return nil, CapacityProviderStatusUnknown, err + return nil, "", err } if len(output.CapacityProviders) == 0 { - return nil, CapacityProviderStatusNotFound, nil + return nil, "", fmt.Errorf("ECS Capacity Provider %q missing", capacityProvider) } - return output.CapacityProviders[0], aws.StringValue(output.CapacityProviders[0].Status), nil + c := output.CapacityProviders[0] + + return c, aws.StringValue(c.UpdateStatus), nil } } @@ -96,24 +107,3 @@ func ClusterStatus(conn *ecs.ECS, arn string) resource.StateRefreshFunc { return output, aws.StringValue(output.Clusters[0].Status), err } } - -// CapacityProviderUpdateStatus fetches the Capacity Provider and its Update Status -func CapacityProviderUpdateStatus(conn *ecs.ECS, capacityProvider string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := conn.DescribeCapacityProviders(&ecs.DescribeCapacityProvidersInput{ - CapacityProviders: []*string{aws.String(capacityProvider)}, - }) - - if err != nil { - return nil, "", err - } - - if len(output.CapacityProviders) == 0 { - return nil, "", fmt.Errorf("ECS Capacity Provider %q missing", capacityProvider) - } - - c := output.CapacityProviders[0] - - return c, aws.StringValue(c.UpdateStatus), nil - } -} diff --git a/aws/internal/service/ecs/waiter/waiter.go b/aws/internal/service/ecs/waiter/waiter.go index 6048ed4d2ff..d4ceb98e29e 100644 --- a/aws/internal/service/ecs/waiter/waiter.go +++ b/aws/internal/service/ecs/waiter/waiter.go @@ -9,8 +9,7 @@ import ( ) const ( - // Maximum amount of time to wait for a Capacity Provider to return INACTIVE - CapacityProviderInactiveTimeout = 20 * time.Minute + CapacityProviderDeleteTimeout = 20 * time.Minute // Maximum amount of time to wait for a Capacity Provider to return UPDATE_COMPLETE or UPDATE_FAILED CapacityProviderUpdateTimeout = 10 * time.Minute @@ -26,13 +25,30 @@ const ( ClusterAvailableDelay = 10 * time.Second ) -// CapacityProviderInactive waits for a Capacity Provider to return INACTIVE -func CapacityProviderInactive(conn *ecs.ECS, capacityProvider string) (*ecs.CapacityProvider, error) { +func CapacityProviderDeleted(conn *ecs.ECS, arn string) (*ecs.CapacityProvider, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ecs.CapacityProviderStatusActive}, - Target: []string{ecs.CapacityProviderStatusInactive}, - Refresh: CapacityProviderStatus(conn, capacityProvider), - Timeout: CapacityProviderInactiveTimeout, + Target: []string{""}, + Refresh: CapacityProviderStatus(conn, arn), + Timeout: CapacityProviderDeleteTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*ecs.CapacityProvider); ok { + return v, err + } + + return nil, err +} + +// CapacityProviderUpdate waits for a Capacity Provider to return UPDATE_COMPLETE or UPDATE_FAILED +func CapacityProviderUpdate(conn *ecs.ECS, capacityProvider string) (*ecs.CapacityProvider, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ecs.CapacityProviderUpdateStatusUpdateInProgress}, + Target: []string{ecs.CapacityProviderUpdateStatusUpdateComplete}, + Refresh: CapacityProviderUpdateStatus(conn, capacityProvider), + Timeout: CapacityProviderUpdateTimeout, } outputRaw, err := stateConf.WaitForState() @@ -140,21 +156,3 @@ func ClusterDeleted(conn *ecs.ECS, arn string) (*ecs.Cluster, error) { return nil, err } - -// CapacityProviderUpdate waits for a Capacity Provider to return UPDATE_COMPLETE or UPDATE_FAILED -func CapacityProviderUpdate(conn *ecs.ECS, capacityProvider string) (*ecs.CapacityProvider, error) { - stateConf := &resource.StateChangeConf{ - Pending: []string{ecs.CapacityProviderUpdateStatusUpdateInProgress}, - Target: []string{ecs.CapacityProviderUpdateStatusUpdateComplete}, - Refresh: CapacityProviderUpdateStatus(conn, capacityProvider), - Timeout: CapacityProviderUpdateTimeout, - } - - outputRaw, err := stateConf.WaitForState() - - if v, ok := outputRaw.(*ecs.CapacityProvider); ok { - return v, err - } - - return nil, err -} diff --git a/aws/resource_aws_ecs_capacity_provider.go b/aws/resource_aws_ecs_capacity_provider.go index 8bd6f84f365..96ea38cae03 100644 --- a/aws/resource_aws_ecs_capacity_provider.go +++ b/aws/resource_aws_ecs_capacity_provider.go @@ -8,11 +8,14 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/ecs" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) const ( @@ -30,12 +33,8 @@ func resourceAwsEcsCapacityProvider() *schema.Resource { }, CustomizeDiff: SetTagsDiff, + Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, "arn": { Type: schema.TypeString, Computed: true, @@ -50,17 +49,8 @@ func resourceAwsEcsCapacityProvider() *schema.Resource { "auto_scaling_group_arn": { Type: schema.TypeString, Required: true, - ValidateFunc: validateArn, ForceNew: true, - }, - "managed_termination_protection": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice([]string{ - ecs.ManagedTerminationProtectionEnabled, - ecs.ManagedTerminationProtectionDisabled, - }, false), + ValidateFunc: validateArn, }, "managed_scaling": { Type: schema.TypeList, @@ -88,13 +78,10 @@ func resourceAwsEcsCapacityProvider() *schema.Resource { ValidateFunc: validation.IntBetween(1, 10000), }, "status": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice([]string{ - ecs.ManagedScalingStatusEnabled, - ecs.ManagedScalingStatusDisabled, - }, false)}, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(ecs.ManagedScalingStatus_Values(), false)}, "target_capacity": { Type: schema.TypeInt, Optional: true, @@ -104,9 +91,20 @@ func resourceAwsEcsCapacityProvider() *schema.Resource { }, }, }, + "managed_termination_protection": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(ecs.ManagedTerminationProtection_Values(), false), + }, }, }, }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "tags": tagsSchema(), "tags_all": tagsSchemaComputed(), }, @@ -118,8 +116,9 @@ func resourceAwsEcsCapacityProviderCreate(d *schema.ResourceData, meta interface defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + name := d.Get("name").(string) input := ecs.CreateCapacityProviderInput{ - Name: aws.String(d.Get("name").(string)), + Name: aws.String(name), AutoScalingGroupProvider: expandAutoScalingGroupProviderCreate(d.Get("auto_scaling_group_provider")), } @@ -128,16 +127,13 @@ func resourceAwsEcsCapacityProviderCreate(d *schema.ResourceData, meta interface input.Tags = tags.IgnoreAws().EcsTags() } - out, err := conn.CreateCapacityProvider(&input) + output, err := conn.CreateCapacityProvider(&input) if err != nil { - return fmt.Errorf("error creating ECS Capacity Provider: %s", err) + return fmt.Errorf("error creating ECS Capacity Provider (%s): %w", name, err) } - provider := *out.CapacityProvider - - log.Printf("[DEBUG] ECS Capacity Provider created: %s", aws.StringValue(provider.CapacityProviderArn)) - d.SetId(aws.StringValue(provider.CapacityProviderArn)) + d.SetId(aws.StringValue(output.CapacityProvider.CapacityProviderArn)) return resourceAwsEcsCapacityProviderRead(d, meta) } @@ -147,41 +143,27 @@ func resourceAwsEcsCapacityProviderRead(d *schema.ResourceData, meta interface{} defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - input := &ecs.DescribeCapacityProvidersInput{ - CapacityProviders: []*string{aws.String(d.Id())}, - Include: []*string{aws.String(ecs.CapacityProviderFieldTags)}, - } - - output, err := conn.DescribeCapacityProviders(input) - - if err != nil { - return fmt.Errorf("error reading ECS Capacity Provider (%s): %s", d.Id(), err) - } + output, err := finder.CapacityProviderByARN(conn, d.Id()) - var provider *ecs.CapacityProvider - for _, cp := range output.CapacityProviders { - if aws.StringValue(cp.CapacityProviderArn) == d.Id() { - provider = cp - break - } - } - - if provider == nil { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ECS Capacity Provider (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - if aws.StringValue(provider.Status) == ecs.CapacityProviderStatusInactive { - log.Printf("[WARN] ECS Capacity Provider (%s) is INACTIVE, removing from state", d.Id()) - d.SetId("") - return nil + if err != nil { + return fmt.Errorf("error reading ECS Capacity Provider (%s): %w", d.Id(), err) + } + + d.Set("arn", output.CapacityProviderArn) + + if err := d.Set("auto_scaling_group_provider", flattenAutoScalingGroupProvider(output.AutoScalingGroupProvider)); err != nil { + return fmt.Errorf("error setting auto_scaling_group_provider: %w", err) } - d.Set("arn", provider.CapacityProviderArn) - d.Set("name", provider.Name) + d.Set("name", output.Name) - tags := keyvaluetags.EcsKeyValueTags(provider.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) + tags := keyvaluetags.EcsKeyValueTags(output.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { @@ -192,10 +174,6 @@ func resourceAwsEcsCapacityProviderRead(d *schema.ResourceData, meta interface{} return fmt.Errorf("error setting tags_all: %w", err) } - if err := d.Set("auto_scaling_group_provider", flattenAutoScalingGroupProvider(provider.AutoScalingGroupProvider)); err != nil { - return fmt.Errorf("error setting autoscaling group provider: %s", err) - } - return nil } @@ -245,17 +223,21 @@ func resourceAwsEcsCapacityProviderUpdate(d *schema.ResourceData, meta interface func resourceAwsEcsCapacityProviderDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ecsconn - input := &ecs.DeleteCapacityProviderInput{ + log.Printf("[DEBUG] Deleting ECS Capacity Provider (%s)", d.Id()) + _, err := conn.DeleteCapacityProvider(&ecs.DeleteCapacityProviderInput{ CapacityProvider: aws.String(d.Id()), - } + }) - _, err := conn.DeleteCapacityProvider(input) + // "An error occurred (ClientException) when calling the DeleteCapacityProvider operation: The specified capacity provider does not exist. Specify a valid name or ARN and try again." + if tfawserr.ErrMessageContains(err, ecs.ErrCodeClientException, "capacity provider does not exist") { + return nil + } if err != nil { return fmt.Errorf("error deleting ECS Capacity Provider (%s): %w", d.Id(), err) } - if _, err := waiter.CapacityProviderInactive(conn, d.Id()); err != nil { + if _, err := waiter.CapacityProviderDeleted(conn, d.Id()); err != nil { return fmt.Errorf("error waiting for ECS Capacity Provider (%s) to delete: %w", d.Id(), err) } diff --git a/aws/resource_aws_ecs_capacity_provider_test.go b/aws/resource_aws_ecs_capacity_provider_test.go index bf740f330a7..c0146e78d27 100644 --- a/aws/resource_aws_ecs_capacity_provider_test.go +++ b/aws/resource_aws_ecs_capacity_provider_test.go @@ -11,7 +11,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/lister" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func init() { @@ -27,71 +29,48 @@ func init() { func testSweepEcsCapacityProviders(region string) error { client, err := sharedClientForRegion(region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.(*AWSClient).ecsconn input := &ecs.DescribeCapacityProvidersInput{} var sweeperErrs *multierror.Error + sweepResources := make([]*testSweepResource, 0) - for { - output, err := conn.DescribeCapacityProviders(input) - - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping ECS Capacity Provider sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() + err = lister.DescribeCapacityProvidersPages(conn, input, func(page *ecs.DescribeCapacityProvidersOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving ECS Capacity Provider: %w", err)) - return sweeperErrs - } - - for _, capacityProvider := range output.CapacityProviders { - if capacityProvider == nil { - continue - } - + for _, capacityProvider := range page.CapacityProviders { arn := aws.StringValue(capacityProvider.CapacityProviderArn) - input := &ecs.DeleteCapacityProviderInput{ - CapacityProvider: capacityProvider.CapacityProviderArn, - } - if aws.StringValue(capacityProvider.Name) == "FARGATE" || aws.StringValue(capacityProvider.Name) == "FARGATE_SPOT" { + if name := aws.StringValue(capacityProvider.Name); name == "FARGATE" || name == "FARGATE_SPOT" { log.Printf("[INFO] Skipping AWS managed ECS Capacity Provider: %s", arn) continue } - if aws.StringValue(capacityProvider.Status) == ecs.CapacityProviderStatusInactive { - log.Printf("[INFO] Skipping ECS Capacity Provider with INACTIVE status: %s", arn) - continue - } + r := resourceAwsEcsCapacityProvider() + d := r.Data(nil) + d.SetId(arn) - log.Printf("[INFO] Deleting ECS Capacity Provider: %s", arn) - _, err := conn.DeleteCapacityProvider(input) + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) + } - if err != nil { - sweeperErr := fmt.Errorf("error deleting ECS Capacity Provider (%s): %w", arn, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - continue - } + return !lastPage + }) - if _, err := waiter.CapacityProviderInactive(conn, arn); err != nil { - sweeperErr := fmt.Errorf("error waiting for ECS Capacity Provider (%s) to delete: %w", arn, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - continue - } - } + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping ECS Capacity Providers sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() + } - if aws.StringValue(output.NextToken) == "" { - break - } + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing ECS Capacity Providers for %s: %w", region, err)) + } - input.NextToken = output.NextToken + if err := testSweepResourceOrchestrator(sweepResources); err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping ECS Capacity Providers for %s: %w", region, err)) } return sweeperErrs.ErrorOrNil() @@ -296,21 +275,17 @@ func testAccCheckAWSEcsCapacityProviderDestroy(s *terraform.State) error { continue } - input := &ecs.DescribeCapacityProvidersInput{ - CapacityProviders: aws.StringSlice([]string{rs.Primary.ID}), - } + _, err := finder.CapacityProviderByARN(conn, rs.Primary.ID) - output, err := conn.DescribeCapacityProviders(input) + if tfresource.NotFound(err) { + continue + } if err != nil { return err } - for _, capacityProvider := range output.CapacityProviders { - if aws.StringValue(capacityProvider.CapacityProviderArn) == rs.Primary.ID && aws.StringValue(capacityProvider.Status) != ecs.CapacityProviderStatusInactive { - return fmt.Errorf("ECS Capacity Provider (%s) still exists", rs.Primary.ID) - } - } + return fmt.Errorf("ECS Capacity Provider ID %s still exists", rs.Primary.ID) } return nil @@ -323,27 +298,21 @@ func testAccCheckAWSEcsCapacityProviderExists(resourceName string, provider *ecs return fmt.Errorf("Not found: %s", resourceName) } - conn := testAccProvider.Meta().(*AWSClient).ecsconn - - input := &ecs.DescribeCapacityProvidersInput{ - CapacityProviders: []*string{aws.String(rs.Primary.ID)}, - Include: []*string{aws.String(ecs.CapacityProviderFieldTags)}, + if rs.Primary.ID == "" { + return fmt.Errorf("No ECS Capacity Provider ID is set") } - output, err := conn.DescribeCapacityProviders(input) + conn := testAccProvider.Meta().(*AWSClient).ecsconn + + output, err := finder.CapacityProviderByARN(conn, rs.Primary.ID) if err != nil { - return fmt.Errorf("error reading ECS Capacity Provider (%s): %s", rs.Primary.ID, err) + return err } - for _, cp := range output.CapacityProviders { - if aws.StringValue(cp.CapacityProviderArn) == rs.Primary.ID { - *provider = *cp - return nil - } - } + *provider = *output - return fmt.Errorf("ECS Capacity Provider (%s) not found", rs.Primary.ID) + return nil } } From 341a912fae2c72cd888dce5bfacff7944f8896aa Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 23 Jun 2021 17:25:42 -0400 Subject: [PATCH 3/4] r/aws_ecs_capacity_provider: Simplify waiting for update. --- aws/internal/service/ecs/waiter/status.go | 20 ++++----- aws/internal/service/ecs/waiter/waiter.go | 9 ++-- aws/resource_aws_ecs_capacity_provider.go | 42 +++++++++---------- ...resource_aws_ecs_capacity_provider_test.go | 22 +++++----- 4 files changed, 42 insertions(+), 51 deletions(-) diff --git a/aws/internal/service/ecs/waiter/status.go b/aws/internal/service/ecs/waiter/status.go index db1dd693756..2572a04dca1 100644 --- a/aws/internal/service/ecs/waiter/status.go +++ b/aws/internal/service/ecs/waiter/status.go @@ -1,7 +1,6 @@ package waiter import ( - "fmt" "log" "github.com/aws/aws-sdk-go/aws" @@ -41,24 +40,19 @@ func CapacityProviderStatus(conn *ecs.ECS, arn string) resource.StateRefreshFunc } } -// CapacityProviderUpdateStatus fetches the Capacity Provider and its Update Status -func CapacityProviderUpdateStatus(conn *ecs.ECS, capacityProvider string) resource.StateRefreshFunc { +func CapacityProviderUpdateStatus(conn *ecs.ECS, arn string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := conn.DescribeCapacityProviders(&ecs.DescribeCapacityProvidersInput{ - CapacityProviders: []*string{aws.String(capacityProvider)}, - }) + output, err := finder.CapacityProviderByARN(conn, arn) - if err != nil { - return nil, "", err + if tfresource.NotFound(err) { + return nil, "", nil } - if len(output.CapacityProviders) == 0 { - return nil, "", fmt.Errorf("ECS Capacity Provider %q missing", capacityProvider) + if err != nil { + return nil, "", err } - c := output.CapacityProviders[0] - - return c, aws.StringValue(c.UpdateStatus), nil + return output, aws.StringValue(output.UpdateStatus), nil } } diff --git a/aws/internal/service/ecs/waiter/waiter.go b/aws/internal/service/ecs/waiter/waiter.go index d4ceb98e29e..8c4a462047d 100644 --- a/aws/internal/service/ecs/waiter/waiter.go +++ b/aws/internal/service/ecs/waiter/waiter.go @@ -10,8 +10,6 @@ import ( const ( CapacityProviderDeleteTimeout = 20 * time.Minute - - // Maximum amount of time to wait for a Capacity Provider to return UPDATE_COMPLETE or UPDATE_FAILED CapacityProviderUpdateTimeout = 10 * time.Minute ServiceCreateTimeout = 2 * time.Minute @@ -28,7 +26,7 @@ const ( func CapacityProviderDeleted(conn *ecs.ECS, arn string) (*ecs.CapacityProvider, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ecs.CapacityProviderStatusActive}, - Target: []string{""}, + Target: []string{}, Refresh: CapacityProviderStatus(conn, arn), Timeout: CapacityProviderDeleteTimeout, } @@ -42,12 +40,11 @@ func CapacityProviderDeleted(conn *ecs.ECS, arn string) (*ecs.CapacityProvider, return nil, err } -// CapacityProviderUpdate waits for a Capacity Provider to return UPDATE_COMPLETE or UPDATE_FAILED -func CapacityProviderUpdate(conn *ecs.ECS, capacityProvider string) (*ecs.CapacityProvider, error) { +func CapacityProviderUpdated(conn *ecs.ECS, arn string) (*ecs.CapacityProvider, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ecs.CapacityProviderUpdateStatusUpdateInProgress}, Target: []string{ecs.CapacityProviderUpdateStatusUpdateComplete}, - Refresh: CapacityProviderUpdateStatus(conn, capacityProvider), + Refresh: CapacityProviderUpdateStatus(conn, arn), Timeout: CapacityProviderUpdateTimeout, } diff --git a/aws/resource_aws_ecs_capacity_provider.go b/aws/resource_aws_ecs_capacity_provider.go index 96ea38cae03..244bcab916a 100644 --- a/aws/resource_aws_ecs_capacity_provider.go +++ b/aws/resource_aws_ecs_capacity_provider.go @@ -3,7 +3,6 @@ package aws import ( "fmt" "log" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" @@ -18,10 +17,6 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) -const ( - ecsCapacityProviderTimeoutUpdate = 10 * time.Minute -) - func resourceAwsEcsCapacityProvider() *schema.Resource { return &schema.Resource{ Create: resourceAwsEcsCapacityProviderCreate, @@ -127,6 +122,7 @@ func resourceAwsEcsCapacityProviderCreate(d *schema.ResourceData, meta interface input.Tags = tags.IgnoreAws().EcsTags() } + log.Printf("[DEBUG] Creating ECS Capacity Provider: %s", input) output, err := conn.CreateCapacityProvider(&input) if err != nil { @@ -180,40 +176,44 @@ func resourceAwsEcsCapacityProviderRead(d *schema.ResourceData, meta interface{} func resourceAwsEcsCapacityProviderUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ecsconn - input := &ecs.UpdateCapacityProviderInput{ - Name: aws.String(d.Get("name").(string)), - } - - if d.HasChange("auto_scaling_group_provider") { - input.AutoScalingGroupProvider = expandAutoScalingGroupProviderUpdate(d.Get("auto_scaling_group_provider")) + if d.HasChangesExcept("tags", "tags_all") { + input := &ecs.UpdateCapacityProviderInput{ + AutoScalingGroupProvider: expandAutoScalingGroupProviderUpdate(d.Get("auto_scaling_group_provider")), + Name: aws.String(d.Get("name").(string)), + } - err := resource.Retry(ecsCapacityProviderTimeoutUpdate, func() *resource.RetryError { + log.Printf("[DEBUG] Updating ECS Capacity Provider: %s", input) + err := resource.Retry(waiter.CapacityProviderUpdateTimeout, func() *resource.RetryError { _, err := conn.UpdateCapacityProvider(input) + + if tfawserr.ErrCodeEquals(err, ecs.ErrCodeUpdateInProgressException) { + return resource.RetryableError(err) + } + if err != nil { - if isAWSErr(err, ecs.ErrCodeUpdateInProgressException, "") { - return resource.RetryableError(err) - } return resource.NonRetryableError(err) } + return nil }) - if isResourceTimeoutError(err) { + + if tfresource.TimedOut(err) { _, err = conn.UpdateCapacityProvider(input) } + if err != nil { - return fmt.Errorf("error updating ECS Capacity Provider (%s): %s", d.Id(), err) + return fmt.Errorf("error updating ECS Capacity Provider (%s): %w", d.Id(), err) } - if _, err = waiter.CapacityProviderUpdate(conn, d.Id()); err != nil { - return fmt.Errorf("error waiting for ECS Capacity Provider (%s) update: %s", d.Id(), err) + if _, err = waiter.CapacityProviderUpdated(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for ECS Capacity Provider (%s) to update: %w", d.Id(), err) } } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := keyvaluetags.EcsUpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating ECS Capacity Provider (%s) tags: %s", d.Id(), err) + return fmt.Errorf("error updating ECS Capacity Provider (%s) tags: %w", d.Id(), err) } } diff --git a/aws/resource_aws_ecs_capacity_provider_test.go b/aws/resource_aws_ecs_capacity_provider_test.go index c0146e78d27..ff206af8d73 100644 --- a/aws/resource_aws_ecs_capacity_provider_test.go +++ b/aws/resource_aws_ecs_capacity_provider_test.go @@ -380,17 +380,17 @@ resource "aws_ecs_capacity_provider" "test" { func testAccAWSEcsCapacityProviderConfigManagedScaling(rName, status string, warmup, max, min, cap int) string { return testAccAWSEcsCapacityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_ecs_capacity_provider" "test" { - name = %q + name = %[1]q auto_scaling_group_provider { auto_scaling_group_arn = aws_autoscaling_group.test.arn managed_scaling { - instance_warmup_period = %v - maximum_scaling_step_size = %v - minimum_scaling_step_size = %v - status = %q - target_capacity = %v + instance_warmup_period = %[2]d + maximum_scaling_step_size = %[3]d + minimum_scaling_step_size = %[4]d + status = %[5]q + target_capacity = %[6]d } } } @@ -400,7 +400,7 @@ resource "aws_ecs_capacity_provider" "test" { func testAccAWSEcsCapacityProviderConfigManagedScalingPartial(rName string) string { return testAccAWSEcsCapacityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_ecs_capacity_provider" "test" { - name = %q + name = %[1]q auto_scaling_group_provider { auto_scaling_group_arn = aws_autoscaling_group.test.arn @@ -420,7 +420,7 @@ resource "aws_ecs_capacity_provider" "test" { name = %[1]q tags = { - %[2]q = %[3]q, + %[2]q = %[3]q } auto_scaling_group_provider { @@ -433,11 +433,11 @@ resource "aws_ecs_capacity_provider" "test" { func testAccAWSEcsCapacityProviderConfigTags2(rName, tag1Key, tag1Value, tag2Key, tag2Value string) string { return testAccAWSEcsCapacityProviderConfigBase(rName) + fmt.Sprintf(` resource "aws_ecs_capacity_provider" "test" { - name = %q + name = %[1]q tags = { - %q = %q, - %q = %q, + %[2]q = %[3]q + %[4]q = %[5]q } auto_scaling_group_provider { From 81edd552bbb9508f449509e3f9381ff036f4ecbd Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 23 Jun 2021 17:27:41 -0400 Subject: [PATCH 4/4] Add CHANGELOG entry. --- .changelog/16942.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/16942.txt diff --git a/.changelog/16942.txt b/.changelog/16942.txt new file mode 100644 index 00000000000..c1aba454d3e --- /dev/null +++ b/.changelog/16942.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_ecs_capacity_provider: Allow updates to the `auto_scaling_group_provider` argument without recreating the resource +``` \ No newline at end of file