Skip to content

Commit

Permalink
Merge pull request #51 from trussworks/cblkwell/packer-janitor
Browse files Browse the repository at this point in the history
packer janitor
  • Loading branch information
cblkwell authored Jun 26, 2019
2 parents e303bf9 + 7e27617 commit 5fdb71c
Show file tree
Hide file tree
Showing 13 changed files with 616 additions and 38 deletions.
3 changes: 1 addition & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ jobs:
- v1-vendor-{{ checksum "Gopkg.lock" }}
- run: echo 'export PATH=${PATH}:~/go/bin' >> $BASH_ENV
- run: go get -u github.com/golang/dep/cmd/dep
- run: go get -u github.com/alecthomas/gometalinter
- run: gometalinter --install
- run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.17.1
- run: mkdir -p "${TEST_RESULTS}"
- run: make all
- save_cache:
Expand Down
42 changes: 42 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
linters-settings:
govet:
check-shadowing: true
settings:
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf

linters:
enable:
- gosec
- golint
- gofmt
- goimports
- govet
- varcheck
- typecheck
- structcheck
- deadcode
disable:
- unused #deprecated https://github.com/dominikh/go-tools/tree/master/cmd/unused
- errcheck #requires patching code
- gosimple # 20+ files need to be patched
- ineffassign # 20+ files need to be patched
- staticcheck # 30+files need to be patched
linters-settings:
govet:
check-shadowing: false # Disabling; a couple things fail this, and it's not urgent to fix
issues:
fix: true
run:
# timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 8m
concurrency: 1

# which dirs to skip: they won't be analyzed;
skip-dirs:
- pkg/gen
- mocks
9 changes: 5 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ repos:
- id: pretty-format-json
args:
- --autofix
- repo: git://github.com/dnephin/pre-commit-golang
sha: v0.2
- repo: git://github.com/golangci/golangci-lint
rev: v1.17.1
hooks:
- id: go-fmt
- id: gometalinter
- id: golangci-lint
entry: golangci-lint run --verbose
verbose: true
- repo: git://github.com/igorshubovych/markdownlint-cli
sha: v0.5.0
hooks:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
SHELL = /bin/sh
VERSION = 2.7
VERSION = 2.8

all: install

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ AWS tools that come in handy.
| trusted-advisor-refresh | triggers a refresh of Trusted Advisor because AWS doesn't do this for you. | Yes |
| aws-health-notifier | Sends notifcations to a Slack webhook when AWS Health Events (read AWS outage) are triggered | Yes |
| ami-cleaner | Deregisters AMIs and deletes associated snapshots based on name/tag/age | Yes |
| packer-janitor | Removes abandoned Packer instances and their associated keypairs and security groups. | Yes |

## Installation

