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

Nuke ECS services and ECS clusters #36

Merged
merged 4 commits into from
Oct 25, 2018
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ The currently supported functionality includes:
* Deleting all Snapshots in an AWS account
* Deleting all Elastic IPs in an AWS account
* Deleting all Launch Configurations in an AWS account
* Deleting all ECS services in an AWS account

### Caveats

* We currently do not support deleting ECS clusters because AWS
does not give us a good way to blacklist clusters off the list (there are not
tags and we do not know the creation timestamp). Given the destructive nature
of the tool, we have opted not to support deleting ECS clusters at the
moment. See https://github.com/gruntwork-io/cloud-nuke/pull/36 for a more
detailed discussion.

## Azure

Expand Down
17 changes: 17 additions & 0 deletions aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,23 @@ func GetAllResources(regions []string, excludedRegions []string, excludeAfter ti
resourcesInRegion.Resources = append(resourcesInRegion.Resources, snapshots)
// End Snapshots

// ECS resources
clusterArns, err := getAllEcsClusters(session)
if err != nil {
return nil, errors.WithStackTrace(err)
}
serviceArns, serviceClusterMap, err := getAllEcsServices(session, clusterArns, excludeAfter)
if err != nil {
return nil, errors.WithStackTrace(err)
}

ecsServices := ECSServices{
Services: awsgo.StringValueSlice(serviceArns),
ServiceClusterMap: serviceClusterMap,
}
resourcesInRegion.Resources = append(resourcesInRegion.Resources, ecsServices)
// End ECS resources

account.Resources[region] = resourcesInRegion
}

Expand Down
74 changes: 48 additions & 26 deletions aws/ec2_test.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,50 @@
package aws

import (
"errors"
"testing"
"time"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/gruntwork-io/cloud-nuke/util"
"github.com/gruntwork-io/gruntwork-cli/errors"
gruntworkerrors "github.com/gruntwork-io/gruntwork-cli/errors"
"github.com/stretchr/testify/assert"
)

func createTestEC2Instance(t *testing.T, session *session.Session, name string, protected bool) ec2.Instance {
svc := ec2.New(session)

// getAMIIdByName - Retrieves an AMI ImageId given the name of the Id. Used for
// retrieving a standard AMI across AWS regions.
func getAMIIdByName(svc *ec2.EC2, name string) (string, error) {
imagesResult, err := svc.DescribeImages(&ec2.DescribeImagesInput{
Owners: []*string{awsgo.String("self"), awsgo.String("amazon")},
Filters: []*ec2.Filter{
&ec2.Filter{
Name: awsgo.String("name"),
Values: []*string{awsgo.String("amzn-ami-hvm-2017.09.1.20180115-x86_64-gp2")},
Values: []*string{awsgo.String(name)},
},
},
})

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
return "", gruntworkerrors.WithStackTrace(err)
}

imageID := *imagesResult.Images[0].ImageId

params := &ec2.RunInstancesInput{
ImageId: awsgo.String(imageID),
InstanceType: awsgo.String("t2.micro"),
MinCount: awsgo.Int64(1),
MaxCount: awsgo.Int64(1),
DisableApiTermination: awsgo.Bool(protected),
}
return *imagesResult.Images[0].ImageId, nil
}

// runAndWaitForInstance - Given a preconstructed ec2.RunInstancesInput object,
// make the API call to run the instance and then wait for the instance to be
// up and running before returning.
func runAndWaitForInstance(svc *ec2.EC2, name string, params *ec2.RunInstancesInput) (ec2.Instance, error) {
runResult, err := svc.RunInstances(params)
if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
return ec2.Instance{}, gruntworkerrors.WithStackTrace(err)
}

if len(runResult.Instances) == 0 {
assert.Fail(t, "Could not create test EC2 instance in "+*session.Config.Region)
err := errors.New("Could not create test EC2 instance")
return ec2.Instance{}, gruntworkerrors.WithStackTrace(err)
}

err = svc.WaitUntilInstanceExists(&ec2.DescribeInstancesInput{
Expand All @@ -58,7 +57,7 @@ func createTestEC2Instance(t *testing.T, session *session.Session, name string,
})

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
return ec2.Instance{}, gruntworkerrors.WithStackTrace(err)
}

// Add test tag to the created instance
Expand All @@ -73,7 +72,7 @@ func createTestEC2Instance(t *testing.T, session *session.Session, name string,
})

if err != nil {
assert.Failf(t, "Could not tag EC2 instance", errors.WithStackTrace(err).Error())
return ec2.Instance{}, gruntworkerrors.WithStackTrace(err)
}

// EC2 Instance must be in a running before this function returns
Expand All @@ -87,10 +86,33 @@ func createTestEC2Instance(t *testing.T, session *session.Session, name string,
})

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
return ec2.Instance{}, gruntworkerrors.WithStackTrace(err)
}

return *runResult.Instances[0], nil

}

func createTestEC2Instance(t *testing.T, session *session.Session, name string, protected bool) ec2.Instance {
svc := ec2.New(session)

imageID, err := getAMIIdByName(svc, "amzn-ami-hvm-2017.09.1.20180115-x86_64-gp2")
if err != nil {
assert.Fail(t, err.Error())
}

return *runResult.Instances[0]
params := &ec2.RunInstancesInput{
ImageId: awsgo.String(imageID),
InstanceType: awsgo.String("t2.micro"),
MinCount: awsgo.Int64(1),
MaxCount: awsgo.Int64(1),
DisableApiTermination: awsgo.Bool(protected),
}
instance, err := runAndWaitForInstance(svc, name, params)
if err != nil {
assert.Fail(t, err.Error())
}
return instance
}

func removeEC2InstanceProtection(svc *ec2.EC2, instance *ec2.Instance) error {
Expand All @@ -108,7 +130,7 @@ func removeEC2InstanceProtection(svc *ec2.EC2, instance *ec2.Instance) error {
func findEC2InstancesByNameTag(t *testing.T, session *session.Session, name string) []*string {
output, err := ec2.New(session).DescribeInstances(&ec2.DescribeInstancesInput{})
if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
assert.Fail(t, gruntworkerrors.WithStackTrace(err).Error())
}

var instanceIds []*string
Expand Down Expand Up @@ -140,7 +162,7 @@ func TestListInstances(t *testing.T) {
)

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
assert.Fail(t, gruntworkerrors.WithStackTrace(err).Error())
}

uniqueTestID := "cloud-nuke-test-" + util.UniqueID()
Expand All @@ -166,7 +188,7 @@ func TestListInstances(t *testing.T) {
assert.NotContains(t, instanceIds, protectedInstance.InstanceId)

if err = removeEC2InstanceProtection(ec2.New(session), &protectedInstance); err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
assert.Fail(t, gruntworkerrors.WithStackTrace(err).Error())
}
}

Expand All @@ -179,7 +201,7 @@ func TestNukeInstances(t *testing.T) {
)

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
assert.Fail(t, gruntworkerrors.WithStackTrace(err).Error())
}

uniqueTestID := "cloud-nuke-test-" + util.UniqueID()
Expand All @@ -188,7 +210,7 @@ func TestNukeInstances(t *testing.T) {
instanceIds := findEC2InstancesByNameTag(t, session, uniqueTestID)

if err := nukeAllEc2Instances(session, instanceIds); err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
assert.Fail(t, gruntworkerrors.WithStackTrace(err).Error())
}
instances, err := getAllEc2Instances(session, region, time.Now().Add(1*time.Hour))

Expand Down
Loading