Skip to content

Commit

Permalink
filter VPCs by name (#291)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonstrohmeyer committed Mar 17, 2022
1 parent 7ab9690 commit 1c5eb51
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 72 deletions.
2 changes: 1 addition & 1 deletion aws/ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ func NukeDefaultSecurityGroupRules(sgs []DefaultSecurityGroup) error {
return nil
}

// Given an map of tags, return the value of the Name tag
// Given an slice of tags, return the value of the Name tag
func GetEC2ResourceNameTagValue(tags []*ec2.Tag) (string, error) {
t := make(map[string]string)

Expand Down
36 changes: 21 additions & 15 deletions aws/ec2_vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package aws
import (
"time"

"github.com/aws/aws-sdk-go/aws"
awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
Expand Down Expand Up @@ -68,7 +67,21 @@ func getAllVpcs(session *session.Session, region string, excludeAfter time.Time,
var ids []*string
var vpcs []Vpc
for _, vpc := range result.Vpcs {
if shouldIncludeVpc(svc, vpc, excludeAfter, configObj) {
firstSeenTime, err := getFirstSeenVpcTag(*vpc, firstSeenTagKey)
if err != nil {
logging.Logger.Error("Unable to retrieve tags")
return nil, nil, errors.WithStackTrace(err)
}

if firstSeenTime == nil {
now := time.Now().UTC()
firstSeenTime = &now
if err := setFirstSeenVpcTag(svc, *vpc, firstSeenTagKey, time.Now().UTC()); err != nil {
return nil, nil, err
}
}

if shouldIncludeVpc(vpc, excludeAfter, *firstSeenTime, configObj) {
ids = append(ids, vpc.VpcId)

vpcs = append(vpcs, Vpc{
Expand All @@ -82,28 +95,21 @@ func getAllVpcs(session *session.Session, region string, excludeAfter time.Time,
return ids, vpcs, nil
}

func shouldIncludeVpc(svc *ec2.EC2, vpc *ec2.Vpc, excludeAfter time.Time, configObj config.Config) bool {
func shouldIncludeVpc(vpc *ec2.Vpc, excludeAfter time.Time, firstSeenTime time.Time, configObj config.Config) bool {
if vpc == nil {
return false
}

firstSeenTime, err := getFirstSeenVpcTag(*vpc, firstSeenTagKey)
if err != nil {
logging.Logger.Error("Unable to retrieve tags")
if excludeAfter.Before(firstSeenTime) {
return false
}

if firstSeenTime == nil {
setFirstSeenVpcTag(svc, *vpc, firstSeenTagKey, time.Now().UTC())
return false
}

if excludeAfter.Before(*firstSeenTime) {
return false
}
// If Name is unset, GetEC2ResourceNameTagValue returns error and zero value string
// Ignore this error and pass empty string to config.ShouldInclude
vpcName, _ := GetEC2ResourceNameTagValue(vpc.Tags)

return config.ShouldInclude(
aws.StringValue(vpc.VpcId),
vpcName,
configObj.VPC.IncludeRule.NamesRegExp,
configObj.VPC.ExcludeRule.NamesRegExp,
)
Expand Down
144 changes: 88 additions & 56 deletions aws/ec2_vpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/cloud-nuke/logging"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -115,62 +116,6 @@ func TestListVpcs(t *testing.T) {
assert.Contains(t, awsgo.StringValueSlice(vpcIds), vpcId)
}

func TestListVpcsWithConfigFile(t *testing.T) {
t.Parallel()

region, err := getRandomRegion()
require.NoError(t, err)

session, err := session.NewSession(&awsgo.Config{
Region: awsgo.String(region)},
)

require.NoError(t, err)

includedVpcId := createTestVpc(t, session)
excludedVpcId := createTestVpc(t, session)

// clean up after this test
defer nukeAllVPCs(session, []string{includedVpcId, excludedVpcId}, []Vpc{{
Region: region,
VpcId: includedVpcId,
svc: ec2.New(session),
}, {
Region: region,
VpcId: excludedVpcId,
svc: ec2.New(session),
}})

// First run gives us a chance to tag the VPC
_, _, err = getAllVpcs(session, region, time.Now().Add(1*time.Hour), config.Config{
VPC: config.ResourceType{
IncludeRule: config.FilterRule{
NamesRegExp: []config.Expression{
{RE: *regexp.MustCompile(includedVpcId)},
},
},
},
})

require.NoError(t, err)

// VPC should be tagged at this point
vpcIds, _, err := getAllVpcs(session, region, time.Now().Add(1*time.Hour), config.Config{
VPC: config.ResourceType{
IncludeRule: config.FilterRule{
NamesRegExp: []config.Expression{
{RE: *regexp.MustCompile(includedVpcId)},
},
},
},
})

require.NoError(t, err)

require.Equal(t, 1, len(vpcIds))
assert.Contains(t, awsgo.StringValueSlice(vpcIds), includedVpcId)
}

func TestNukeVpcs(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -204,3 +149,90 @@ func TestNukeVpcs(t *testing.T) {

assert.NotContains(t, awsgo.StringValueSlice(vpcIds), vpcId)
}

// Test config file filtering works as expected
func TestShouldIncludeVpc(t *testing.T) {

mockVpc := &ec2.Vpc{
Tags: []*ec2.Tag{
{
Key: awsgo.String("Name"),
Value: awsgo.String("cloud-nuke-test"),
},
{
Key: awsgo.String("Foo"),
Value: awsgo.String("Bar"),
},
},
}

mockExpression, err := regexp.Compile("^cloud-nuke-*")
if err != nil {
logging.Logger.Fatalf("There was an error compiling regex expression %v", err)
}

mockExcludeConfig := config.Config{
VPC: config.ResourceType{
ExcludeRule: config.FilterRule{
NamesRegExp: []config.Expression{
{
RE: *mockExpression,
},
},
},
},
}

mockIncludeConfig := config.Config{
VPC: config.ResourceType{
IncludeRule: config.FilterRule{
NamesRegExp: []config.Expression{
{
RE: *mockExpression,
},
},
},
},
}

cases := []struct {
Name string
Vpc *ec2.Vpc
Config config.Config
ExcludeAfter time.Time
FirstSeenTime time.Time
Expected bool
}{
{
Name: "ConfigExclude",
Vpc: mockVpc,
Config: mockExcludeConfig,
ExcludeAfter: time.Now().Add(1 * time.Hour),
FirstSeenTime: time.Now(),
Expected: false,
},
{
Name: "ConfigInclude",
Vpc: mockVpc,
Config: mockIncludeConfig,
ExcludeAfter: time.Now().Add(1 * time.Hour),
FirstSeenTime: time.Now(),
Expected: true,
},
{
Name: "NotOlderThan",
Vpc: mockVpc,
Config: config.Config{},
ExcludeAfter: time.Now().Add(1 * time.Hour * -1),
FirstSeenTime: time.Now(),
Expected: false,
},
}

for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
result := shouldIncludeVpc(c.Vpc, c.ExcludeAfter, c.FirstSeenTime, c.Config)
assert.Equal(t, c.Expected, result)
})
}
}

0 comments on commit 1c5eb51

Please sign in to comment.