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 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 14d54c83e19..2572a04dca1 100644 --- a/aws/internal/service/ecs/waiter/status.go +++ b/aws/internal/service/ecs/waiter/status.go @@ -8,15 +8,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" @@ -29,24 +24,35 @@ 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) - output, err := conn.DescribeCapacityProviders(input) + if tfresource.NotFound(err) { + return nil, "", nil + } if err != nil { - return nil, CapacityProviderStatusUnknown, err + return nil, "", err + } + + return output, aws.StringValue(output.Status), nil + } +} + +func CapacityProviderUpdateStatus(conn *ecs.ECS, arn string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.CapacityProviderByARN(conn, arn) + + if tfresource.NotFound(err) { + return nil, "", nil } - if len(output.CapacityProviders) == 0 { - return nil, CapacityProviderStatusNotFound, nil + if err != nil { + return nil, "", err } - return output.CapacityProviders[0], aws.StringValue(output.CapacityProviders[0].Status), 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 a7135e7b3fa..8c4a462047d 100644 --- a/aws/internal/service/ecs/waiter/waiter.go +++ b/aws/internal/service/ecs/waiter/waiter.go @@ -9,8 +9,8 @@ import ( ) const ( - // Maximum amount of time to wait for a Capacity Provider to return INACTIVE - CapacityProviderInactiveTimeout = 20 * time.Minute + CapacityProviderDeleteTimeout = 20 * time.Minute + CapacityProviderUpdateTimeout = 10 * time.Minute ServiceCreateTimeout = 2 * time.Minute ServiceInactiveTimeout = 10 * time.Minute @@ -23,13 +23,29 @@ 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 +} + +func CapacityProviderUpdated(conn *ecs.ECS, arn string) (*ecs.CapacityProvider, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ecs.CapacityProviderUpdateStatusUpdateInProgress}, + Target: []string{ecs.CapacityProviderUpdateStatusUpdateComplete}, + Refresh: CapacityProviderUpdateStatus(conn, arn), + Timeout: CapacityProviderUpdateTimeout, } outputRaw, err := stateConf.WaitForState() diff --git a/aws/resource_aws_ecs_capacity_provider.go b/aws/resource_aws_ecs_capacity_provider.go index 0e63f6aeb3b..244bcab916a 100644 --- a/aws/resource_aws_ecs_capacity_provider.go +++ b/aws/resource_aws_ecs_capacity_provider.go @@ -7,10 +7,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" ) func resourceAwsEcsCapacityProvider() *schema.Resource { @@ -24,12 +28,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, @@ -44,70 +44,62 @@ 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, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - ecs.ManagedTerminationProtectionEnabled, - ecs.ManagedTerminationProtectionDisabled, - }, false), + ValidateFunc: validateArn, }, "managed_scaling": { Type: schema.TypeList, 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, - }, false)}, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(ecs.ManagedScalingStatus_Values(), false)}, "target_capacity": { Type: schema.TypeInt, Optional: true, Computed: true, - ForceNew: true, ValidateFunc: validation.IntBetween(1, 100), }, }, }, }, + "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(), }, @@ -119,9 +111,10 @@ 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)), - AutoScalingGroupProvider: expandAutoScalingGroupProvider(d.Get("auto_scaling_group_provider")), + Name: aws.String(name), + AutoScalingGroupProvider: expandAutoScalingGroupProviderCreate(d.Get("auto_scaling_group_provider")), } // `CreateCapacityProviderInput` does not accept an empty array of tags @@ -129,16 +122,14 @@ func resourceAwsEcsCapacityProviderCreate(d *schema.ResourceData, meta interface input.Tags = tags.IgnoreAws().EcsTags() } - out, err := conn.CreateCapacityProvider(&input) + log.Printf("[DEBUG] Creating ECS Capacity Provider: %s", input) + output, 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): %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) } @@ -148,41 +139,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) + output, err := finder.CapacityProviderByARN(conn, d.Id()) - if err != nil { - return fmt.Errorf("error reading ECS Capacity Provider (%s): %s", d.Id(), err) - } - - 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 { @@ -193,41 +170,74 @@ 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 } func resourceAwsEcsCapacityProviderUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ecsconn + if d.HasChangesExcept("tags", "tags_all") { + input := &ecs.UpdateCapacityProviderInput{ + AutoScalingGroupProvider: expandAutoScalingGroupProviderUpdate(d.Get("auto_scaling_group_provider")), + Name: aws.String(d.Get("name").(string)), + } + + 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 { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + _, err = conn.UpdateCapacityProvider(input) + } + + if err != nil { + return fmt.Errorf("error updating ECS Capacity Provider (%s): %w", 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 Cluster (%s) tags: %s", d.Id(), err) + return fmt.Errorf("error updating ECS Capacity Provider (%s) tags: %w", d.Id(), err) } } - return nil + return resourceAwsEcsCapacityProviderRead(d, meta) } 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) } @@ -246,7 +256,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 +274,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..ff206af8d73 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() - } - - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving ECS Capacity Provider: %w", err)) - return sweeperErrs + err = lister.DescribeCapacityProvidersPages(conn, input, func(page *ecs.DescribeCapacityProvidersOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - 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() @@ -170,14 +149,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 +168,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 +267,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 @@ -284,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 @@ -311,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 } } @@ -396,30 +377,30 @@ 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 + name = %[1]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 = %[2]d + maximum_scaling_step_size = %[3]d + minimum_scaling_step_size = %[4]d + status = %[5]q + target_capacity = %[6]d } } } -`, rName) +`, rName, warmup, max, min, status, cap) } 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 @@ -439,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 { @@ -452,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 {