Merge pull request #718 from docker/capacity_provider_shutdown

delete CapacityProvider before we invoke DeleteStack
This commit is contained in:
Nicolas De loof 2020-10-05 09:26:17 +02:00 committed by GitHub
commit 10824fe7c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 114 additions and 6 deletions

22
ecs/aws.go Normal file
View File

@ -0,0 +1,22 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ecs
const (
awsTypeCapacityProvider = "AWS::ECS::CapacityProvider"
awsTypeAutoscalingGroup = "AWS::AutoScaling::AutoScalingGroup"
)

View File

@ -18,12 +18,58 @@ package ecs
import (
"context"
"github.com/docker/compose-cli/progress"
)
func (b *ecsAPIService) Down(ctx context.Context, project string) error {
err := b.SDK.DeleteStack(ctx, project)
resources, err := b.SDK.ListStackResources(ctx, project)
if err != nil {
return err
}
return b.WaitStackCompletion(ctx, project, stackDelete)
err = resources.apply(awsTypeCapacityProvider, delete(ctx, b.SDK.DeleteCapacityProvider))
if err != nil {
return err
}
err = resources.apply(awsTypeAutoscalingGroup, delete(ctx, b.SDK.DeleteAutoscalingGroup))
if err != nil {
return err
}
previousEvents, err := b.previousStackEvents(ctx, project)
if err != nil {
return err
}
err = b.SDK.DeleteStack(ctx, project)
if err != nil {
return err
}
return b.WaitStackCompletion(ctx, project, stackDelete, previousEvents...)
}
func (b *ecsAPIService) previousStackEvents(ctx context.Context, project string) ([]string, error) {
events, err := b.SDK.DescribeStackEvents(ctx, project)
if err != nil {
return nil, err
}
var previousEvents []string
for _, e := range events {
previousEvents = append(previousEvents, *e.EventId)
}
return previousEvents, nil
}
func delete(ctx context.Context, delete func(ctx context.Context, arn string) error) func(r stackResource) error {
return func(r stackResource) error {
w := progress.ContextWriter(ctx)
w.Event(progress.Event{
ID: r.LogicalID,
Status: progress.Working,
StatusText: "DELETE_IN_PROGRESS",
})
return delete(ctx, r.ARN)
}
}

View File

@ -32,6 +32,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/autoscaling/autoscalingiface"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
@ -48,6 +50,7 @@ import (
"github.com/aws/aws-sdk-go/service/iam/iamiface"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface"
"github.com/hashicorp/go-multierror"
"github.com/sirupsen/logrus"
)
@ -61,6 +64,7 @@ type sdk struct {
CF cloudformationiface.CloudFormationAPI
SM secretsmanageriface.SecretsManagerAPI
SSM ssmiface.SSMAPI
AG autoscalingiface.AutoScalingAPI
}
func newSDK(sess *session.Session) sdk {
@ -77,6 +81,7 @@ func newSDK(sess *session.Session) sdk {
CF: cloudformation.New(sess),
SM: secretsmanager.New(sess),
SSM: ssm.New(sess),
AG: autoscaling.New(sess),
}
}
@ -364,7 +369,24 @@ type stackResource struct {
Status string
}
func (s sdk) ListStackResources(ctx context.Context, name string) ([]stackResource, error) {
type stackResourceFn func(r stackResource) error
type stackResources []stackResource
func (resources stackResources) apply(awsType string, fn stackResourceFn) error {
var errs *multierror.Error
for _, r := range resources {
if r.Type == awsType {
err := fn(r)
if err != nil {
errs = multierror.Append(err)
}
}
}
return errs.ErrorOrNil()
}
func (s sdk) ListStackResources(ctx context.Context, name string) (stackResources, error) {
// FIXME handle pagination
res, err := s.CF.ListStackResourcesWithContext(ctx, &cloudformation.ListStackResourcesInput{
StackName: aws.String(name),
@ -373,7 +395,7 @@ func (s sdk) ListStackResources(ctx context.Context, name string) ([]stackResour
return nil, err
}
resources := []stackResource{}
resources := stackResources{}
for _, r := range res.StackResourceSummaries {
resources = append(resources, stackResource{
LogicalID: aws.StringValue(r.LogicalResourceId),
@ -714,3 +736,18 @@ func (s sdk) SecurityGroupExists(ctx context.Context, sg string) (bool, error) {
}
return len(desc.SecurityGroups) > 0, nil
}
func (s sdk) DeleteCapacityProvider(ctx context.Context, arn string) error {
_, err := s.ECS.DeleteCapacityProvider(&ecs.DeleteCapacityProviderInput{
CapacityProvider: aws.String(arn),
})
return err
}
func (s sdk) DeleteAutoscalingGroup(ctx context.Context, arn string) error {
_, err := s.AG.DeleteAutoScalingGroup(&autoscaling.DeleteAutoScalingGroupInput{
AutoScalingGroupName: aws.String(arn),
ForceDelete: aws.Bool(true),
})
return err
}

View File

@ -28,8 +28,12 @@ import (
"github.com/aws/aws-sdk-go/aws"
)
func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, operation int) error { //nolint:gocyclo
func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, operation int, ignored ...string) error { //nolint:gocyclo
knownEvents := map[string]struct{}{}
for _, id := range ignored {
knownEvents[id] = struct{}{}
}
// progress writer
w := progress.ContextWriter(ctx)
// Get the unique Stack ID so we can collect events without getting some from previous deployments with same name
@ -78,7 +82,6 @@ func (b *ecsAPIService) WaitStackCompletion(ctx context.Context, name string, op
case "CREATE_COMPLETE":
if operation == stackCreate {
progressStatus = progress.Done
}
case "UPDATE_COMPLETE":
if operation == stackUpdate {