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

Refactor Nuke Implementation for EC2 Resources. #686

Merged
merged 2 commits into from
Apr 22, 2024
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
27 changes: 21 additions & 6 deletions aws/resources/ec2_dhcp_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package resources

import (
"context"
"github.com/aws/aws-sdk-go/aws"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/cloud-nuke/logging"
"github.com/gruntwork-io/cloud-nuke/report"
"github.com/gruntwork-io/go-commons/errors"
"github.com/pterm/pterm"
)

func (v *EC2DhcpOption) getAll(c context.Context, configObj config.Config) ([]*string, error) {
func (v *EC2DhcpOption) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
var dhcpOptionIds []*string
err := v.Client.DescribeDhcpOptionsPages(&ec2.DescribeDhcpOptionsInput{}, func(page *ec2.DescribeDhcpOptionsOutput, lastPage bool) bool {
for _, dhcpOption := range page.DhcpOptions {
Expand All @@ -33,9 +35,8 @@ func (v *EC2DhcpOption) getAll(c context.Context, configObj config.Config) ([]*s

func (v *EC2DhcpOption) nukeAll(identifiers []*string) error {
for _, identifier := range identifiers {
_, err := v.Client.DeleteDhcpOptions(&ec2.DeleteDhcpOptionsInput{
DhcpOptionsId: identifier,
})

err := nukeDhcpOption(v.Client, identifier)
if err != nil {
logging.Debugf("Failed to delete DHCP option w/ err: %s.", err)
} else {
Expand All @@ -44,7 +45,7 @@ func (v *EC2DhcpOption) nukeAll(identifiers []*string) error {

// Record status of this resource
e := report.Entry{
Identifier: aws.StringValue(identifier),
Identifier: awsgo.StringValue(identifier),
ResourceType: v.ResourceName(),
Error: err,
}
Expand All @@ -53,3 +54,17 @@ func (v *EC2DhcpOption) nukeAll(identifiers []*string) error {

return nil
}

func nukeDhcpOption(client ec2iface.EC2API, option *string) error {
logging.Debugf("Deleting DHCP Option %s", awsgo.StringValue(option))

_, err := client.DeleteDhcpOptions(&ec2.DeleteDhcpOptionsInput{
DhcpOptionsId: option,
})
if err != nil {
logging.Debugf("[Failed] Error deleting DHCP option %s: %s", awsgo.StringValue(option), err)
return errors.WithStackTrace(err)
}
logging.Debugf("[Ok] DHCP Option deleted successfully %s", awsgo.StringValue(option))
return nil
}
31 changes: 23 additions & 8 deletions aws/resources/ec2_egress_only_igw.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package resources

import (
"context"
"fmt"
"time"

"github.com/aws/aws-sdk-go/aws"
awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/cloud-nuke/logging"
"github.com/gruntwork-io/cloud-nuke/report"
Expand All @@ -18,8 +20,8 @@ func (egigw *EgressOnlyInternetGateway) setFirstSeenTag(eoig ec2.EgressOnlyInter
Resources: []*string{eoig.EgressOnlyInternetGatewayId},
Tags: []*ec2.Tag{
{
Key: aws.String(util.FirstSeenTagKey),
Value: aws.String(util.FormatTimestamp(value)),
Key: awsgo.String(util.FirstSeenTagKey),
Value: awsgo.String(util.FormatTimestamp(value)),
},
},
})
Expand Down Expand Up @@ -97,7 +99,7 @@ func (egigw *EgressOnlyInternetGateway) getAll(_ context.Context, configObj conf
egigw.VerifyNukablePermissions(result, func(id *string) error {
_, err := egigw.Client.DeleteEgressOnlyInternetGateway(&ec2.DeleteEgressOnlyInternetGatewayInput{
EgressOnlyInternetGatewayId: id,
DryRun: aws.Bool(true),
DryRun: awsgo.Bool(true),
})
return err
})
Expand All @@ -123,13 +125,11 @@ func (egigw *EgressOnlyInternetGateway) nukeAll(ids []*string) error {
continue
}

_, err := egigw.Client.DeleteEgressOnlyInternetGateway(&ec2.DeleteEgressOnlyInternetGatewayInput{
EgressOnlyInternetGatewayId: id,
})
err := nukeEgressOnlyGateway(egigw.Client, id)

// Record status of this resource
e := report.Entry{
Identifier: aws.StringValue(id),
Identifier: awsgo.StringValue(id),
ResourceType: "Egress Only Internet Gateway",
Error: err,
}
Expand All @@ -148,3 +148,18 @@ func (egigw *EgressOnlyInternetGateway) nukeAll(ids []*string) error {
return nil

}

func nukeEgressOnlyGateway(client ec2iface.EC2API, gateway *string) error {
logging.Debugf("[Nuke] Egress only gateway %s", awsgo.StringValue(gateway))

_, err := client.DeleteEgressOnlyInternetGateway(&ec2.DeleteEgressOnlyInternetGatewayInput{
EgressOnlyInternetGatewayId: gateway,
})
if err != nil {
logging.Debug(fmt.Sprintf("[Failed] to delete Egress Only Internet Gateway %s", *gateway))
return errors.WithStackTrace(err)
}

logging.Debug(fmt.Sprintf("[Success] deleted Egress Only Internet Gateway %s", *gateway))
return nil
}
44 changes: 22 additions & 22 deletions aws/resources/ec2_egress_only_igw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/gruntwork-io/cloud-nuke/config"
Expand Down Expand Up @@ -46,26 +46,26 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) {
DescribeEgressOnlyInternetGatewaysOutput: ec2.DescribeEgressOnlyInternetGatewaysOutput{
EgressOnlyInternetGateways: []*ec2.EgressOnlyInternetGateway{
{
EgressOnlyInternetGatewayId: aws.String(gateway1),
EgressOnlyInternetGatewayId: awsgo.String(gateway1),
Tags: []*ec2.Tag{
{
Key: aws.String("Name"),
Value: aws.String(testName1),
Key: awsgo.String("Name"),
Value: awsgo.String(testName1),
}, {
Key: aws.String(util.FirstSeenTagKey),
Value: aws.String(util.FormatTimestamp(now)),
Key: awsgo.String(util.FirstSeenTagKey),
Value: awsgo.String(util.FormatTimestamp(now)),
},
},
},
{
EgressOnlyInternetGatewayId: aws.String(gateway2),
EgressOnlyInternetGatewayId: awsgo.String(gateway2),
Tags: []*ec2.Tag{
{
Key: aws.String("Name"),
Value: aws.String(testName2),
Key: awsgo.String("Name"),
Value: awsgo.String(testName2),
}, {
Key: aws.String(util.FirstSeenTagKey),
Value: aws.String(util.FormatTimestamp(now.Add(1 * time.Hour))),
Key: awsgo.String(util.FirstSeenTagKey),
Value: awsgo.String(util.FormatTimestamp(now.Add(1 * time.Hour))),
},
},
},
Expand Down Expand Up @@ -95,14 +95,14 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) {
"timeAfterExclusionFilter": {
configObj: config.ResourceType{
ExcludeRule: config.FilterRule{
TimeAfter: aws.Time(now),
TimeAfter: awsgo.Time(now),
}},
expected: []string{gateway1},
},
"timeBeforeExclusionFilter": {
configObj: config.ResourceType{
ExcludeRule: config.FilterRule{
TimeBefore: aws.Time(now.Add(1)),
TimeBefore: awsgo.Time(now.Add(1)),
}},
expected: []string{gateway2},
},
Expand All @@ -113,7 +113,7 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) {
EgressOnlyInternetGateway: tc.configObj,
})
require.NoError(t, err)
require.Equal(t, tc.expected, aws.StringValueSlice(names))
require.Equal(t, tc.expected, awsgo.StringValueSlice(names))
})
}

Expand All @@ -138,20 +138,20 @@ func TestEc2EgressOnlyInternetGateway_NukeAll(t *testing.T) {
DescribeEgressOnlyInternetGatewaysOutput: ec2.DescribeEgressOnlyInternetGatewaysOutput{
EgressOnlyInternetGateways: []*ec2.EgressOnlyInternetGateway{
{
EgressOnlyInternetGatewayId: aws.String(gateway1),
EgressOnlyInternetGatewayId: awsgo.String(gateway1),
Attachments: []*ec2.InternetGatewayAttachment{
{
State: aws.String("testing-state"),
VpcId: aws.String("test-gateway-vpc"),
State: awsgo.String("testing-state"),
VpcId: awsgo.String("test-gateway-vpc"),
},
},
},
{
EgressOnlyInternetGatewayId: aws.String(gateway2),
EgressOnlyInternetGatewayId: awsgo.String(gateway2),
Attachments: []*ec2.InternetGatewayAttachment{
{
State: aws.String("testing-state"),
VpcId: aws.String("test-gateway-vpc"),
State: awsgo.String("testing-state"),
VpcId: awsgo.String("test-gateway-vpc"),
},
},
},
Expand All @@ -162,8 +162,8 @@ func TestEc2EgressOnlyInternetGateway_NukeAll(t *testing.T) {
}

err := igw.nukeAll([]*string{
aws.String(gateway1),
aws.String(gateway2),
awsgo.String(gateway1),
awsgo.String(gateway2),
})
require.NoError(t, err)
}
69 changes: 60 additions & 9 deletions aws/resources/ec2_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,27 @@ package resources

import (
"context"
"fmt"
"time"

"github.com/aws/aws-sdk-go/aws"
awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/cloud-nuke/logging"
"github.com/gruntwork-io/cloud-nuke/report"
"github.com/gruntwork-io/cloud-nuke/util"
"github.com/gruntwork-io/go-commons/errors"
"github.com/gruntwork-io/go-commons/retry"
)

func (e *EC2Endpoints) setFirstSeenTag(endpoint ec2.VpcEndpoint, value time.Time) error {
_, err := e.Client.CreateTags(&ec2.CreateTagsInput{
Resources: []*string{endpoint.VpcEndpointId},
Tags: []*ec2.Tag{
{
Key: aws.String(util.FirstSeenTagKey),
Value: aws.String(util.FormatTimestamp(value)),
Key: awsgo.String(util.FirstSeenTagKey),
Value: awsgo.String(util.FormatTimestamp(value)),
},
},
})
Expand Down Expand Up @@ -95,7 +98,7 @@ func (e *EC2Endpoints) getAll(_ context.Context, configObj config.Config) ([]*st
e.VerifyNukablePermissions(result, func(id *string) error {
_, err := e.Client.DeleteVpcEndpoints(&ec2.DeleteVpcEndpointsInput{
VpcEndpointIds: []*string{id},
DryRun: aws.Bool(true),
DryRun: awsgo.Bool(true),
})
return err
})
Expand All @@ -118,13 +121,11 @@ func (e *EC2Endpoints) nukeAll(identifiers []*string) error {
continue
}

_, err := e.Client.DeleteVpcEndpoints(&ec2.DeleteVpcEndpointsInput{
VpcEndpointIds: []*string{id},
})
err := nukeVpcEndpoint(e.Client, []*string{id})

// Record status of this resource
e := report.Entry{
Identifier: aws.StringValue(id),
Identifier: awsgo.StringValue(id),
ResourceType: "Vpc Endpoint",
Error: err,
}
Expand All @@ -134,11 +135,61 @@ func (e *EC2Endpoints) nukeAll(identifiers []*string) error {
logging.Debugf("[Failed] %s", err)
} else {
deletedAddresses = append(deletedAddresses, id)
logging.Debugf("Deleted Vpc Endpoint: %s", *id)
}
}

logging.Debugf("[OK] %d Vpc Endpoint(s) deleted in %s", len(deletedAddresses), e.Region)

return nil
}

func nukeVpcEndpoint(client ec2iface.EC2API, endpointIds []*string) error {
logging.Debugf("Deleting VPC endpoints %s", awsgo.StringValueSlice(endpointIds))

_, err := client.DeleteVpcEndpoints(&ec2.DeleteVpcEndpointsInput{
VpcEndpointIds: endpointIds,
})
if err != nil {
logging.Debug(fmt.Sprintf("Failed to delete VPC endpoints: %s", err.Error()))
return errors.WithStackTrace(err)
}

logging.Debug(fmt.Sprintf("Successfully deleted VPC endpoints %s",
awsgo.StringValueSlice(endpointIds)))

return nil
}

func waitForVPCEndpointToBeDeleted(client ec2iface.EC2API, vpcID string) error {
return retry.DoWithRetry(
logging.Logger.WithTime(time.Now()),
"Waiting for all VPC endpoints to be deleted",
10,
2*time.Second,
func() error {
endpoints, err := client.DescribeVpcEndpoints(
&ec2.DescribeVpcEndpointsInput{
Filters: []*ec2.Filter{
{
Name: awsgo.String("vpc-id"),
Values: []*string{awsgo.String(vpcID)},
},
{
Name: awsgo.String("vpc-endpoint-state"),
Values: []*string{awsgo.String("deleting")},
},
},
},
)
if err != nil {
return err
}

if len(endpoints.VpcEndpoints) == 0 {
return nil
}
return fmt.Errorf("Not all VPC endpoints deleted.")
},
)
return nil
}
Loading