Skip to content

Commit

Permalink
Merge pull request #16936 from shuheiktgw/ecs_service_deployment_circ…
Browse files Browse the repository at this point in the history
…uit_breaker

resource/aws_ecs_service: Add support for deployment circuit breaker
  • Loading branch information
YakDriver authored Mar 27, 2021
2 parents d76c0dc + 4246861 commit d0fb8dd
Show file tree
Hide file tree
Showing 8 changed files with 1,148 additions and 1,083 deletions.
7 changes: 7 additions & 0 deletions .changelog/16936.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_ecs_service: Add `deployment_circuit_breaker`
```

```release-note:bug
resource/aws_ecs_service: Improve handling of eventual consistency including security group dependency violations on deletion
```
37 changes: 37 additions & 0 deletions aws/internal/service/ecs/waiter/status.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package waiter

import (
"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/helper/resource"
)

Expand All @@ -12,6 +15,14 @@ const (

// EventSubscription Unknown
CapacityProviderStatusUnknown = "Unknown"

// AWS will likely add consts for these at some point
ServiceStatusInactive = "INACTIVE"
ServiceStatusActive = "ACTIVE"
ServiceStatusDraining = "DRAINING"

ServiceStatusError = "ERROR"
ServiceStatusNone = "NONE"
)

// CapacityProviderStatus fetches the Capacity Provider and its Status
Expand All @@ -34,3 +45,29 @@ func CapacityProviderStatus(conn *ecs.ECS, capacityProvider string) resource.Sta
return output.CapacityProviders[0], aws.StringValue(output.CapacityProviders[0].Status), nil
}
}

func ServiceStatus(conn *ecs.ECS, id, cluster string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &ecs.DescribeServicesInput{
Services: aws.StringSlice([]string{id}),
Cluster: aws.String(cluster),
}

output, err := conn.DescribeServices(input)

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

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

if len(output.Services) == 0 {
return nil, ServiceStatusNone, nil
}

log.Printf("[DEBUG] ECS service (%s) is currently %q", id, *output.Services[0].Status)
return output, aws.StringValue(output.Services[0].Status), err
}
}
69 changes: 69 additions & 0 deletions aws/internal/service/ecs/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ package waiter
import (
"time"

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

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

ServiceCreateTimeout = 2 * time.Minute
ServiceInactiveTimeout = 10 * time.Minute
ServiceInactiveTimeoutMin = 1 * time.Second
ServiceDescribeTimeout = 2 * time.Minute
ServiceUpdateTimeout = 2 * time.Minute
)

// CapacityProviderInactive waits for a Capacity Provider to return INACTIVE
Expand All @@ -29,3 +36,65 @@ func CapacityProviderInactive(conn *ecs.ECS, capacityProvider string) (*ecs.Capa

return nil, err
}

func ServiceStable(conn *ecs.ECS, id, cluster string) error {
input := &ecs.DescribeServicesInput{
Services: aws.StringSlice([]string{id}),
}

if cluster != "" {
input.Cluster = aws.String(cluster)
}

if err := conn.WaitUntilServicesStable(input); err != nil {
return err
}
return nil
}

func ServiceInactive(conn *ecs.ECS, id, cluster string) error {
input := &ecs.DescribeServicesInput{
Services: aws.StringSlice([]string{id}),
}

if cluster != "" {
input.Cluster = aws.String(cluster)
}

if err := conn.WaitUntilServicesInactive(input); err != nil {
return err
}

stateConf := &resource.StateChangeConf{
Pending: []string{ServiceStatusActive, ServiceStatusDraining},
Target: []string{ServiceStatusInactive, ServiceStatusNone},
Refresh: ServiceStatus(conn, id, cluster),
Timeout: ServiceInactiveTimeout,
MinTimeout: ServiceInactiveTimeoutMin,
}

_, err := stateConf.WaitForState()

if err != nil {
return err
}

return nil
}

func ServiceDescribeReady(conn *ecs.ECS, id, cluster string) (*ecs.DescribeServicesOutput, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{ServiceStatusInactive, ServiceStatusDraining, ServiceStatusNone},
Target: []string{ServiceStatusActive},
Refresh: ServiceStatus(conn, id, cluster),
Timeout: ServiceDescribeTimeout,
}

outputRaw, err := stateConf.WaitForState()

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

return nil, err
}
Loading

0 comments on commit d0fb8dd

Please sign in to comment.