Expand Down Expand Up @@ -63,7 +64,6 @@ make S3_BUCKET=your-s3-bucket lambda_release
* ebs volume snapshot deleter (all snaps older than x days, support keep tags)
* redshift snapshot cleaner
* automatic filesystem resizer (use case: you can make EBS volumes larger, but if you do, you still have to go in and run resize2fs (or whatever). Why not just do this at boot always?
* Packer debris cleaner (old instances, security groups, etc)
* AWS id lookup (ie, figure out from the id which describe API to call, and do it).
* ebs snapshot creator (for all EBS volumes, trigger a snapshot).
* Something that will pull AWS Bucket Inventory data (AWS ships it as an Athena or Hive compatible format, so you need to read a manifest.json and then pull a set of CSV or ORC files).
2 changes: 1 addition & 1 deletion bin/make-lambda-build
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ cleanup() {
trap "cleanup" EXIT INT

readonly build_dir=$(mktemp -d)
readonly lambda_tools="rds-snapshot-cleaner trusted-advisor-refresh iam-keys-check rds-cloudwatch-logs aws-health-notifier ami-cleaner"
readonly lambda_tools="rds-snapshot-cleaner trusted-advisor-refresh iam-keys-check rds-cloudwatch-logs aws-health-notifier ami-cleaner packer-janitor"
mkdir -p "$build_dir"

for cmd in $lambda_tools
Expand Down
4 changes: 2 additions & 2 deletions bin/prereqs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ function has() {
if [[ $(uname -s) = Darwin ]]; then
has dep "brew install dep"
has pre-commit "brew install pre-commit"
has gometalinter "go get -u github.com/alecthomas/gometalinter"
has golangci-lint "go get -u github.com/golangci/golangci-lint/cmd/golangci-lint"
has shellcheck "brew install shellcheck"
else
has dep "go get -u github.com/golang/dep/cmd/dep"
has pre-commit "pip install pre-commit"
has gometalinter "go get -u github.com/alecthomas/gometalinter"
has golangci-lint "go get -u github.com/golangci/golangci-lint/cmd/golangci-lint"
has shellcheck "apt-get install shellcheck"
fi

Expand Down
117 changes: 117 additions & 0 deletions cmd/packer-janitor/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"github.com/trussworks/truss-aws-tools/internal/aws/session"
"github.com/trussworks/truss-aws-tools/pkg/packerjanitor"

"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/service/ec2"
flag "github.com/jessevdk/go-flags"
"go.uber.org/zap"

"log"
"time"
)

// Options describes the command line options available.
type Options struct {
Delete bool `short:"D" long:"delete" env:"DELETE" description:"Actually purge AWS resources (runs in dryrun mode by default)."`
Lambda bool `long:"lambda" env:"LAMBDA" required:"false" description:"Run as an AWS Lambda function."`
TimeLimit int `short:"t" long:"timelimit" default:"4" env:"TIMELIMIT" description:"Number of hours after which Packer resources should be considered abandoned."`
Profile string `short:"p" long:"profile" env:"AWS_PROFILE" required:"false" description:"The AWS profile to use."`
Region string `short:"r" long:"region" env:"AWS_REGION" required:"false" description:"The AWS region to use."`
}

var options Options
var logger *zap.Logger

// makeEC2Client establishes our session with AWS.
func makeEC2Client(region, profile string) *ec2.EC2 {
sess := session.MustMakeSession(region, profile)
ec2Client := ec2.New(sess)
return ec2Client
}

// cleanPackerResources is where the work is being done here.
func cleanPackerResources() {
now := time.Now().UTC()

p := packerjanitor.PackerClean{
Delete: options.Delete,
ExpirationDate: now.Add(time.Hour * time.Duration(-options.TimeLimit)),
Logger: logger,
EC2Client: makeEC2Client(options.Region, options.Profile),
}

// First, we get the list of instances that fulfills our
// requirements from EC2.
packerInstanceList, err := p.GetPackerInstances()
if err != nil {
logger.Fatal("unable to get list of Packer instances",
zap.Error(err),
)
}

// Now, for each instance, we want to purge it and its associated
// resources. First, let's check to see if the list is empty; if
// it is, we can just skip the rest.
if len(packerInstanceList) == 0 {
logger.Info("No abandoned Packer instances found.")
} else {
for _, instance := range packerInstanceList {
err := p.PurgePackerResource(instance)
if err != nil {
logger.Fatal("Failed to purge Packer instance and associated resources",
zap.String("instance-id", *instance.InstanceId),
zap.String("keyname", *instance.KeyName),
zap.String("securitygroup-id", *instance.SecurityGroups[0].GroupId),
zap.Error(err),
)
}
// If we didn't error out, it worked! Log our
// success.
if p.Delete {
logger.Info("Successfully purged Packer instance and associated resources",
zap.String("instance-id", *instance.InstanceId),
zap.String("keyname", *instance.KeyName),
zap.String("securitygroup-id", *instance.SecurityGroups[0].GroupId),
)
} else {
logger.Info("Would have purged Packer instance and associated resources",
zap.String("instance-id", *instance.InstanceId),
zap.String("keyname", *instance.KeyName),
zap.String("securitygroup-id", *instance.SecurityGroups[0].GroupId),
)
}
}
}

}

func lambdaHandler() {
lambda.Start(cleanPackerResources)
}

func main() {
// First, parse out our command line options:
parser := flag.NewParser(&options, flag.Default)
_, err := parser.Parse()
if err != nil {
log.Fatalf("could not parse options: %v", err)
}

// Initialize the zap logger:
logger, err = zap.NewProduction()
if err != nil {
log.Fatalf("could not initialize zap logger: %v", err)
}

// Last thing -- see if we were called as a Lambda function.
if options.Lambda {
logger.Info("Running Lambda handler.")
lambdaHandler()
} else {
cleanPackerResources()
}

}
4 changes: 2 additions & 2 deletions cmd/s3-bucket-size/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,11 @@ func makeGetMetricStatisticsInputForSize(bucket string, storageType cloudWatchSt
// get.
startTime := now.Add(-time.Duration(86400*3) * time.Second)
d := []*cloudwatch.Dimension{
&cloudwatch.Dimension{
{
Name: aws.String("BucketName"),
Value: &bucket,
},
&cloudwatch.Dimension{
{
Name: aws.String("StorageType"),
Value: &storageTypeString,
},
Expand Down
11 changes: 0 additions & 11 deletions gometalinter.json

This file was deleted.

28 changes: 14 additions & 14 deletions pkg/amiclean/ami_cleaner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ var newMasterImage = &ec2.Image{
ImageId: aws.String("ami-11111111111111111"),
CreationDate: aws.String("2019-03-31T21:04:57.000Z"),
Tags: []*ec2.Tag{
&ec2.Tag{Key: aws.String("Branch"), Value: aws.String("master")},
&ec2.Tag{Key: aws.String("Name"), Value: aws.String("newMasterImage")},
{Key: aws.String("Branch"), Value: aws.String("master")},
{Key: aws.String("Name"), Value: aws.String("newMasterImage")},
},
BlockDeviceMappings: []*ec2.BlockDeviceMapping{
&ec2.BlockDeviceMapping{
{
DeviceName: aws.String("/dev/xvda"),
Ebs: &ec2.EbsBlockDevice{
SnapshotId: aws.String("snap-11111111111111111"),
Expand All @@ -35,17 +35,17 @@ var newishDevImage = &ec2.Image{
ImageId: aws.String("ami-22222222222222222"),
CreationDate: aws.String("2019-03-30T21:04:57.000Z"),
Tags: []*ec2.Tag{
&ec2.Tag{Key: aws.String("Branch"), Value: aws.String("development")},
&ec2.Tag{Key: aws.String("Name"), Value: aws.String("newishDevImage")},
{Key: aws.String("Branch"), Value: aws.String("development")},
{Key: aws.String("Name"), Value: aws.String("newishDevImage")},
},
BlockDeviceMappings: []*ec2.BlockDeviceMapping{
&ec2.BlockDeviceMapping{
{
DeviceName: aws.String("/dev/xvda"),
Ebs: &ec2.EbsBlockDevice{
SnapshotId: aws.String("snap-22222222222222222"),
},
},
&ec2.BlockDeviceMapping{
{
DeviceName: aws.String("/dev/xvdb"),
Ebs: &ec2.EbsBlockDevice{
SnapshotId: aws.String("snap-22222222222222223"),
Expand All @@ -61,12 +61,12 @@ var oldDevImage = &ec2.Image{
ImageId: aws.String("ami-33333333333333333"),
CreationDate: aws.String("2019-03-01T21:04:57.000Z"),
Tags: []*ec2.Tag{
&ec2.Tag{Key: aws.String("Name"), Value: aws.String("oldDevImage")},
&ec2.Tag{Key: aws.String("Branch"), Value: aws.String("development")},
&ec2.Tag{Key: aws.String("Foozle"), Value: aws.String("Fizzbin")},
{Key: aws.String("Name"), Value: aws.String("oldDevImage")},
{Key: aws.String("Branch"), Value: aws.String("development")},
{Key: aws.String("Foozle"), Value: aws.String("Fizzbin")},
},
BlockDeviceMappings: []*ec2.BlockDeviceMapping{
&ec2.BlockDeviceMapping{
{
DeviceName: aws.String("/dev/xvda"),
Ebs: &ec2.EbsBlockDevice{
SnapshotId: aws.String("snap-33333333333333333"),
Expand All @@ -82,9 +82,9 @@ var noEbsImage = &ec2.Image{
ImageId: aws.String("ami-44444444444444444"),
CreationDate: aws.String("2019-03-01T21:04:57.000Z"),
Tags: []*ec2.Tag{
&ec2.Tag{Key: aws.String("Name"), Value: aws.String("noEbsImage")},
&ec2.Tag{Key: aws.String("Branch"), Value: aws.String("experimental")},
&ec2.Tag{Key: aws.String("Foozle"), Value: aws.String("Whatsit")},
{Key: aws.String("Name"), Value: aws.String("noEbsImage")},
{Key: aws.String("Branch"), Value: aws.String("experimental")},
{Key: aws.String("Foozle"), Value: aws.String("Whatsit")},
},
RootDeviceType: aws.String("instance-store"),
}
Expand Down
Loading

0 comments on commit 5fdb71c

Please sign in to comment.