-
-
Notifications
You must be signed in to change notification settings - Fork 359
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
Add support for more resources #5
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great. Clear structure, obvious how to add more features, and nice tests 👍
} | ||
} | ||
|
||
func TestListAutoScalingGroups(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love the tests!
One issue: do the tests clean up after themselves? Or do they leave the instances / ASGs running? You should probably add a defer
to each test that runs the appropriate clean up functions you already have in this codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically I run aws-nuke to clean up the resources the tests leave around. I feel it's a good exercise of the functionality of the tool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, we'll be hooking up the tests to run automatically after every commit to the aws-nuke
repo, so we can't rely on manual cleanup. I'd rather have the tests make an effort to clean up after themselves and we'll have a scheduled run of aws-nuke in our AWS accounts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
List tests now cleanup after themselves
aws/asg_test.go
Outdated
assert.Fail(t, errors.WithStackTrace(err).Error()) | ||
} | ||
|
||
err = svc.WaitUntilGroupNotExists(&autoscaling.DescribeAutoScalingGroupsInput{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if the nukeAllAutoScalingGroups
should do this wait call itself? Otherwise, steps that run after it may not realize the ASGs are still in progress of being deleted, and therefore not work correctly. E.g., If we try to delete EBS volumes immediately after, the ASGs still using those volumes may not yet have terminated.
|
||
_, err := svc.DeleteVolume(params) | ||
if err != nil { | ||
// Ignore not found errors, some volumes are deleted along with EC2 Instances |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably still worth logging that "Volume XXX has already been deleted."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably also a good idea to add a log statement with the ID/name of each EBS volume, instance, ASG, etc. that gets nuked. That'll be very useful for debugging and seeing the progress of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logging has been updated to reflect this
aws/ebs_test.go
Outdated
func createTestEBSVolume(t *testing.T, session *session.Session, name string) ec2.Volume { | ||
svc := ec2.New(session) | ||
volume, err := svc.CreateVolume(&ec2.CreateVolumeInput{ | ||
AvailabilityZone: awsgo.String("us-west-2a"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just noticed that these tests all hard-code the region. The downside to this is if we have some region-specific bug, we won't catch it.
You can use this method to pick a random one.
aws/ebs_test.go
Outdated
// Retrive only IDs of instances with the unique test tag | ||
for _, tag := range volume.Tags { | ||
if *tag.Key == "Name" { | ||
if *tag.Value == name { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of a second level of nesting, you could combine the two if-statements: if *tag.Key == "Name" && *tag.Value == name
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I wonder if there is a chance tag.Key
or tag.Value
could be a nil pointer? Might be safer to use aws.StringValue
.
|
||
volumeIds := findEBSVolumesByNameTag(output, uniqueTestID) | ||
|
||
if err := nukeAllEbsVolumes(session, volumeIds); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this method wait for all volumes to be deleted? Or is it possible for it to return while deletion is still in progress, causing the next check to fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does now
aws/ec2.go
Outdated
@@ -85,6 +85,20 @@ func nukeAllEc2Instances(session *session.Session, instanceIds []*string) error | |||
return errors.WithStackTrace(err) | |||
} | |||
|
|||
err = svc.WaitUntilInstanceTerminated(&ec2.DescribeInstancesInput{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yea, this seems like a good addition. I'd recommend adding equivalent "wait until XXX deleted" to the EBS, ASG, and ELB code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been done for everything except classic ELB which doesn't have this functionality
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there's no built-in WaitUntilXXX
method, you can create your own! You just create a retry loop (see DoWithRetry), looking up the ELB by name until it no longer exists or max retries is exceeded.
assert.Contains(t, awsgo.StringValueSlice(elbNames), elbName) | ||
} | ||
|
||
func TestNukeELBs(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How long do the tests take to run? It seems like it would take a while.
You should probably add:
t.Parallel()
As the first line of each test. This will get them all to run in parallel. It will also flush out any concurrency bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests now run and pass concurrently
aws/elbv2_test.go
Outdated
assert.Failf(t, "Could not create test ELBv2: %s", errors.WithStackTrace(err).Error()) | ||
} | ||
|
||
balancer := *result.LoadBalancers[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this list be empty? Prob worth checking and returning an error if it is.
README.md
Outdated
@@ -19,6 +22,14 @@ The currently supported functionality includes: | |||
|
|||
Simply running `aws-nuke` will start the process of cleaning up your AWS account. You'll be shown a list of resources that'll be deleted as well as a prompt to confirm before any deletion actually takes place. | |||
|
|||
### Excluding Regions | |||
|
|||
You can use the `--exclude` or `-e` flag to exclude resources in certain regions from being deleted. For example the following command does not nuke resources in `ap-south-1` and `ap-south-2` regions: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to call it --exclude-region
, as I suspect we'll have other types of excludes in the future (e.g., --exclude-ec2-instances
).
aws/asg_test.go
Outdated
instance := createTestEC2Instance(t, session, name) | ||
|
||
// EC2 Instance must be in a running state before ASG can be created | ||
err := ec2.New(session).WaitUntilInstanceRunning(&ec2.DescribeInstancesInput{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the createTestEC2Instance
method do the waiting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah missed this. Thanks
aws/aws.go
Outdated
"github.com/gruntwork-io/gruntwork-cli/errors" | ||
) | ||
|
||
// Returns a list of all AWS regions | ||
func getAllRegions() []string { | ||
// chinese and government regions are not accessible with regular accounts | ||
reservedRegions := []string{ | ||
"cn-north-1", "cn-northwest-1", "us-gov-west-1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good catch
aws/aws.go
Outdated
account := AwsAccountResources{ | ||
Resources: make(map[string]AwsRegionResource), | ||
} | ||
|
||
for _, region := range getAllRegions() { | ||
// Ignore all cli excluded regions | ||
if collections.ListContainsElement(excludedRegions, region) { | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be a good idea to log either "Processing region XXX" or "Skipping region XXX".
aws/aws.go
Outdated
@@ -39,6 +62,46 @@ func GetAllResources() (*AwsAccountResources, error) { | |||
|
|||
resourcesInRegion := AwsRegionResource{} | |||
|
|||
// ASG Names | |||
groupNames, err := getAllAutoScalingGroups(session, region) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to add a comment near the top of this method that the order of these resources matters due to dependencies between them: e.g., ASGs first, then EC2 instances, then EBS volumes.
|
||
_, err := svc.DeleteVolume(params) | ||
if err != nil { | ||
// Ignore not found errors, some volumes are deleted along with EC2 Instances |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably also a good idea to add a log statement with the ID/name of each EBS volume, instance, ASG, etc. that gets nuked. That'll be very useful for debugging and seeing the progress of the code.
aws/ebs_test.go
Outdated
func findEBSVolumesByNameTag(output *ec2.DescribeVolumesOutput, name string) []*string { | ||
var volumeIds []*string | ||
for _, volume := range output.Volumes { | ||
// Retrive only IDs of instances with the unique test tag |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Retrive/Retrieve/
aws/ec2_test.go
Outdated
rand := rand.New(rand.NewSource(time.Now().UnixNano())) | ||
for i := 0; i < UNIQUE_ID_LENGTH; i++ { | ||
out.WriteByte(BASE_62_CHARS[rand.Intn(len(BASE_62_CHARS))]) | ||
func getLinuxAmiIDForRegion(region string) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be a pain to maintain as regions are added or AMIs removed. You should be able to find the AMI automatically using Amazon's APIs, similar to Packer's source_ami_filter.
commands/cli.go
Outdated
@@ -29,7 +35,7 @@ func CreateCli(version string) *cli.App { | |||
func awsNuke(c *cli.Context) error { | |||
logging.Logger.Infoln("Retrieving all active AWS resources") | |||
|
|||
account, err := aws.GetAllResources() | |||
account, err := aws.GetAllResources(c.StringSlice("exclude")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should validate the list of regions passed in to ensure each one corresponds to a valid AWS region. We don't want to blow away a region because someone made a typo (e.g., us-wes-1).
aws/asg_test.go
Outdated
assert.Contains(t, awsgo.StringValueSlice(groupNames), groupName) | ||
|
||
// clean up after this test | ||
defer nukeAllAutoScalingGroups(session, []*string{&groupName}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should go immediately after the createTestAutoScalingGroup
call. Otherwise, one of the assert
calls may fail and prevent the code from ever executing this cleanup step.
aws/ebs_test.go
Outdated
assert.Contains(t, awsgo.StringValueSlice(volumeIds), awsgo.StringValue(volume.VolumeId)) | ||
|
||
// clean up after this test | ||
defer nukeAllEbsVolumes(session, []*string{volume.VolumeId}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about defer
here and any other test cases.
aws/ec2_test.go
Outdated
if err != nil { | ||
assert.Failf(t, "Could not create test EC2 instance: %s", errors.WithStackTrace(err).Error()) | ||
if err != nil || len(runResult.Instances) == 0 { | ||
assert.Fail(t, "Could not create test EC2 instance") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to include err
and runResult.Instances
in the log statement so we know what the issue was.
aws/elb.go
Outdated
for i := 0; i < 30; i++ { | ||
_, err := svc.DescribeLoadBalancers(input) | ||
if err != nil { | ||
// an error is returned when ELB no longer exists |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to check which error! This could be a 500 error or 404 or 403 or a dozen other things, most of which don't mean the ELB is gone. See here for how to check AWS errors.
aws/elb.go
Outdated
return nil | ||
} | ||
|
||
time.Sleep(1 * time.Second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Log here (debug) that you're waiting for the EBS volume to be deleted so it's clear what's going on.
aws/elb.go
Outdated
time.Sleep(1 * time.Second) | ||
} | ||
|
||
panic("ELBs failed to delete") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return an error. Don't panic.
commands/cli.go
Outdated
for _, excludedRegion := range excludedRegions { | ||
if !collections.ListContainsElement(regions, excludedRegion) { | ||
fmt.Println(excludedRegion + "is not a valid AWS Region") | ||
return InvalidFlagError{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of the error message in a println and a separate error with no info, update InvalidFlagError
to have the flag name and value as fields and to have an Error()
method that clearly shows them to the user.
commands/cli.go
Outdated
|
||
account, err := aws.GetAllResources() | ||
fmt.Println("Retrieving all active AWS resources") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the logger rather than fmt.Println
. Applies everywhere in this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic :-D
Adds support for nuking: