Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/aws_ecs_capacity_provider: Implement API deletion support #13740

Merged
merged 1 commit into from
Jun 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions aws/internal/service/ecs/waiter/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package waiter

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

const (
// EventSubscription NotFound
CapacityProviderStatusNotFound = "NotFound"

// EventSubscription Unknown
CapacityProviderStatusUnknown = "Unknown"
)

// CapacityProviderStatus fetches the Capacity Provider and its Status
func CapacityProviderStatus(conn *ecs.ECS, capacityProvider string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &ecs.DescribeCapacityProvidersInput{
CapacityProviders: aws.StringSlice([]string{capacityProvider}),
}

output, err := conn.DescribeCapacityProviders(input)

if err != nil {
return nil, CapacityProviderStatusUnknown, err
}

if len(output.CapacityProviders) == 0 {
return nil, CapacityProviderStatusNotFound, nil
}

return output.CapacityProviders[0], aws.StringValue(output.CapacityProviders[0].Status), nil
}
}
31 changes: 31 additions & 0 deletions aws/internal/service/ecs/waiter/waiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package waiter

import (
"time"

"github.com/aws/aws-sdk-go/service/ecs"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

const (
// Maximum amount of time to wait for a Capacity Provider to return INACTIVE
CapacityProviderInactiveTimeout = 20 * time.Minute
)

// CapacityProviderInactive waits for a Capacity Provider to return INACTIVE
func CapacityProviderInactive(conn *ecs.ECS, capacityProvider string) (*ecs.CapacityProvider, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{ecs.CapacityProviderStatusActive},
Target: []string{ecs.CapacityProviderStatusInactive},
Refresh: CapacityProviderStatus(conn, capacityProvider),
Timeout: CapacityProviderInactiveTimeout,
}

outputRaw, err := stateConf.WaitForState()

if v, ok := outputRaw.(*ecs.CapacityProvider); ok {
return v, err
}

return nil, err
}
25 changes: 23 additions & 2 deletions aws/resource_aws_ecs_capacity_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/waiter"
)

func resourceAwsEcsCapacityProvider() *schema.Resource {
Expand Down Expand Up @@ -159,6 +160,12 @@ func resourceAwsEcsCapacityProviderRead(d *schema.ResourceData, meta interface{}
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
}

d.Set("arn", provider.CapacityProviderArn)
d.Set("name", provider.Name)

Expand Down Expand Up @@ -188,8 +195,22 @@ func resourceAwsEcsCapacityProviderUpdate(d *schema.ResourceData, meta interface
}

func resourceAwsEcsCapacityProviderDelete(d *schema.ResourceData, meta interface{}) error {
// Reference: https://github.com/aws/containers-roadmap/issues/632
log.Printf("[WARN] delete is not yet implemented for ECS capacity providers")
conn := meta.(*AWSClient).ecsconn

input := &ecs.DeleteCapacityProviderInput{
CapacityProvider: aws.String(d.Id()),
}

_, err := conn.DeleteCapacityProvider(input)

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 {
return fmt.Errorf("error waiting for ECS Capacity Provider (%s) to delete: %w", d.Id(), err)
}

return nil
}

Expand Down
133 changes: 131 additions & 2 deletions aws/resource_aws_ecs_capacity_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,100 @@ package aws

import (
"fmt"
"log"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ecs"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecs/waiter"
)

// TODO sweepers once Delete is implemented
func init() {
resource.AddTestSweepers("aws_ecs_capacity_provider", &resource.Sweeper{
Name: "aws_ecs_capacity_provider",
F: testSweepEcsCapacityProviders,
Dependencies: []string{
"aws_ecs_cluster",
"aws_ecs_service",
},
})
}

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

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
}

for _, capacityProvider := range output.CapacityProviders {
if capacityProvider == nil {
continue
}

arn := aws.StringValue(capacityProvider.CapacityProviderArn)
input := &ecs.DeleteCapacityProviderInput{
CapacityProvider: capacityProvider.CapacityProviderArn,
}

if aws.StringValue(capacityProvider.Name) == "FARGATE" || aws.StringValue(capacityProvider.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
}

log.Printf("[INFO] Deleting ECS Capacity Provider: %s", arn)
_, err := conn.DeleteCapacityProvider(input)

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
}

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 aws.StringValue(output.NextToken) == "" {
break
}

input.NextToken = output.NextToken
}

return sweeperErrs.ErrorOrNil()
}

func TestAccAWSEcsCapacityProvider_basic(t *testing.T) {
var provider ecs.CapacityProvider
Expand Down Expand Up @@ -49,6 +133,28 @@ func TestAccAWSEcsCapacityProvider_basic(t *testing.T) {
})
}

func TestAccAWSEcsCapacityProvider_disappears(t *testing.T) {
var provider ecs.CapacityProvider
rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_ecs_capacity_provider.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEcsCapacityProviderDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEcsCapacityProviderConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEcsCapacityProviderExists(resourceName, &provider),
testAccCheckResourceDisappears(testAccProvider, resourceAwsEcsCapacityProvider(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func TestAccAWSEcsCapacityProvider_ManagedScaling(t *testing.T) {
var provider ecs.CapacityProvider
rName := acctest.RandomWithPrefix("tf-acc-test")
Expand Down Expand Up @@ -163,7 +269,30 @@ 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 {
// Reference: https://github.com/aws/containers-roadmap/issues/632
conn := testAccProvider.Meta().(*AWSClient).ecsconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_ecs_capacity_provider" {
continue
}

input := &ecs.DescribeCapacityProvidersInput{
CapacityProviders: aws.StringSlice([]string{rs.Primary.ID}),
}

output, err := conn.DescribeCapacityProviders(input)

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 nil
}

Expand Down
2 changes: 0 additions & 2 deletions website/docs/r/ecs_capacity_provider.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ description: |-

Provides an ECS cluster capacity provider. More information can be found on the [ECS Developer Guide](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-capacity-providers.html).

~> **NOTE:** The AWS API does not currently support deleting ECS cluster capacity providers. Removing this Terraform resource will only remove the Terraform state for it.

## Example Usage

```hcl
Expand Down