Skip to content

Commit

Permalink
Merge pull request #22672 from roberth-k/f-aws_ecs_cluster_capacity_p…
Browse files Browse the repository at this point in the history
…roviders

New Resource: aws_ecs_cluster_capacity_providers
  • Loading branch information
YakDriver authored Jan 26, 2022
2 parents 90bf785 + fac6b6f commit c47375e
Show file tree
Hide file tree
Showing 15 changed files with 1,001 additions and 134 deletions.
7 changes: 7 additions & 0 deletions .changelog/22672.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-resource
aws_ecs_cluster_capacity_providers
```

```release-note:bug
resource/aws_ecs_cluster: Provide new resource `aws_ecs_cluster_capacity_providers` to avoid bugs using `capacity_providers` and `default_capacity_provider_strategy`, which arguments will be deprecated in a future version
```
15 changes: 8 additions & 7 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1193,13 +1193,14 @@ func Provider() *schema.Provider {
"aws_ecrpublic_repository": ecrpublic.ResourceRepository(),
"aws_ecrpublic_repository_policy": ecrpublic.ResourceRepositoryPolicy(),

"aws_ecs_account_setting_default": ecs.ResourceAccountSettingDefault(),
"aws_ecs_capacity_provider": ecs.ResourceCapacityProvider(),
"aws_ecs_cluster": ecs.ResourceCluster(),
"aws_ecs_service": ecs.ResourceService(),
"aws_ecs_tag": ecs.ResourceTag(),
"aws_ecs_task_definition": ecs.ResourceTaskDefinition(),
"aws_ecs_task_set": ecs.ResourceTaskSet(),
"aws_ecs_account_setting_default": ecs.ResourceAccountSettingDefault(),
"aws_ecs_capacity_provider": ecs.ResourceCapacityProvider(),
"aws_ecs_cluster": ecs.ResourceCluster(),
"aws_ecs_cluster_capacity_providers": ecs.ResourceClusterCapacityProviders(),
"aws_ecs_service": ecs.ResourceService(),
"aws_ecs_tag": ecs.ResourceTag(),
"aws_ecs_task_definition": ecs.ResourceTaskDefinition(),
"aws_ecs_task_set": ecs.ResourceTaskSet(),

"aws_efs_access_point": efs.ResourceAccessPoint(),
"aws_efs_backup_policy": efs.ResourceBackupPolicy(),
Expand Down
65 changes: 17 additions & 48 deletions internal/service/ecs/cluster.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ecs

import (
"context"
"fmt"
"log"
"time"
Expand Down Expand Up @@ -42,7 +43,7 @@ func ResourceCluster() *schema.Resource {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringLenBetween(1, 255),
ValidateFunc: validateClusterName,
},
"arn": {
Type: schema.TypeString,
Expand All @@ -51,6 +52,7 @@ func ResourceCluster() *schema.Resource {
"capacity_providers": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Expand Down Expand Up @@ -114,6 +116,7 @@ func ResourceCluster() *schema.Resource {
"default_capacity_provider_strategy": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"base": {
Expand Down Expand Up @@ -220,7 +223,7 @@ func resourceClusterCreate(d *schema.ResourceData, meta interface{}) error {

d.SetId(aws.StringValue(out.Cluster.ClusterArn))

if _, err := waitClusterAvailable(conn, d.Id()); err != nil {
if _, err := waitClusterAvailable(context.Background(), conn, d.Id()); err != nil {
return fmt.Errorf("error waiting for ECS Cluster (%s) to become Available: %w", d.Id(), err)
}

Expand All @@ -247,26 +250,23 @@ func resourceClusterRead(d *schema.ResourceData, meta interface{}) error {
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig

var out *ecs.DescribeClustersOutput
err := resource.Retry(2*time.Minute, func() *resource.RetryError {
var cluster *ecs.Cluster
err := resource.Retry(clusterReadTimeout, func() *resource.RetryError {
var err error
out, err = FindClusterByNameOrARN(conn, d.Id())
cluster, err = FindClusterByNameOrARN(context.Background(), conn, d.Id())

if err != nil {
return resource.NonRetryableError(err)
if d.IsNewResource() && tfresource.NotFound(err) {
return resource.RetryableError(err)
}

if out == nil || len(out.Failures) > 0 {
if d.IsNewResource() {
return resource.RetryableError(&resource.NotFoundError{})
}
return resource.NonRetryableError(&resource.NotFoundError{})
if err != nil {
return resource.NonRetryableError(err)
}

return nil
})
if tfresource.TimedOut(err) {
out, err = FindClusterByNameOrARN(conn, d.Id())
cluster, err = FindClusterByNameOrARN(context.Background(), conn, d.Id())
}

if tfresource.NotFound(err) {
Expand All @@ -279,20 +279,6 @@ func resourceClusterRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("error reading ECS Cluster (%s): %s", d.Id(), err)
}

var cluster *ecs.Cluster
for _, c := range out.Clusters {
if aws.StringValue(c.ClusterArn) == d.Id() {
cluster = c
break
}
}

if cluster == nil {
log.Printf("[WARN] ECS Cluster (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

// Status==INACTIVE means deleted cluster
if aws.StringValue(cluster.Status) == "INACTIVE" {
log.Printf("[WARN] ECS Cluster (%s) deleted, removing from state", d.Id())
Expand Down Expand Up @@ -361,7 +347,7 @@ func resourceClusterUpdate(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("error changing ECS cluster (%s): %w", d.Id(), err)
}

if _, err := waitClusterAvailable(conn, d.Id()); err != nil {
if _, err := waitClusterAvailable(context.Background(), conn, d.Id()); err != nil {
return fmt.Errorf("error waiting for ECS Cluster (%s) to become Available: %w", d.Id(), err)
}
}
Expand All @@ -373,30 +359,13 @@ func resourceClusterUpdate(d *schema.ResourceData, meta interface{}) error {
DefaultCapacityProviderStrategy: expandCapacityProviderStrategy(d.Get("default_capacity_provider_strategy").(*schema.Set)),
}

err := resource.Retry(ecsClusterTimeoutUpdate, func() *resource.RetryError {
_, err := conn.PutClusterCapacityProviders(&input)
if err != nil {
if tfawserr.ErrMessageContains(err, ecs.ErrCodeClientException, "Cluster was not ACTIVE") {
return resource.RetryableError(err)
}
if tfawserr.ErrMessageContains(err, ecs.ErrCodeResourceInUseException, "") {
return resource.RetryableError(err)
}
if tfawserr.ErrMessageContains(err, ecs.ErrCodeUpdateInProgressException, "") {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if tfresource.TimedOut(err) {
_, err = conn.PutClusterCapacityProviders(&input)
}
err := retryClusterCapacityProvidersPut(context.Background(), conn, &input)

if err != nil {
return fmt.Errorf("error changing ECS cluster capacity provider settings (%s): %w", d.Id(), err)
}

if _, err := waitClusterAvailable(conn, d.Id()); err != nil {
if _, err := waitClusterAvailable(context.Background(), conn, d.Id()); err != nil {
return fmt.Errorf("error waiting for ECS Cluster (%s) to become Available: %w", d.Id(), err)
}
}
Expand Down
189 changes: 189 additions & 0 deletions internal/service/ecs/cluster_capacity_providers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package ecs

import (
"context"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"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/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
)

func ResourceClusterCapacityProviders() *schema.Resource {
return &schema.Resource{
CreateContext: resourceClusterCapacityProvidersPut,
ReadContext: resourceClusterCapacityProvidersRead,
UpdateContext: resourceClusterCapacityProvidersPut,
DeleteContext: resourceClusterCapacityProvidersDelete,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"capacity_providers": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"cluster_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
// The API accepts both an ARN and a name in a generic "cluster"
// parameter, but allowing that would force the resource to guess
// which one to return on read.
ValidateFunc: validateClusterName,
},
"default_capacity_provider_strategy": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"base": {
Type: schema.TypeInt,
Default: 0,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100000),
},
"capacity_provider": {
Type: schema.TypeString,
Required: true,
},
"weight": {
Type: schema.TypeInt,
Default: 0,
Optional: true,
ValidateFunc: validation.IntBetween(0, 1000),
},
},
},
},
},
}
}

func resourceClusterCapacityProvidersPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).ECSConn

clusterName := d.Get("cluster_name").(string)

input := &ecs.PutClusterCapacityProvidersInput{
Cluster: aws.String(clusterName),
CapacityProviders: flex.ExpandStringSet(d.Get("capacity_providers").(*schema.Set)),
DefaultCapacityProviderStrategy: expandCapacityProviderStrategy(d.Get("default_capacity_provider_strategy").(*schema.Set)),
}

log.Printf("[DEBUG] Updating ECS cluster capacity providers: %s", input)

err := retryClusterCapacityProvidersPut(ctx, conn, input)

if err != nil {
return diag.Errorf("error updating ECS Cluster (%s) capacity providers: %s", clusterName, err)
}

if _, err := waitClusterAvailable(ctx, conn, clusterName); err != nil {
return diag.Errorf("error waiting for ECS Cluster (%s) to become available: %s", clusterName, err)
}

d.SetId(clusterName)

return resourceClusterCapacityProvidersRead(ctx, d, meta)
}

func resourceClusterCapacityProvidersRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).ECSConn

cluster, err := FindClusterByNameOrARN(ctx, conn, d.Id())

if tfresource.NotFound(err) {
diag.Errorf("[WARN] ECS Cluster (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return diag.Errorf("error reading ECS Cluster (%s): %s", d.Id(), err)
}

// Status==INACTIVE means deleted cluster
if aws.StringValue(cluster.Status) == "INACTIVE" {
diag.Errorf("[WARN] ECS Cluster (%s) deleted, removing from state", d.Id())
d.SetId("")
return nil
}

if err := d.Set("capacity_providers", aws.StringValueSlice(cluster.CapacityProviders)); err != nil {
return diag.Errorf("error setting capacity_providers: %s", err)
}

d.Set("cluster_name", cluster.ClusterName)

if err := d.Set("default_capacity_provider_strategy", flattenCapacityProviderStrategy(cluster.DefaultCapacityProviderStrategy)); err != nil {
return diag.Errorf("error setting default_capacity_provider_strategy: %s", err)
}

return nil
}

func resourceClusterCapacityProvidersDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).ECSConn

input := &ecs.PutClusterCapacityProvidersInput{
Cluster: aws.String(d.Id()),
CapacityProviders: []*string{},
DefaultCapacityProviderStrategy: []*ecs.CapacityProviderStrategyItem{},
}

log.Printf("[DEBUG] Removing ECS cluster (%s) capacity providers", d.Id())

err := retryClusterCapacityProvidersPut(ctx, conn, input)

if tfawserr.ErrCodeEquals(err, ecs.ErrCodeClusterNotFoundException) {
return nil
}

if err != nil {
return diag.Errorf("error deleting ECS Cluster (%s) capacity providers: %s", d.Id(), err)
}

if _, err := waitClusterAvailable(ctx, conn, d.Id()); err != nil {
return diag.Errorf("error waiting for ECS Cluster (%s) to become available: %s", d.Id(), err)
}

return nil
}

func retryClusterCapacityProvidersPut(ctx context.Context, conn *ecs.ECS, input *ecs.PutClusterCapacityProvidersInput) error {
err := resource.RetryContext(ctx, ecsClusterTimeoutUpdate, func() *resource.RetryError {
_, err := conn.PutClusterCapacityProvidersWithContext(ctx, input)
if err != nil {
if tfawserr.ErrMessageContains(err, ecs.ErrCodeClientException, "Cluster was not ACTIVE") {
return resource.RetryableError(err)
}
if tfawserr.ErrMessageContains(err, ecs.ErrCodeResourceInUseException, "") {
return resource.RetryableError(err)
}
if tfawserr.ErrMessageContains(err, ecs.ErrCodeUpdateInProgressException, "") {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})

if tfresource.TimedOut(err) {
_, err = conn.PutClusterCapacityProvidersWithContext(ctx, input)
}

return err
}
Loading

0 comments on commit c47375e

Please sign in to comment.