Skip to content

Commit

Permalink
Merge pull request #36 from gruntwork-io/yori-nuke-ecs
Browse files Browse the repository at this point in the history
Nuke ECS services and ECS clusters
  • Loading branch information
yorinasub17 authored Oct 25, 2018
2 parents de8f49c + 5487ffd commit 4c5838a
Show file tree
Hide file tree
Showing 7 changed files with 858 additions and 26 deletions.
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

0 comments on commit 4c5838a

Please sign in to comment.