diff --git a/aws/resources/ec2_egress_only_igw.go b/aws/resources/ec2_egress_only_igw.go index 3143556f..5d47b8cb 100644 --- a/aws/resources/ec2_egress_only_igw.go +++ b/aws/resources/ec2_egress_only_igw.go @@ -5,9 +5,9 @@ import ( "fmt" "time" - 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" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" "github.com/gruntwork-io/cloud-nuke/report" @@ -15,10 +15,10 @@ import ( "github.com/gruntwork-io/go-commons/errors" ) -func shouldIncludeEgressOnlyInternetGateway(gateway *ec2.EgressOnlyInternetGateway, firstSeenTime *time.Time, configObj config.Config) bool { +func shouldIncludeEgressOnlyInternetGateway(gateway types.EgressOnlyInternetGateway, firstSeenTime *time.Time, configObj config.Config) bool { var gatewayName string // get the tags as map - tagMap := util.ConvertEC2TagsToMap(gateway.Tags) + tagMap := util.ConvertTypesTagsToMap(gateway.Tags) if name, ok := tagMap["Name"]; ok { gatewayName = name } @@ -34,13 +34,13 @@ func (egigw *EgressOnlyInternetGateway) getAll(c context.Context, configObj conf var firstSeenTime *time.Time var err error - output, err := egigw.Client.DescribeEgressOnlyInternetGatewaysWithContext(egigw.Context, &ec2.DescribeEgressOnlyInternetGatewaysInput{}) + output, err := egigw.Client.DescribeEgressOnlyInternetGateways(egigw.Context, &ec2.DescribeEgressOnlyInternetGatewaysInput{}) if err != nil { return nil, errors.WithStackTrace(err) } for _, igw := range output.EgressOnlyInternetGateways { - firstSeenTime, err = util.GetOrCreateFirstSeen(c, egigw.Client, igw.EgressOnlyInternetGatewayId, util.ConvertEC2TagsToMap(igw.Tags)) + firstSeenTime, err = util.GetOrCreateFirstSeen(c, egigw.Client, igw.EgressOnlyInternetGatewayId, util.ConvertTypesTagsToMap(igw.Tags)) if err != nil { logging.Error("Unable to retrieve tags") return nil, errors.WithStackTrace(err) @@ -51,7 +51,7 @@ func (egigw *EgressOnlyInternetGateway) getAll(c context.Context, configObj conf } // checking the nukable permissions egigw.VerifyNukablePermissions(result, func(id *string) error { - _, err := egigw.Client.DeleteEgressOnlyInternetGatewayWithContext(egigw.Context, &ec2.DeleteEgressOnlyInternetGatewayInput{ + _, err := egigw.Client.DeleteEgressOnlyInternetGateway(egigw.Context, &ec2.DeleteEgressOnlyInternetGatewayInput{ EgressOnlyInternetGatewayId: id, DryRun: awsgo.Bool(true), }) @@ -82,7 +82,7 @@ func (egigw *EgressOnlyInternetGateway) nukeAll(ids []*string) error { // Record status of this resource e := report.Entry{ - Identifier: awsgo.StringValue(id), + Identifier: awsgo.ToString(id), ResourceType: "Egress Only Internet Gateway", Error: err, } @@ -102,10 +102,10 @@ func (egigw *EgressOnlyInternetGateway) nukeAll(ids []*string) error { } -func nukeEgressOnlyGateway(client ec2iface.EC2API, gateway *string) error { - logging.Debugf("[Nuke] Egress only gateway %s", awsgo.StringValue(gateway)) +func nukeEgressOnlyGateway(client EgressOnlyIGAPI, gateway *string) error { + logging.Debugf("[Nuke] Egress only gateway %s", awsgo.ToString(gateway)) - _, err := client.DeleteEgressOnlyInternetGateway(&ec2.DeleteEgressOnlyInternetGatewayInput{ + _, err := client.DeleteEgressOnlyInternetGateway(context.Background(), &ec2.DeleteEgressOnlyInternetGatewayInput{ EgressOnlyInternetGatewayId: gateway, }) if err != nil { diff --git a/aws/resources/ec2_egress_only_igw_test.go b/aws/resources/ec2_egress_only_igw_test.go index dc1e728b..0039bc88 100644 --- a/aws/resources/ec2_egress_only_igw_test.go +++ b/aws/resources/ec2_egress_only_igw_test.go @@ -6,10 +6,9 @@ import ( "testing" "time" - awsgo "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/util" "github.com/stretchr/testify/require" @@ -17,20 +16,16 @@ import ( type mockedEgressOnlyIgw struct { BaseAwsResource - ec2iface.EC2API + EgressOnlyIGAPI DescribeEgressOnlyInternetGatewaysOutput ec2.DescribeEgressOnlyInternetGatewaysOutput DeleteEgressOnlyInternetGatewayOutput ec2.DeleteEgressOnlyInternetGatewayOutput } -func (m mockedEgressOnlyIgw) DescribeEgressOnlyInternetGatewaysWithContext(_ awsgo.Context, _ *ec2.DescribeEgressOnlyInternetGatewaysInput, _ ...request.Option) (*ec2.DescribeEgressOnlyInternetGatewaysOutput, error) { +func (m mockedEgressOnlyIgw) DescribeEgressOnlyInternetGateways(ctx context.Context, params *ec2.DescribeEgressOnlyInternetGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeEgressOnlyInternetGatewaysOutput, error) { return &m.DescribeEgressOnlyInternetGatewaysOutput, nil } -func (m mockedEgressOnlyIgw) DeleteEgressOnlyInternetGatewayWithContext(_ awsgo.Context, _ *ec2.DeleteEgressOnlyInternetGatewayInput, _ ...request.Option) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error) { - return &m.DeleteEgressOnlyInternetGatewayOutput, nil -} - -func (m mockedEgressOnlyIgw) DeleteEgressOnlyInternetGateway(_ *ec2.DeleteEgressOnlyInternetGatewayInput) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error) { +func (m mockedEgressOnlyIgw) DeleteEgressOnlyInternetGateway(ctx context.Context, params *ec2.DeleteEgressOnlyInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error) { return &m.DeleteEgressOnlyInternetGatewayOutput, nil } @@ -52,10 +47,10 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) { object := EgressOnlyInternetGateway{ Client: mockedEgressOnlyIgw{ DescribeEgressOnlyInternetGatewaysOutput: ec2.DescribeEgressOnlyInternetGatewaysOutput{ - EgressOnlyInternetGateways: []*ec2.EgressOnlyInternetGateway{ + EgressOnlyInternetGateways: []types.EgressOnlyInternetGateway{ { EgressOnlyInternetGatewayId: awsgo.String(gateway1), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName1), @@ -67,7 +62,7 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) { }, { EgressOnlyInternetGatewayId: awsgo.String(gateway2), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName2), @@ -126,7 +121,7 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) { EgressOnlyInternetGateway: tc.configObj, }) require.NoError(t, err) - require.Equal(t, tc.expected, awsgo.StringValueSlice(names)) + require.Equal(t, tc.expected, awsgo.ToStringSlice(names)) }) } @@ -149,21 +144,21 @@ func TestEc2EgressOnlyInternetGateway_NukeAll(t *testing.T) { }, Client: mockedEgressOnlyIgw{ DescribeEgressOnlyInternetGatewaysOutput: ec2.DescribeEgressOnlyInternetGatewaysOutput{ - EgressOnlyInternetGateways: []*ec2.EgressOnlyInternetGateway{ + EgressOnlyInternetGateways: []types.EgressOnlyInternetGateway{ { EgressOnlyInternetGatewayId: awsgo.String(gateway1), - Attachments: []*ec2.InternetGatewayAttachment{ + Attachments: []types.InternetGatewayAttachment{ { - State: awsgo.String("testing-state"), + State: "testing-state", VpcId: awsgo.String("test-gateway-vpc"), }, }, }, { EgressOnlyInternetGatewayId: awsgo.String(gateway2), - Attachments: []*ec2.InternetGatewayAttachment{ + Attachments: []types.InternetGatewayAttachment{ { - State: awsgo.String("testing-state"), + State: "testing-state", VpcId: awsgo.String("test-gateway-vpc"), }, }, diff --git a/aws/resources/ec2_egress_only_igw_types.go b/aws/resources/ec2_egress_only_igw_types.go index af9d3b69..888cc73a 100644 --- a/aws/resources/ec2_egress_only_igw_types.go +++ b/aws/resources/ec2_egress_only_igw_types.go @@ -3,27 +3,32 @@ package resources import ( "context" - 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/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/go-commons/errors" ) +type EgressOnlyIGAPI interface { + DescribeEgressOnlyInternetGateways(ctx context.Context, params *ec2.DescribeEgressOnlyInternetGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeEgressOnlyInternetGatewaysOutput, error) + DeleteEgressOnlyInternetGateway(ctx context.Context, params *ec2.DeleteEgressOnlyInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error) +} + // EgressOnlyInternetGateway represents all Egress only internet gateway type EgressOnlyInternetGateway struct { BaseAwsResource - Client ec2iface.EC2API + Client EgressOnlyIGAPI Region string Pools []string } -func (egigw *EgressOnlyInternetGateway) Init(session *session.Session) { - egigw.BaseAwsResource.Init(session) - egigw.Client = ec2.New(session) +func (egigw *EgressOnlyInternetGateway) InitV2(cfg aws.Config) { + egigw.Client = ec2.NewFromConfig(cfg) } +func (egigw *EgressOnlyInternetGateway) IsUsingV2() bool { return true } + // ResourceName - the simple name of the aws resource func (egigw *EgressOnlyInternetGateway) ResourceName() string { return "egress-only-internet-gateway" @@ -49,7 +54,7 @@ func (egigw *EgressOnlyInternetGateway) GetAndSetIdentifiers(c context.Context, return nil, err } - egigw.Pools = awsgo.StringValueSlice(identifiers) + egigw.Pools = awsgo.ToStringSlice(identifiers) return egigw.Pools, nil } diff --git a/aws/resources/ec2_endpoints.go b/aws/resources/ec2_endpoints.go index 6eaca77c..1b83b084 100644 --- a/aws/resources/ec2_endpoints.go +++ b/aws/resources/ec2_endpoints.go @@ -80,7 +80,7 @@ func (e *EC2Endpoints) nukeAll(identifiers []*string) error { continue } - err := e.nukeVpcEndpoint([]*string{id}) + err := nukeVpcEndpoint(e.Client, []*string{id}) // Record status of this resource e := report.Entry{ @@ -102,10 +102,10 @@ func (e *EC2Endpoints) nukeAll(identifiers []*string) error { return nil } -func (e *EC2Endpoints) nukeVpcEndpoint(endpointIds []*string) error { +func nukeVpcEndpoint(client EC2EndpointsAPI, endpointIds []*string) error { logging.Debugf("Deleting VPC endpoints %s", aws.ToStringSlice(endpointIds)) - _, err := e.Client.DeleteVpcEndpoints(e.Context, &ec2.DeleteVpcEndpointsInput{ + _, err := client.DeleteVpcEndpoints(context.Background(), &ec2.DeleteVpcEndpointsInput{ VpcEndpointIds: aws.ToStringSlice(endpointIds), }) if err != nil { diff --git a/aws/resources/ec2_internet_gateway.go b/aws/resources/ec2_internet_gateway.go index a3e0c68f..867bc85b 100644 --- a/aws/resources/ec2_internet_gateway.go +++ b/aws/resources/ec2_internet_gateway.go @@ -5,9 +5,9 @@ import ( "fmt" "time" - 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" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" r "github.com/gruntwork-io/cloud-nuke/report" // Alias the package as 'r' @@ -15,10 +15,10 @@ import ( "github.com/gruntwork-io/go-commons/errors" ) -func shouldIncludeGateway(ig *ec2.InternetGateway, firstSeenTime *time.Time, configObj config.Config) bool { +func shouldIncludeGateway(ig types.InternetGateway, firstSeenTime *time.Time, configObj config.Config) bool { var internetGateway string // get the tags as map - tagMap := util.ConvertEC2TagsToMap(ig.Tags) + tagMap := util.ConvertTypesTagsToMap(ig.Tags) if name, ok := tagMap["Name"]; ok { internetGateway = name } @@ -36,13 +36,13 @@ func (igw *InternetGateway) getAll(c context.Context, configObj config.Config) ( var err error input := &ec2.DescribeInternetGatewaysInput{} - resp, err := igw.Client.DescribeInternetGatewaysWithContext(igw.Context, input) + resp, err := igw.Client.DescribeInternetGateways(igw.Context, input) if err != nil { logging.Debugf("[Internet Gateway] Failed to list internet gateways: %s", err) return nil, err } for _, ig := range resp.InternetGateways { - firstSeenTime, err = util.GetOrCreateFirstSeen(c, igw.Client, ig.InternetGatewayId, util.ConvertEC2TagsToMap(ig.Tags)) + firstSeenTime, err = util.GetOrCreateFirstSeen(c, igw.Client, ig.InternetGatewayId, util.ConvertTypesTagsToMap(ig.Tags)) if err != nil { logging.Error("Unable to retrieve tags") return nil, errors.WithStackTrace(err) @@ -53,7 +53,7 @@ func (igw *InternetGateway) getAll(c context.Context, configObj config.Config) ( // get vpc id for this igw and update the map if len(ig.Attachments) > 0 { - igw.GatewayVPCMap[awsgo.StringValue(ig.InternetGatewayId)] = awsgo.StringValue(ig.Attachments[0].VpcId) + igw.GatewayVPCMap[awsgo.ToString(ig.InternetGatewayId)] = awsgo.ToString(ig.Attachments[0].VpcId) } } } @@ -64,7 +64,7 @@ func (igw *InternetGateway) getAll(c context.Context, configObj config.Config) ( InternetGatewayId: id, DryRun: awsgo.Bool(true), } - _, err := igw.Client.DeleteInternetGatewayWithContext(igw.Context, params) + _, err := igw.Client.DeleteInternetGateway(igw.Context, params) return err }) @@ -89,7 +89,7 @@ func (igw *InternetGateway) nukeAll(identifiers []*string) error { err := igw.nuke(id) // Record status of this resource e := r.Entry{ // Use the 'r' alias to refer to the package - Identifier: awsgo.StringValue(id), + Identifier: awsgo.ToString(id), ResourceType: "Internet Gateway", Error: err, } @@ -107,12 +107,12 @@ func (igw *InternetGateway) nukeAll(identifiers []*string) error { func (igw *InternetGateway) nuke(id *string) error { // get the vpc id for current igw - vpcID, ok := igw.GatewayVPCMap[awsgo.StringValue(id)] + vpcID, ok := igw.GatewayVPCMap[awsgo.ToString(id)] if !ok { logging.Debug(fmt.Sprintf("Failed to read the vpc Id for %s", - awsgo.StringValue(id))) + awsgo.ToString(id))) return fmt.Errorf("Failed to retrieve the VPC ID for %s, which is mandatory for the internet gateway nuke operation.", - awsgo.StringValue(id)) + awsgo.ToString(id)) } err := nukeInternetGateway(igw.Client, id, vpcID) @@ -123,11 +123,11 @@ func (igw *InternetGateway) nuke(id *string) error { return nil } -func nukeInternetGateway(client ec2iface.EC2API, gatewayId *string, vpcID string) error { +func nukeInternetGateway(client InternetGatewayAPI, gatewayId *string, vpcID string) error { var err error logging.Debug(fmt.Sprintf("Detaching Internet Gateway %s", - awsgo.StringValue(gatewayId))) - _, err = client.DetachInternetGateway( + awsgo.ToString(gatewayId))) + _, err = client.DetachInternetGateway(context.Background(), &ec2.DetachInternetGatewayInput{ InternetGatewayId: gatewayId, VpcId: awsgo.String(vpcID), @@ -135,27 +135,27 @@ func nukeInternetGateway(client ec2iface.EC2API, gatewayId *string, vpcID string ) if err != nil { logging.Debug(fmt.Sprintf("Failed to detach internet gateway %s", - awsgo.StringValue(gatewayId))) + awsgo.ToString(gatewayId))) return errors.WithStackTrace(err) } logging.Debug(fmt.Sprintf("Successfully detached internet gateway %s", - awsgo.StringValue(gatewayId))) + awsgo.ToString(gatewayId))) // nuking the internet gateway logging.Debug(fmt.Sprintf("Deleting internet gateway %s", - awsgo.StringValue(gatewayId))) - _, err = client.DeleteInternetGateway( + awsgo.ToString(gatewayId))) + _, err = client.DeleteInternetGateway(context.Background(), &ec2.DeleteInternetGatewayInput{ InternetGatewayId: gatewayId, }, ) if err != nil { logging.Debug(fmt.Sprintf("Failed to delete internet gateway %s", - awsgo.StringValue(gatewayId))) + awsgo.ToString(gatewayId))) return errors.WithStackTrace(err) } logging.Debug(fmt.Sprintf("Successfully deleted internet gateway %s", - awsgo.StringValue(gatewayId))) + awsgo.ToString(gatewayId))) return nil diff --git a/aws/resources/ec2_internet_gateway_test.go b/aws/resources/ec2_internet_gateway_test.go index ea3021da..07f4bf30 100644 --- a/aws/resources/ec2_internet_gateway_test.go +++ b/aws/resources/ec2_internet_gateway_test.go @@ -6,10 +6,9 @@ import ( "testing" "time" - awsgo "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/util" "github.com/stretchr/testify/require" @@ -17,21 +16,21 @@ import ( type mockedInternetGateway struct { BaseAwsResource - ec2iface.EC2API + InternetGatewayAPI DescribeInternetGatewaysOutput ec2.DescribeInternetGatewaysOutput DetachInternetGatewayOutput ec2.DetachInternetGatewayOutput DeleteInternetGatewayOutput ec2.DeleteInternetGatewayOutput } -func (m mockedInternetGateway) DescribeInternetGatewaysWithContext(_ awsgo.Context, _ *ec2.DescribeInternetGatewaysInput, _ ...request.Option) (*ec2.DescribeInternetGatewaysOutput, error) { +func (m mockedInternetGateway) DescribeInternetGateways(ctx context.Context, params *ec2.DescribeInternetGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInternetGatewaysOutput, error) { return &m.DescribeInternetGatewaysOutput, nil } -func (m mockedInternetGateway) DetachInternetGatewayWithContext(_ awsgo.Context, _ *ec2.DetachInternetGatewayInput, _ ...request.Option) (*ec2.DetachInternetGatewayOutput, error) { +func (m mockedInternetGateway) DetachInternetGateway(ctx context.Context, params *ec2.DetachInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DetachInternetGatewayOutput, error) { return &m.DetachInternetGatewayOutput, nil } -func (m mockedInternetGateway) DeleteInternetGatewayWithContext(_ awsgo.Context, _ *ec2.DeleteInternetGatewayInput, _ ...request.Option) (*ec2.DeleteInternetGatewayOutput, error) { +func (m mockedInternetGateway) DeleteInternetGateway(ctx context.Context, params *ec2.DeleteInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteInternetGatewayOutput, error) { return &m.DeleteInternetGatewayOutput, nil } @@ -54,10 +53,10 @@ func TestEc2InternetGateway_GetAll(t *testing.T) { igw := InternetGateway{ Client: mockedInternetGateway{ DescribeInternetGatewaysOutput: ec2.DescribeInternetGatewaysOutput{ - InternetGateways: []*ec2.InternetGateway{ + InternetGateways: []types.InternetGateway{ { InternetGatewayId: awsgo.String(gateway1), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName1), @@ -69,7 +68,7 @@ func TestEc2InternetGateway_GetAll(t *testing.T) { }, { InternetGatewayId: awsgo.String(gateway2), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName2), @@ -120,7 +119,7 @@ func TestEc2InternetGateway_GetAll(t *testing.T) { InternetGateway: tc.configObj, }) require.NoError(t, err) - require.Equal(t, tc.expected, awsgo.StringValueSlice(names)) + require.Equal(t, tc.expected, awsgo.ToStringSlice(names)) }) } @@ -143,21 +142,21 @@ func TestEc2InternetGateway_NukeAll(t *testing.T) { }, Client: mockedInternetGateway{ DescribeInternetGatewaysOutput: ec2.DescribeInternetGatewaysOutput{ - InternetGateways: []*ec2.InternetGateway{ + InternetGateways: []types.InternetGateway{ { InternetGatewayId: awsgo.String(gateway1), - Attachments: []*ec2.InternetGatewayAttachment{ + Attachments: []types.InternetGatewayAttachment{ { - State: awsgo.String("testing-state"), + State: "testing-state", VpcId: awsgo.String("test-gateway-vpc"), }, }, }, { InternetGatewayId: awsgo.String(gateway2), - Attachments: []*ec2.InternetGatewayAttachment{ + Attachments: []types.InternetGatewayAttachment{ { - State: awsgo.String("testing-state"), + State: "testing-state", VpcId: awsgo.String("test-gateway-vpc"), }, }, diff --git a/aws/resources/ec2_internet_gateway_types.go b/aws/resources/ec2_internet_gateway_types.go index bd3b6665..99d678ec 100644 --- a/aws/resources/ec2_internet_gateway_types.go +++ b/aws/resources/ec2_internet_gateway_types.go @@ -3,32 +3,36 @@ package resources import ( "context" - "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/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/go-commons/errors" ) +type InternetGatewayAPI interface { + DescribeInternetGateways(ctx context.Context, params *ec2.DescribeInternetGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInternetGatewaysOutput, error) + DeleteInternetGateway(ctx context.Context, params *ec2.DeleteInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteInternetGatewayOutput, error) + DetachInternetGateway(ctx context.Context, params *ec2.DetachInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DetachInternetGatewayOutput, error) +} + type InternetGateway struct { BaseAwsResource - Client ec2iface.EC2API + Client InternetGatewayAPI Region string GatewayIds []string GatewayVPCMap map[string]string } -func (igw *InternetGateway) Init(session *session.Session) { - igw.BaseAwsResource.Init(session) - igw.Client = ec2.New(session) - +func (igw *InternetGateway) InitV2(cfg aws.Config) { + igw.Client = ec2.NewFromConfig(cfg) // Since the nuking of the internet gateway requires the VPC ID, and to avoid redundant API calls for this information within the nuke method, // we utilize the getAll method to retrieve it. // This map is used to store the information and access the value within the nuke method. igw.GatewayVPCMap = make(map[string]string) } +func (igw *InternetGateway) IsUsingV2() bool { return true } + func (igw *InternetGateway) ResourceName() string { return "internet-gateway" } @@ -51,7 +55,7 @@ func (igw *InternetGateway) GetAndSetIdentifiers(c context.Context, configObj co return nil, err } - igw.GatewayIds = aws.StringValueSlice(identifiers) + igw.GatewayIds = aws.ToStringSlice(identifiers) return igw.GatewayIds, nil } diff --git a/aws/resources/ec2_network_acl.go b/aws/resources/ec2_network_acl.go index ca394d66..efd2fd40 100644 --- a/aws/resources/ec2_network_acl.go +++ b/aws/resources/ec2_network_acl.go @@ -4,9 +4,9 @@ import ( "context" "time" - 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" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" "github.com/gruntwork-io/cloud-nuke/report" @@ -14,10 +14,9 @@ import ( "github.com/gruntwork-io/go-commons/errors" ) -func shouldIncludeNetworkACL(networkAcl *ec2.NetworkAcl, firstSeenTime *time.Time, configObj config.Config) bool { +func shouldIncludeNetworkACL(networkAcl *types.NetworkAcl, firstSeenTime *time.Time, configObj config.Config) bool { var naclName string - // get the tags as map - tagMap := util.ConvertEC2TagsToMap(networkAcl.Tags) + tagMap := util.ConvertTypesTagsToMap(networkAcl.Tags) if name, ok := tagMap["Name"]; ok { naclName = name } @@ -28,45 +27,49 @@ func shouldIncludeNetworkACL(networkAcl *ec2.NetworkAcl, firstSeenTime *time.Tim }) } -func (nacl *NetworkACL) getAll(c context.Context, configObj config.Config) ([]*string, error) { +func (nacl *NetworkACL) getAll(ctx context.Context, cnfObj config.Config) ([]*string, error) { var identifiers []*string - var firstSeenTime *time.Time - var err error - - resp, err := nacl.Client.DescribeNetworkAclsWithContext(nacl.Context, &ec2.DescribeNetworkAclsInput{ - Filters: []*ec2.Filter{ + hasMorePages := true + params := &ec2.DescribeNetworkAclsInput{ + Filters: []types.Filter{ { - Name: awsgo.String("default"), - Values: []*string{ - awsgo.String("false"), // can't able to nuke default nacl - }, + Name: awsgo.String("default"), + Values: []string{"false"}, }, }, - }) - if err != nil { - logging.Debugf("[Network ACL] Failed to list network ACL: %s", err) - return nil, err } - for _, networkAcl := range resp.NetworkAcls { - firstSeenTime, err = util.GetOrCreateFirstSeen(c, nacl.Client, networkAcl.NetworkAclId, util.ConvertEC2TagsToMap(networkAcl.Tags)) + for hasMorePages { + resp, err := nacl.Client.DescribeNetworkAcls(ctx, params) if err != nil { - logging.Error("unable to retrieve first seen tag") - continue + logging.Debugf("[Network ACL] Failed to list network ACLs: %s", err) + return nil, errors.WithStackTrace(err) } - if shouldIncludeNetworkACL(networkAcl, firstSeenTime, configObj) { - identifiers = append(identifiers, networkAcl.NetworkAclId) + for _, networkAcl := range resp.NetworkAcls { + firstSeenTime, err := util.GetOrCreateFirstSeen(ctx, nacl.Client, networkAcl.NetworkAclId, util.ConvertTypesTagsToMap(networkAcl.Tags)) + if err != nil { + logging.Errorf("[Network ACL] Unable to retrieve first seen tag for ACL ID: %s, error: %s", *networkAcl.NetworkAclId, err) + continue + } + + if shouldIncludeNetworkACL(&networkAcl, firstSeenTime, cnfObj) { + identifiers = append(identifiers, networkAcl.NetworkAclId) + } } + + params.NextToken = resp.NextToken + hasMorePages = params.NextToken != nil } - // Check and verify the list of allowed nuke actions + // Verify permissions for nuking NACLs nacl.VerifyNukablePermissions(identifiers, func(id *string) error { - _, err := nacl.Client.DeleteNetworkAclWithContext(nacl.Context, &ec2.DeleteNetworkAclInput{ + _, err := nacl.Client.DeleteNetworkAcl(ctx, &ec2.DeleteNetworkAclInput{ NetworkAclId: id, DryRun: awsgo.Bool(true), }) + return err }) @@ -76,7 +79,7 @@ func (nacl *NetworkACL) getAll(c context.Context, configObj config.Config) ([]*s func (nacl *NetworkACL) nuke(id *string) error { // nuke attached subnets - if err := nacl.nukeAssociatedSubnets(id); err != nil { + if err := nacl.nukeAssociatedSubnets(*id); err != nil { return errors.WithStackTrace(err) } @@ -93,9 +96,9 @@ func (nacl *NetworkACL) nuke(id *string) error { // // Thus, to remove the association, it requires another network ACL ID. Here, we check the default network ACL of the VPC to which the current network ACL is attached, // and then associate that network ACL with the association. -func (nacl *NetworkACL) nukeAssociatedSubnets(id *string) error { - resp, err := nacl.Client.DescribeNetworkAclsWithContext(nacl.Context, &ec2.DescribeNetworkAclsInput{ - NetworkAclIds: []*string{id}, +func (nacl *NetworkACL) nukeAssociatedSubnets(id string) error { + resp, err := nacl.Client.DescribeNetworkAcls(nacl.Context, &ec2.DescribeNetworkAclsInput{ + NetworkAclIds: []string{id}, }) if err != nil { logging.Debugf("[Network ACL] Failed to describe network ACL: %s", err) @@ -103,28 +106,26 @@ func (nacl *NetworkACL) nukeAssociatedSubnets(id *string) error { } if len(resp.NetworkAcls) == 0 { - logging.Debugf("[Network ACL] Nothing found: %s", awsgo.StringValue(id)) + logging.Debugf("[Network ACL] Nothing found: %s", id) return nil } var ( networkAcl = resp.NetworkAcls[0] - vpcID = networkAcl.VpcId // get the vpc of this nacl + vpcID = networkAcl.VpcId ) // Get the default nacl association id - networkACLs, err := nacl.Client.DescribeNetworkAclsWithContext( + networkACLs, err := nacl.Client.DescribeNetworkAcls( nacl.Context, &ec2.DescribeNetworkAclsInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("vpc-id"), - Values: []*string{vpcID}, + Values: []string{*vpcID}, }, { - Name: awsgo.String("default"), - Values: []*string{ - awsgo.String("true"), - }, + Name: awsgo.String("default"), + Values: []string{"true"}, }, }, }, @@ -135,17 +136,20 @@ func (nacl *NetworkACL) nukeAssociatedSubnets(id *string) error { } if len(networkACLs.NetworkAcls) == 0 { - logging.Debugf("[Network ACL] Nothing found to check the default association: %s", awsgo.StringValue(id)) + logging.Debugf("[Network ACL] Nothing found to check the default association: %s", id) return nil } - var defaultNetworkAclID *string - defaultNetworkAclID = networkACLs.NetworkAcls[0].NetworkAclId + defaultNetworkAclID := networkACLs.NetworkAcls[0].NetworkAclId + + var associations []*types.NetworkAclAssociation + for i := range networkAcl.Associations { + associations = append(associations, &networkAcl.Associations[i]) + } - // replace the association with default - err = replaceNetworkAclAssociation(nacl.Client, defaultNetworkAclID, networkAcl.Associations) + err = replaceNetworkAclAssociation(nacl.Client, defaultNetworkAclID, associations) if err != nil { - logging.Debugf("Failed to replace network ACL associations: %s", awsgo.StringValue(defaultNetworkAclID)) + logging.Debugf("Failed to replace network ACL associations: %s", *defaultNetworkAclID) return errors.WithStackTrace(err) } return nil @@ -169,8 +173,8 @@ func (nacl *NetworkACL) nukeAll(identifiers []*string) error { err := nacl.nuke(id) // Record status of this resource - e := report.Entry{ // Use the 'r' alias to refer to the package - Identifier: awsgo.StringValue(id), + e := report.Entry{ + Identifier: awsgo.ToString(id), ResourceType: "Network ACL", Error: err, } @@ -186,38 +190,37 @@ func (nacl *NetworkACL) nukeAll(identifiers []*string) error { return nil } -func replaceNetworkAclAssociation(client ec2iface.EC2API, networkAclId *string, associations []*ec2.NetworkAclAssociation) error { - logging.Debugf("Start replacing network ACL associations: %s", awsgo.StringValue(networkAclId)) +func replaceNetworkAclAssociation(client NetworkACLAPI, networkAclId *string, associations []*types.NetworkAclAssociation) error { + logging.Debugf("Start replacing network ACL associations: %s", *networkAclId) for _, association := range associations { logging.Debugf("Found %d network ACL associations to replace", len(associations)) - _, err := client.ReplaceNetworkAclAssociation(&ec2.ReplaceNetworkAclAssociationInput{ + _, err := client.ReplaceNetworkAclAssociation(context.TODO(), &ec2.ReplaceNetworkAclAssociationInput{ AssociationId: association.NetworkAclAssociationId, NetworkAclId: networkAclId, }) if err != nil { - logging.Debugf("Failed to replace network ACL association: %s to default", - awsgo.StringValue(association.NetworkAclAssociationId)) + logging.Debugf("Failed to replace network ACL association: %s to default", *association.NetworkAclAssociationId) return errors.WithStackTrace(err) } logging.Debugf("Successfully replaced network ACL association: %s to default", - awsgo.StringValue(association.NetworkAclAssociationId)) + *association.NetworkAclAssociationId) } - logging.Debugf("Successfully replaced network ACL associations: %s", awsgo.StringValue(networkAclId)) + logging.Debugf("Successfully replaced network ACL associations: %s", *networkAclId) return nil } -func nukeNetworkAcl(client ec2iface.EC2API, id *string) error { - logging.Debugf("Deleting network Acl %s", awsgo.StringValue(id)) +func nukeNetworkAcl(client NetworkACLAPI, id *string) error { + logging.Debugf("Deleting network Acl %s", *id) - if _, err := client.DeleteNetworkAcl(&ec2.DeleteNetworkAclInput{ + if _, err := client.DeleteNetworkAcl(context.TODO(), &ec2.DeleteNetworkAclInput{ NetworkAclId: id, }); err != nil { - logging.Debugf("An error happened while nuking NACL %s, error %v", awsgo.StringValue(id), err) + logging.Debugf("An error happened while nuking NACL %s, error %v", *id, err) return err } - logging.Debugf("[Ok] network acl deleted successfully %s", awsgo.StringValue(id)) + logging.Debugf("[Ok] network acl deleted successfully %s", *id) return nil } diff --git a/aws/resources/ec2_network_acl_test.go b/aws/resources/ec2_network_acl_test.go index 200969e0..55e609f0 100644 --- a/aws/resources/ec2_network_acl_test.go +++ b/aws/resources/ec2_network_acl_test.go @@ -6,82 +6,72 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - awsgo "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/util" "github.com/stretchr/testify/require" ) type mockedNetworkACL struct { - BaseAwsResource - ec2iface.EC2API - DescribeNetworkAclsOutput ec2.DescribeNetworkAclsOutput DeleteNetworkAclOutput ec2.DeleteNetworkAclOutput ReplaceNetworkAclAssociationOutput ec2.ReplaceNetworkAclAssociationOutput } -func (m mockedNetworkACL) DescribeNetworkAclsWithContext(_ awsgo.Context, _ *ec2.DescribeNetworkAclsInput, _ ...request.Option) (*ec2.DescribeNetworkAclsOutput, error) { +func (m *mockedNetworkACL) DescribeNetworkAcls(ctx context.Context, params *ec2.DescribeNetworkAclsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNetworkAclsOutput, error) { return &m.DescribeNetworkAclsOutput, nil } -func (m mockedNetworkACL) DeleteNetworkAcl(_ *ec2.DeleteNetworkAclInput) (*ec2.DeleteNetworkAclOutput, error) { - return &m.DeleteNetworkAclOutput, nil -} - -func (m mockedNetworkACL) DeleteNetworkAclWithContext(_ awsgo.Context, _ *ec2.DeleteNetworkAclInput, _ ...request.Option) (*ec2.DeleteNetworkAclOutput, error) { +func (m *mockedNetworkACL) DeleteNetworkAcl(ctx context.Context, params *ec2.DeleteNetworkAclInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNetworkAclOutput, error) { return &m.DeleteNetworkAclOutput, nil } -func (m mockedNetworkACL) ReplaceNetworkAclAssociation(_ *ec2.ReplaceNetworkAclAssociationInput) (*ec2.ReplaceNetworkAclAssociationOutput, error) { +func (m *mockedNetworkACL) ReplaceNetworkAclAssociation(ctx context.Context, params *ec2.ReplaceNetworkAclAssociationInput, optFns ...func(*ec2.Options)) (*ec2.ReplaceNetworkAclAssociationOutput, error) { return &m.ReplaceNetworkAclAssociationOutput, nil } func TestNetworkAcl_GetAll(t *testing.T) { - // Set excludeFirstSeenTag to false for testing ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false) var ( now = time.Now() - testId1 = "acl-09e36c45cbdbfb001" - testId2 = "acl-09e36c45cbdbfb002" + testId1 = awsgo.String("acl-09e36c45cbdbfb001") + testId2 = awsgo.String("acl-09e36c45cbdbfb002") testName1 = "cloud-nuke-acl-001" testName2 = "cloud-nuke-acl-002" ) resourceObject := NetworkACL{ - Client: mockedNetworkACL{ + Client: &mockedNetworkACL{ DescribeNetworkAclsOutput: ec2.DescribeNetworkAclsOutput{ - NetworkAcls: []*ec2.NetworkAcl{ + NetworkAcls: []types.NetworkAcl{ { - NetworkAclId: aws.String(testId1), - Tags: []*ec2.Tag{ + NetworkAclId: testId1, + Tags: []types.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)), }, }, }, { - NetworkAclId: aws.String(testId2), - Tags: []*ec2.Tag{ + NetworkAclId: testId2, + Tags: []types.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))), }, }, }, @@ -94,12 +84,12 @@ func TestNetworkAcl_GetAll(t *testing.T) { tests := map[string]struct { ctx context.Context configObj config.ResourceType - expected []string + expected []*string }{ "emptyFilter": { ctx: ctx, configObj: config.ResourceType{}, - expected: []string{testId1, testId2}, + expected: []*string{testId1, testId2}, }, "nameExclusionFilter": { ctx: ctx, @@ -109,7 +99,7 @@ func TestNetworkAcl_GetAll(t *testing.T) { RE: *regexp.MustCompile(testName1), }}}, }, - expected: []string{testId2}, + expected: []*string{testId2}, }, "nameInclusionFilter": { ctx: ctx, @@ -119,17 +109,15 @@ func TestNetworkAcl_GetAll(t *testing.T) { RE: *regexp.MustCompile(testName1), }}}, }, - expected: []string{testId1}, + expected: []*string{testId1}, }, "timeAfterExclusionFilter": { ctx: ctx, configObj: config.ResourceType{ ExcludeRule: config.FilterRule{ - TimeAfter: aws.Time(now), + TimeAfter: awsgo.Time(now), }}, - expected: []string{ - testId1, - }, + expected: []*string{testId1}, }, } for name, tc := range tests { @@ -138,14 +126,12 @@ func TestNetworkAcl_GetAll(t *testing.T) { NetworkACL: tc.configObj, }) require.NoError(t, err) - require.Equal(t, tc.expected, aws.StringValueSlice(names)) + require.Equal(t, tc.expected, names) }) } - } func TestNetworkAcl_NukeAll(t *testing.T) { - var ( testId1 = "acl-09e36c45cbdbfb001" testId2 = "acl-09e36c45cbdbfb002" @@ -155,42 +141,38 @@ func TestNetworkAcl_NukeAll(t *testing.T) { ) resourceObject := NetworkACL{ - BaseAwsResource: BaseAwsResource{ - Nukables: map[string]error{ - testId1: nil, - testId2: nil, - }, - }, - Client: mockedNetworkACL{ + Client: &mockedNetworkACL{ DescribeNetworkAclsOutput: ec2.DescribeNetworkAclsOutput{ - NetworkAcls: []*ec2.NetworkAcl{ + NetworkAcls: []types.NetworkAcl{ { - NetworkAclId: aws.String(testId1), - Associations: []*ec2.NetworkAclAssociation{ + NetworkAclId: awsgo.String(testId1), + Associations: []types.NetworkAclAssociation{ { - NetworkAclAssociationId: aws.String("assoc-09e36c45cbdbfb001"), - NetworkAclId: aws.String("acl-09e36c45cbdbfb001"), + NetworkAclAssociationId: awsgo.String("assoc-09e36c45cbdbfb001"), + NetworkAclId: awsgo.String(testId1), + SubnetId: awsgo.String("subnet-1234"), }, }, - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { - Key: aws.String("Name"), - Value: aws.String(testName1), + Key: awsgo.String("Name"), + Value: awsgo.String(testName1), }, }, }, { - NetworkAclId: aws.String(testId2), - Associations: []*ec2.NetworkAclAssociation{ + NetworkAclId: awsgo.String(testId2), + Associations: []types.NetworkAclAssociation{ { - NetworkAclAssociationId: aws.String("assoc-09e36c45cbdbfb002"), - NetworkAclId: aws.String("acl-09e36c45cbdbfb002"), + NetworkAclAssociationId: awsgo.String("assoc-09e36c45cbdbfb002"), + NetworkAclId: awsgo.String(testId2), + SubnetId: awsgo.String("subnet-5678"), }, }, - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { - Key: aws.String("Name"), - Value: aws.String(testName2), + Key: awsgo.String("Name"), + Value: awsgo.String(testName2), }, }, }, @@ -198,10 +180,9 @@ func TestNetworkAcl_NukeAll(t *testing.T) { }, }, } - err := resourceObject.nukeAll([]*string{ - aws.String(testId1), - aws.String(testId2), + awsgo.String(testId1), + awsgo.String(testId2), }) require.NoError(t, err) } diff --git a/aws/resources/ec2_network_acl_types.go b/aws/resources/ec2_network_acl_types.go index 7e171d56..7900994a 100644 --- a/aws/resources/ec2_network_acl_types.go +++ b/aws/resources/ec2_network_acl_types.go @@ -3,26 +3,31 @@ package resources import ( "context" - "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/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/go-commons/errors" ) +type NetworkACLAPI interface { + DescribeNetworkAcls(ctx context.Context, params *ec2.DescribeNetworkAclsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNetworkAclsOutput, error) + DeleteNetworkAcl(ctx context.Context, params *ec2.DeleteNetworkAclInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNetworkAclOutput, error) + ReplaceNetworkAclAssociation(ctx context.Context, params *ec2.ReplaceNetworkAclAssociationInput, optFns ...func(*ec2.Options)) (*ec2.ReplaceNetworkAclAssociationOutput, error) // Add this line +} + type NetworkACL struct { BaseAwsResource - Client ec2iface.EC2API + Client NetworkACLAPI Region string Ids []string } -func (nacl *NetworkACL) Init(session *session.Session) { - nacl.BaseAwsResource.Init(session) - nacl.Client = ec2.New(session) +func (nacl *NetworkACL) InitV2(cfg aws.Config) { + nacl.Client = ec2.NewFromConfig(cfg) } +func (nacl *NetworkACL) IsUsingV2() bool { return true } + func (nacl *NetworkACL) ResourceName() string { return "network-acl" } @@ -45,7 +50,7 @@ func (nacl *NetworkACL) GetAndSetIdentifiers(c context.Context, configObj config return nil, err } - nacl.Ids = aws.StringValueSlice(identifiers) + nacl.Ids = aws.ToStringSlice(identifiers) return nacl.Ids, nil } diff --git a/aws/resources/ec2_network_interface.go b/aws/resources/ec2_network_interface.go index 88cb4cd1..a763e7b4 100644 --- a/aws/resources/ec2_network_interface.go +++ b/aws/resources/ec2_network_interface.go @@ -4,9 +4,9 @@ import ( "context" "time" - 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" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" "github.com/gruntwork-io/cloud-nuke/report" @@ -14,10 +14,10 @@ import ( "github.com/gruntwork-io/go-commons/errors" ) -func shouldIncludeNetworkInterface(networkInterface *ec2.NetworkInterface, firstSeenTime *time.Time, configObj config.Config) bool { +func shouldIncludeNetworkInterface(networkInterface types.NetworkInterface, firstSeenTime *time.Time, configObj config.Config) bool { var interfaceName string // get the tags as map - tagMap := util.ConvertEC2TagsToMap(networkInterface.TagSet) + tagMap := util.ConvertTypesTagsToMap(networkInterface.TagSet) if name, ok := tagMap["Name"]; ok { interfaceName = name } @@ -34,14 +34,14 @@ func (ni *NetworkInterface) getAll(c context.Context, configObj config.Config) ( var firstSeenTime *time.Time var err error - resp, err := ni.Client.DescribeNetworkInterfacesWithContext(ni.Context, &ec2.DescribeNetworkInterfacesInput{}) + resp, err := ni.Client.DescribeNetworkInterfaces(ni.Context, &ec2.DescribeNetworkInterfacesInput{}) if err != nil { logging.Debugf("[Internet Gateway] Failed to list internet gateways: %s", err) return nil, err } for _, networkInterface := range resp.NetworkInterfaces { - firstSeenTime, err = util.GetOrCreateFirstSeen(c, ni.Client, networkInterface.NetworkInterfaceId, util.ConvertEC2TagsToMap(networkInterface.TagSet)) + firstSeenTime, err = util.GetOrCreateFirstSeen(c, ni.Client, networkInterface.NetworkInterfaceId, util.ConvertTypesTagsToMap(networkInterface.TagSet)) if err != nil { logging.Error("unable to retrieve first seen tag") continue @@ -51,10 +51,10 @@ func (ni *NetworkInterface) getAll(c context.Context, configObj config.Config) ( // Interfaces attached to Lambda or other AWS services may have specific detachment mechanisms managed by // those services. Attempting to detach these via the API can cause errors. Skipping non-interface types // ensures they are cleaned up automatically upon service deletion. - if awsgo.StringValue(networkInterface.InterfaceType) != NetworkInterfaceTypeInterface { + if networkInterface.InterfaceType != NetworkInterfaceTypeInterface { logging.Debugf("[Skip] Can't detach network interface of type '%v' via API. "+ "Detachment for this type is managed by the dependent service and will occur automatically upon "+ - "resource deletion.", awsgo.StringValue(networkInterface.InterfaceType)) + "resource deletion.", networkInterface.InterfaceType) continue } @@ -65,7 +65,7 @@ func (ni *NetworkInterface) getAll(c context.Context, configObj config.Config) ( // Check and verify the list of allowed nuke actions ni.VerifyNukablePermissions(identifiers, func(id *string) error { - _, err := ni.Client.DeleteNetworkInterfaceWithContext(ni.Context, &ec2.DeleteNetworkInterfaceInput{ + _, err := ni.Client.DeleteNetworkInterface(ni.Context, &ec2.DeleteNetworkInterfaceInput{ NetworkInterfaceId: id, DryRun: awsgo.Bool(true), }) @@ -89,13 +89,13 @@ func (ni *NetworkInterface) nuke(id *string) error { } func (ni *NetworkInterface) detachNetworkInterface(id *string) error { - logging.Debugf("Detaching network interface %s from instances ", awsgo.StringValue(id)) + logging.Debugf("Detaching network interface %s from instances ", awsgo.ToString(id)) - output, err := ni.Client.DescribeNetworkInterfacesWithContext(ni.Context, &ec2.DescribeNetworkInterfacesInput{ - NetworkInterfaceIds: []*string{id}, + output, err := ni.Client.DescribeNetworkInterfaces(ni.Context, &ec2.DescribeNetworkInterfacesInput{ + NetworkInterfaceIds: []string{*id}, }) if err != nil { - logging.Debugf("[Failed] Error describing network interface %s: %s", awsgo.StringValue(id), err) + logging.Debugf("[Failed] Error describing network interface %s: %s", awsgo.ToString(id), err) return errors.WithStackTrace(err) } @@ -109,7 +109,7 @@ func (ni *NetworkInterface) detachNetworkInterface(id *string) error { // nuking the attached instance // this will also remove the network interface if err := ni.nukeInstance(networkInterface.Attachment.InstanceId); err != nil { - logging.Debugf("[Failed] Error nuking the attached instance %s on network interface %s %s", awsgo.StringValue(networkInterface.Attachment.InstanceId), awsgo.StringValue(id), err) + logging.Debugf("[Failed] Error nuking the attached instance %s on network interface %s %s", awsgo.ToString(networkInterface.Attachment.InstanceId), awsgo.ToString(id), err) return errors.WithStackTrace(err) } } @@ -120,14 +120,14 @@ func (ni *NetworkInterface) detachNetworkInterface(id *string) error { } func (ni *NetworkInterface) releaseEIPs(instance *string) error { - logging.Debugf("Releasing Elastic IP address(s) associated on instance %s", awsgo.StringValue(instance)) + logging.Debugf("Releasing Elastic IP address(s) associated on instance %s", awsgo.ToString(instance)) // get the elastic ip's associated with the EC2's - output, err := ni.Client.DescribeAddressesWithContext(ni.Context, &ec2.DescribeAddressesInput{ - Filters: []*ec2.Filter{ + output, err := ni.Client.DescribeAddresses(ni.Context, &ec2.DescribeAddressesInput{ + Filters: []types.Filter{ { Name: awsgo.String("instance-id"), - Values: []*string{ - instance, + Values: []string{ + *instance, }, }, }, @@ -137,14 +137,14 @@ func (ni *NetworkInterface) releaseEIPs(instance *string) error { } for _, address := range output.Addresses { - if _, err := ni.Client.ReleaseAddressWithContext(ni.Context, &ec2.ReleaseAddressInput{ + if _, err := ni.Client.ReleaseAddress(ni.Context, &ec2.ReleaseAddressInput{ AllocationId: address.AllocationId, }); err != nil { - logging.Debugf("An error happened while releasing the elastic ip address %s, error %v", awsgo.StringValue(address.AllocationId), err) + logging.Debugf("An error happened while releasing the elastic ip address %s, error %v", awsgo.ToString(address.AllocationId), err) continue } - logging.Debugf("Released Elastic IP address %s from instance %s", awsgo.StringValue(address.AllocationId), awsgo.StringValue(instance)) + logging.Debugf("Released Elastic IP address %s from instance %s", awsgo.ToString(address.AllocationId), awsgo.ToString(instance)) } logging.Debugf("[OK] successfully released Elastic IP address(s) associated on instances") @@ -153,32 +153,34 @@ func (ni *NetworkInterface) releaseEIPs(instance *string) error { } func (ni *NetworkInterface) nukeInstance(id *string) error { - - // Needs to release the elastic ips attached on the instance before nuking + // Release the elastic IPs attached to the instance before nuking if err := ni.releaseEIPs(id); err != nil { logging.Debugf("[Failed EIP release] %s", err) return errors.WithStackTrace(err) } - // terminating the instance - if _, err := ni.Client.TerminateInstancesWithContext(ni.Context, &ec2.TerminateInstancesInput{ - InstanceIds: []*string{id}, - }); err != nil { - logging.Debugf("[Failed] Ec2 termination %s", err) + // Terminate the instance + _, err := ni.Client.TerminateInstances(ni.Context, &ec2.TerminateInstancesInput{ + InstanceIds: []string{*id}, + }) + if err != nil { + logging.Debugf("[Failed] EC2 termination %s", err) return errors.WithStackTrace(err) } - logging.Debugf("[Instance Termination] waiting to terminate instance %s", awsgo.StringValue(id)) + logging.Debugf("[Instance Termination] waiting to terminate instance %s", awsgo.ToString(id)) - // wait until the instance terminated. - if err := ni.Client.WaitUntilInstanceTerminatedWithContext(ni.Context, &ec2.DescribeInstancesInput{ - InstanceIds: []*string{id}, - }); err != nil { + // Use the NewInstanceTerminatedWaiter + waiter := ec2.NewInstanceTerminatedWaiter(ni.Client) + err = waiter.Wait(ni.Context, &ec2.DescribeInstancesInput{ + InstanceIds: []string{*id}, + }, 5*time.Minute) + if err != nil { logging.Debugf("[Instance Termination Waiting] Failed to terminate instance %s : %s", *id, err) return errors.WithStackTrace(err) } - logging.Debugf("[OK] successfully nuked instance %v", awsgo.StringValue(id)) + logging.Debugf("[OK] successfully nuked instance %v", awsgo.ToString(id)) return nil } @@ -201,7 +203,7 @@ func (ni *NetworkInterface) nukeAll(identifiers []*string) error { // Record status of this resource e := report.Entry{ // Use the 'r' alias to refer to the package - Identifier: awsgo.StringValue(id), + Identifier: awsgo.ToString(id), ResourceType: "Network Interface", Error: err, } @@ -217,8 +219,8 @@ func (ni *NetworkInterface) nukeAll(identifiers []*string) error { return nil } -func nukeNetworkInterface(client ec2iface.EC2API, id *string) error { - logging.Debugf("Deleting network interface %s", awsgo.StringValue(id)) +func nukeNetworkInterface(client NetworkInterfaceAPI, id *string) error { + logging.Debugf("Deleting network interface %s", awsgo.ToString(id)) // If the network interface was attached to an instance, then when we remove the instance above, the network interface will also be removed. // However, when we attempt to nuke the interface here, we may encounter an error such as InvalidNetworkInterfaceID.NotFound. @@ -226,16 +228,16 @@ func nukeNetworkInterface(client ec2iface.EC2API, id *string) error { // // Note: We are handling the situation here by checking the error response from AWS. - _, err := client.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ + _, err := client.DeleteNetworkInterface(context.Background(), &ec2.DeleteNetworkInterfaceInput{ NetworkInterfaceId: id, }) // check the error exists and it is not the interfaceid not found if err != nil && util.TransformAWSError(err) != util.ErrInterfaceIDNotFound { - logging.Debugf("[Failed] Error deleting network interface %s: %s", awsgo.StringValue(id), err) + logging.Debugf("[Failed] Error deleting network interface %s: %s", awsgo.ToString(id), err) return errors.WithStackTrace(err) } - logging.Debugf("[Ok] network interface deleted successfully %s", awsgo.StringValue(id)) + logging.Debugf("[Ok] network interface deleted successfully %s", awsgo.ToString(id)) return nil } diff --git a/aws/resources/ec2_network_interface_test.go b/aws/resources/ec2_network_interface_test.go index 21b5bf37..3fb53614 100644 --- a/aws/resources/ec2_network_interface_test.go +++ b/aws/resources/ec2_network_interface_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - awsgo "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/smithy-go" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/util" "github.com/stretchr/testify/require" @@ -17,41 +17,35 @@ import ( type mockedNetworkInterface struct { BaseAwsResource - ec2iface.EC2API + NetworkInterfaceAPI DescribeNetworkInterfacesOutput ec2.DescribeNetworkInterfacesOutput DeleteNetworkInterfaceOutput ec2.DeleteNetworkInterfaceOutput DescribeAddressesOutput ec2.DescribeAddressesOutput TerminateInstancesOutput ec2.TerminateInstancesOutput ReleaseAddressOutput ec2.ReleaseAddressOutput + DescribeNetworkInterfacesError error } -func (m mockedNetworkInterface) DescribeNetworkInterfacesWithContext(_ awsgo.Context, _ *ec2.DescribeNetworkInterfacesInput, _ ...request.Option) (*ec2.DescribeNetworkInterfacesOutput, error) { - return &m.DescribeNetworkInterfacesOutput, nil +func (m mockedNetworkInterface) DescribeNetworkInterfaces(ctx context.Context, params *ec2.DescribeNetworkInterfacesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) { + return &m.DescribeNetworkInterfacesOutput, m.DescribeNetworkInterfacesError } -func (m mockedNetworkInterface) DeleteNetworkInterface(_ *ec2.DeleteNetworkInterfaceInput) (*ec2.DeleteNetworkInterfaceOutput, error) { - return &m.DeleteNetworkInterfaceOutput, nil -} -func (m mockedNetworkInterface) DeleteNetworkInterfaceWithContext(_ awsgo.Context, _ *ec2.DeleteNetworkInterfaceInput, _ ...request.Option) (*ec2.DeleteNetworkInterfaceOutput, error) { +func (m mockedNetworkInterface) DeleteNetworkInterface(ctx context.Context, params *ec2.DeleteNetworkInterfaceInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNetworkInterfaceOutput, error) { return &m.DeleteNetworkInterfaceOutput, nil } -func (m mockedNetworkInterface) DescribeAddressesWithContext(_ awsgo.Context, _ *ec2.DescribeAddressesInput, _ ...request.Option) (*ec2.DescribeAddressesOutput, error) { +func (m mockedNetworkInterface) DescribeAddresses(ctx context.Context, params *ec2.DescribeAddressesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error) { return &m.DescribeAddressesOutput, nil } -func (m mockedNetworkInterface) ReleaseAddressWithContext(_ awsgo.Context, _ *ec2.ReleaseAddressInput, _ ...request.Option) (*ec2.ReleaseAddressOutput, error) { +func (m mockedNetworkInterface) ReleaseAddress(ctx context.Context, params *ec2.ReleaseAddressInput, optFns ...func(*ec2.Options)) (*ec2.ReleaseAddressOutput, error) { return &m.ReleaseAddressOutput, nil } -func (m mockedNetworkInterface) TerminateInstancesWithContext(_ awsgo.Context, _ *ec2.TerminateInstancesInput, _ ...request.Option) (*ec2.TerminateInstancesOutput, error) { +func (m mockedNetworkInterface) TerminateInstances(ctx context.Context, params *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) { return &m.TerminateInstancesOutput, nil } -func (m mockedNetworkInterface) WaitUntilInstanceTerminatedWithContext(_ awsgo.Context, _ *ec2.DescribeInstancesInput, _ ...request.WaiterOption) error { - return nil -} - func TestNetworkInterface_GetAll(t *testing.T) { // Set excludeFirstSeenTag to false for testing @@ -69,11 +63,11 @@ func TestNetworkInterface_GetAll(t *testing.T) { resourceObject := NetworkInterface{ Client: mockedNetworkInterface{ DescribeNetworkInterfacesOutput: ec2.DescribeNetworkInterfacesOutput{ - NetworkInterfaces: []*ec2.NetworkInterface{ + NetworkInterfaces: []types.NetworkInterface{ { NetworkInterfaceId: awsgo.String(testId1), - InterfaceType: awsgo.String(NetworkInterfaceTypeInterface), - TagSet: []*ec2.Tag{ + InterfaceType: NetworkInterfaceTypeInterface, + TagSet: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName1), @@ -86,8 +80,8 @@ func TestNetworkInterface_GetAll(t *testing.T) { }, { NetworkInterfaceId: awsgo.String(testId2), - InterfaceType: awsgo.String(NetworkInterfaceTypeInterface), - TagSet: []*ec2.Tag{ + InterfaceType: NetworkInterfaceTypeInterface, + TagSet: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName2), @@ -151,7 +145,7 @@ func TestNetworkInterface_GetAll(t *testing.T) { NetworkInterface: tc.configObj, }) require.NoError(t, err) - require.Equal(t, tc.expected, awsgo.StringValueSlice(names)) + require.Equal(t, tc.expected, awsgo.ToStringSlice(names)) }) } @@ -179,31 +173,31 @@ func TestNetworkInterface_NukeAll(t *testing.T) { Client: mockedNetworkInterface{ DeleteNetworkInterfaceOutput: ec2.DeleteNetworkInterfaceOutput{}, DescribeNetworkInterfacesOutput: ec2.DescribeNetworkInterfacesOutput{ - NetworkInterfaces: []*ec2.NetworkInterface{ + NetworkInterfaces: []types.NetworkInterface{ { NetworkInterfaceId: awsgo.String(testId1), - InterfaceType: awsgo.String(NetworkInterfaceTypeInterface), - TagSet: []*ec2.Tag{ + InterfaceType: NetworkInterfaceTypeInterface, + TagSet: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName1), }, }, - Attachment: &ec2.NetworkInterfaceAttachment{ + Attachment: &types.NetworkInterfaceAttachment{ AttachmentId: awsgo.String("network-attachment-09e36c45cbdbfb001"), InstanceId: awsgo.String("ec2-instance-09e36c45cbdbfb001"), }, }, { NetworkInterfaceId: awsgo.String(testId2), - InterfaceType: awsgo.String(NetworkInterfaceTypeInterface), - TagSet: []*ec2.Tag{ + InterfaceType: NetworkInterfaceTypeInterface, + TagSet: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName2), }, }, - Attachment: &ec2.NetworkInterfaceAttachment{ + Attachment: &types.NetworkInterfaceAttachment{ AttachmentId: awsgo.String("network-attachment-09e36c45cbdbfb002"), InstanceId: awsgo.String("ec2-instance-09e36c45cbdbfb002"), }, @@ -211,7 +205,7 @@ func TestNetworkInterface_NukeAll(t *testing.T) { }, }, DescribeAddressesOutput: ec2.DescribeAddressesOutput{ - Addresses: []*ec2.Address{ + Addresses: []types.Address{ { AllocationId: awsgo.String("ec2-addr-alloc-09e36c45cbdbfb001"), InstanceId: awsgo.String("ec2-instance-09e36c45cbdbfb001"), @@ -224,8 +218,12 @@ func TestNetworkInterface_NukeAll(t *testing.T) { }, TerminateInstancesOutput: ec2.TerminateInstancesOutput{}, ReleaseAddressOutput: ec2.ReleaseAddressOutput{}, + DescribeNetworkInterfacesError: &smithy.GenericAPIError{ + Code: "terminated", + }, }, } + resourceObject.Context = context.Background() err := resourceObject.nukeAll([]*string{ awsgo.String(testId1), diff --git a/aws/resources/ec2_network_interface_types.go b/aws/resources/ec2_network_interface_types.go index aba29cf0..f5811606 100644 --- a/aws/resources/ec2_network_interface_types.go +++ b/aws/resources/ec2_network_interface_types.go @@ -3,10 +3,8 @@ package resources import ( "context" - "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/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/go-commons/errors" ) @@ -15,18 +13,27 @@ const ( NetworkInterfaceTypeInterface = "interface" ) +type NetworkInterfaceAPI interface { + DescribeNetworkInterfaces(ctx context.Context, params *ec2.DescribeNetworkInterfacesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) + DeleteNetworkInterface(ctx context.Context, params *ec2.DeleteNetworkInterfaceInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNetworkInterfaceOutput, error) + DescribeAddresses(ctx context.Context, params *ec2.DescribeAddressesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error) + ReleaseAddress(ctx context.Context, params *ec2.ReleaseAddressInput, optFns ...func(*ec2.Options)) (*ec2.ReleaseAddressOutput, error) + TerminateInstances(ctx context.Context, params *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) + DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) +} type NetworkInterface struct { BaseAwsResource - Client ec2iface.EC2API + Client NetworkInterfaceAPI Region string InterfaceIds []string } -func (ni *NetworkInterface) Init(session *session.Session) { - ni.BaseAwsResource.Init(session) - ni.Client = ec2.New(session) +func (ni *NetworkInterface) InitV2(cfg aws.Config) { + ni.Client = ec2.NewFromConfig(cfg) } +func (ni *NetworkInterface) IsUsingV2() bool { return true } + func (ni *NetworkInterface) ResourceName() string { return "network-interface" } @@ -49,7 +56,7 @@ func (ni *NetworkInterface) GetAndSetIdentifiers(c context.Context, configObj co return nil, err } - ni.InterfaceIds = aws.StringValueSlice(identifiers) + ni.InterfaceIds = aws.ToStringSlice(identifiers) return ni.InterfaceIds, nil } diff --git a/aws/resources/ec2_subnet.go b/aws/resources/ec2_subnet.go index 70b62a71..73b8b6d2 100644 --- a/aws/resources/ec2_subnet.go +++ b/aws/resources/ec2_subnet.go @@ -3,12 +3,11 @@ package resources import ( "context" "fmt" - "strconv" "time" - 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" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" "github.com/gruntwork-io/cloud-nuke/report" @@ -16,9 +15,9 @@ import ( "github.com/gruntwork-io/go-commons/errors" ) -func shouldIncludeEc2Subnet(subnet *ec2.Subnet, firstSeenTime *time.Time, configObj config.Config) bool { +func shouldIncludeEc2Subnet(subnet types.Subnet, firstSeenTime *time.Time, configObj config.Config) bool { var subnetName string - tagMap := util.ConvertEC2TagsToMap(subnet.Tags) + tagMap := util.ConvertTypesTagsToMap(subnet.Tags) if name, ok := tagMap["Name"]; ok { subnetName = name } @@ -33,48 +32,50 @@ func shouldIncludeEc2Subnet(subnet *ec2.Subnet, firstSeenTime *time.Time, config // Returns a formatted string of EC2 subnets func (ec2subnet *EC2Subnet) getAll(c context.Context, configObj config.Config) ([]*string, error) { result := []*string{} - var firstSeenTime *time.Time - var err error - // Note: This filter initially handles non-default resources and can be overridden by passing the only-default filter to choose default subnets. + // Configure filters + var filters []types.Filter if configObj.EC2Subnet.DefaultOnly { logging.Debugf("[default only] Retrieving the default subnets") + filters = append(filters, types.Filter{ + Name: awsgo.String("default-for-az"), + Values: []string{"true"}, + }) } - err = ec2subnet.Client.DescribeSubnetsPagesWithContext(ec2subnet.Context, &ec2.DescribeSubnetsInput{ - Filters: []*ec2.Filter{ - { - Name: awsgo.String("default-for-az"), - Values: []*string{ - awsgo.String(strconv.FormatBool(configObj.EC2Subnet.DefaultOnly)), // convert the bool status into string - }, - }, - }, - }, func(pages *ec2.DescribeSubnetsOutput, lastPage bool) bool { - for _, subnet := range pages.Subnets { - firstSeenTime, err = util.GetOrCreateFirstSeen(c, ec2subnet.Client, subnet.SubnetId, util.ConvertEC2TagsToMap(subnet.Tags)) + // Create paginator + paginator := ec2.NewDescribeSubnetsPaginator(ec2subnet.Client, &ec2.DescribeSubnetsInput{ + Filters: filters, + }) + + // Iterate through pages + for paginator.HasMorePages() { + page, err := paginator.NextPage(c) + if err != nil { + return nil, errors.WithStackTrace(err) + } + + // Process subnets in the current page + for _, subnet := range page.Subnets { + firstSeenTime, err := util.GetOrCreateFirstSeen(c, ec2subnet.Client, subnet.SubnetId, util.ConvertTypesTagsToMap(subnet.Tags)) if err != nil { logging.Error("unable to retrieve first seen tag") continue } + if shouldIncludeEc2Subnet(subnet, firstSeenTime, configObj) { result = append(result, subnet.SubnetId) } } - return !lastPage - }) - - if err != nil { - return nil, errors.WithStackTrace(err) } - // check the resources are nukable + // Check if resources are nukable ec2subnet.VerifyNukablePermissions(result, func(id *string) error { params := &ec2.DeleteSubnetInput{ SubnetId: id, - DryRun: awsgo.Bool(true), // dry run set as true , checks permission without actually making the request + DryRun: awsgo.Bool(true), // Check permissions without making the actual request } - _, err := ec2subnet.Client.DeleteSubnetWithContext(ec2subnet.Context, params) + _, err := ec2subnet.Client.DeleteSubnet(c, params) return err }) @@ -105,7 +106,7 @@ func (ec2subnet *EC2Subnet) nukeAll(ids []*string) error { err := nukeSubnet(ec2subnet.Client, id) // Record status of this resource e := report.Entry{ - Identifier: awsgo.StringValue(id), + Identifier: awsgo.ToString(id), ResourceType: "Subnet", Error: err, } @@ -124,20 +125,20 @@ func (ec2subnet *EC2Subnet) nukeAll(ids []*string) error { return nil } -func nukeSubnet(client ec2iface.EC2API, id *string) error { +func nukeSubnet(client EC2SubnetAPI, id *string) error { logging.Debug(fmt.Sprintf("Deleting subnet %s", - awsgo.StringValue(id))) + awsgo.ToString(id))) - _, err := client.DeleteSubnet(&ec2.DeleteSubnetInput{ + _, err := client.DeleteSubnet(context.Background(), &ec2.DeleteSubnetInput{ SubnetId: id, }) if err != nil { logging.Debug(fmt.Sprintf("Failed to delete subnet %s", - awsgo.StringValue(id))) + awsgo.ToString(id))) return errors.WithStackTrace(err) } logging.Debug(fmt.Sprintf("Successfully deleted subnet %s", - awsgo.StringValue(id))) + awsgo.ToString(id))) return nil } diff --git a/aws/resources/ec2_subnet_test.go b/aws/resources/ec2_subnet_test.go index ffda15a6..36878af6 100644 --- a/aws/resources/ec2_subnet_test.go +++ b/aws/resources/ec2_subnet_test.go @@ -6,27 +6,24 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - awsgo "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/util" "github.com/stretchr/testify/require" ) type mockedEC2Subnets struct { - ec2iface.EC2API + EC2SubnetAPI DescribeSubnetsOutput ec2.DescribeSubnetsOutput DeleteSubnetOutput ec2.DeleteSubnetOutput } -func (m mockedEC2Subnets) DescribeSubnetsPagesWithContext(_ awsgo.Context, _ *ec2.DescribeSubnetsInput, callback func(*ec2.DescribeSubnetsOutput, bool) bool, _ ...request.Option) error { - callback(&m.DescribeSubnetsOutput, true) - return nil +func (m mockedEC2Subnets) DescribeSubnets(ctx context.Context, params *ec2.DescribeSubnetsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) { + return &m.DescribeSubnetsOutput, nil } -func (m mockedEC2Subnets) DeleteSubnetWithContext(_ awsgo.Context, _ *ec2.DeleteSubnetInput, _ ...request.Option) (*ec2.DeleteSubnetOutput, error) { +func (m mockedEC2Subnets) DeleteSubnet(ctx context.Context, params *ec2.DeleteSubnetInput, optFns ...func(*ec2.Options)) (*ec2.DeleteSubnetOutput, error) { return &m.DeleteSubnetOutput, nil } @@ -48,10 +45,10 @@ func TestEc2Subnets_GetAll(t *testing.T) { ec2subnet := EC2Subnet{ Client: mockedEC2Subnets{ DescribeSubnetsOutput: ec2.DescribeSubnetsOutput{ - Subnets: []*ec2.Subnet{ + Subnets: []types.Subnet{ { SubnetId: aws.String(subnet1), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: aws.String("Name"), Value: aws.String(testName1), @@ -63,7 +60,7 @@ func TestEc2Subnets_GetAll(t *testing.T) { }, { SubnetId: aws.String(subnet2), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: aws.String("Name"), Value: aws.String(testName2), @@ -119,7 +116,7 @@ func TestEc2Subnets_GetAll(t *testing.T) { EC2Subnet: tc.configObj, }) require.NoError(t, err) - require.Equal(t, tc.expected, aws.StringValueSlice(names)) + require.Equal(t, tc.expected, aws.ToStringSlice(names)) }) } diff --git a/aws/resources/ec2_subnet_types.go b/aws/resources/ec2_subnet_types.go index fff9c4eb..d6634919 100644 --- a/aws/resources/ec2_subnet_types.go +++ b/aws/resources/ec2_subnet_types.go @@ -3,27 +3,31 @@ package resources import ( "context" - 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/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/go-commons/errors" ) +type EC2SubnetAPI interface { + DeleteSubnet(ctx context.Context, params *ec2.DeleteSubnetInput, optFns ...func(*ec2.Options)) (*ec2.DeleteSubnetOutput, error) + DescribeSubnets(ctx context.Context, params *ec2.DescribeSubnetsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) +} + // Ec2Subnet- represents all Subnets type EC2Subnet struct { BaseAwsResource - Client ec2iface.EC2API + Client EC2SubnetAPI Region string Subnets []string } -func (es *EC2Subnet) Init(session *session.Session) { - es.BaseAwsResource.Init(session) - es.Client = ec2.New(session) +func (es *EC2Subnet) InitV2(cfg aws.Config) { + es.Client = ec2.NewFromConfig(cfg) } +func (es *EC2Subnet) IsUsingV2() bool { return true } + // ResourceName - the simple name of the aws resource func (es *EC2Subnet) ResourceName() string { return "ec2-subnet" @@ -45,13 +49,13 @@ func (es *EC2Subnet) GetAndSetIdentifiers(c context.Context, configObj config.Co return nil, err } - es.Subnets = awsgo.StringValueSlice(identifiers) + es.Subnets = aws.ToStringSlice(identifiers) return es.Subnets, nil } // Nuke - nuke 'em all!!! func (es *EC2Subnet) Nuke(identifiers []string) error { - if err := es.nukeAll(awsgo.StringSlice(identifiers)); err != nil { + if err := es.nukeAll(aws.StringSlice(identifiers)); err != nil { return errors.WithStackTrace(err) } diff --git a/aws/resources/ec2_vpc.go b/aws/resources/ec2_vpc.go index d3599c64..12eaa629 100644 --- a/aws/resources/ec2_vpc.go +++ b/aws/resources/ec2_vpc.go @@ -9,16 +9,16 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" - "github.com/aws/aws-sdk-go/service/elbv2" - "github.com/aws/aws-sdk-go/service/elbv2/elbv2iface" "github.com/gruntwork-io/cloud-nuke/util" "github.com/pterm/pterm" "github.com/gruntwork-io/go-commons/retry" - awsgo "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" "github.com/gruntwork-io/cloud-nuke/report" @@ -30,13 +30,11 @@ func (v *EC2VPCs) getAll(c context.Context, configObj config.Config) ([]*string, var firstSeenTime *time.Time var err error // Note: This filter initially handles non-default resources and can be overridden by passing the only-default filter to choose default VPCs. - result, err := v.Client.DescribeVpcsWithContext(v.Context, &ec2.DescribeVpcsInput{ - Filters: []*ec2.Filter{ + result, err := v.Client.DescribeVpcs(v.Context, &ec2.DescribeVpcsInput{ + Filters: []types.Filter{ { - Name: awsgo.String("is-default"), - Values: []*string{ - awsgo.String(strconv.FormatBool(configObj.VPC.DefaultOnly)), // convert the bool status into string - }, + Name: awsgo.String("is-default"), + Values: []string{strconv.FormatBool(configObj.VPC.DefaultOnly)}, // convert the bool status into string }, }, }) @@ -46,7 +44,7 @@ func (v *EC2VPCs) getAll(c context.Context, configObj config.Config) ([]*string, var ids []*string for _, vpc := range result.Vpcs { - firstSeenTime, err = util.GetOrCreateFirstSeen(c, v.Client, vpc.VpcId, util.ConvertEC2TagsToMap(vpc.Tags)) + firstSeenTime, err = util.GetOrCreateFirstSeen(c, v.Client, vpc.VpcId, util.ConvertTypesTagsToMap(vpc.Tags)) if err != nil { logging.Error("Unable to retrieve tags") return nil, errors.WithStackTrace(err) @@ -62,7 +60,7 @@ func (v *EC2VPCs) getAll(c context.Context, configObj config.Config) ([]*string, // checking the nukable permissions v.VerifyNukablePermissions(ids, func(id *string) error { - _, err := v.Client.DeleteVpc(&ec2.DeleteVpcInput{ + _, err := v.Client.DeleteVpc(c, &ec2.DeleteVpcInput{ VpcId: id, DryRun: awsgo.Bool(true), }) @@ -113,7 +111,7 @@ func (v *EC2VPCs) nukeAll(vpcIds []string) error { return nil } -func nuke(client ec2iface.EC2API, elbClient elbv2iface.ELBV2API, vpcID string) error { +func nuke(client EC2VPCAPI, elbClient ELBClientAPI, vpcID string) error { var err error // Note: order is quite important, otherwise you will encounter dependency violation errors. @@ -178,12 +176,12 @@ func nuke(client ec2iface.EC2API, elbClient elbv2iface.ELBV2API, vpcID string) e // Wait a maximum of 5 minutes: 10 seconds in between, up to 30 times 30, 10*time.Second, func() error { - interfaces, err := client.DescribeNetworkInterfaces( + interfaces, err := client.DescribeNetworkInterfaces(context.Background(), &ec2.DescribeNetworkInterfacesInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("vpc-id"), - Values: []*string{awsgo.String(vpcID)}, + Values: []string{vpcID}, }, }, }, @@ -251,68 +249,66 @@ func nuke(client ec2iface.EC2API, elbClient elbv2iface.ELBV2API, vpcID string) e return nil } -func nukePeeringConnections(client ec2iface.EC2API, vpcID string) error { +func nukePeeringConnections(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Finding VPC peering connections to nuke for: %s", vpcID)) peerConnections := []*string{} vpcIds := []string{vpcID} - requesterFilters := []*ec2.Filter{ + requesterFilters := []types.Filter{ { Name: awsgo.String("requester-vpc-info.vpc-id"), - Values: awsgo.StringSlice(vpcIds), + Values: vpcIds, }, { Name: awsgo.String("status-code"), - Values: awsgo.StringSlice([]string{"active"}), + Values: []string{"active"}, }, } - accepterFilters := []*ec2.Filter{ + accepterFilters := []types.Filter{ { Name: awsgo.String("accepter-vpc-info.vpc-id"), - Values: awsgo.StringSlice(vpcIds), + Values: vpcIds, }, { Name: awsgo.String("status-code"), - Values: awsgo.StringSlice([]string{"active"}), + Values: []string{"active"}, }, } // check the peering connection as requester - err := client.DescribeVpcPeeringConnectionsPages( - &ec2.DescribeVpcPeeringConnectionsInput{ - Filters: requesterFilters, - }, - func(page *ec2.DescribeVpcPeeringConnectionsOutput, lastPage bool) bool { - for _, connection := range page.VpcPeeringConnections { - peerConnections = append(peerConnections, connection.VpcPeeringConnectionId) - } - return !lastPage - }, - ) - if err != nil { - logging.Debug(fmt.Sprintf("Failed to describe vpc peering connections for vpc as requester: %s", vpcID)) - return errors.WithStackTrace(err) + paginator := ec2.NewDescribeVpcPeeringConnectionsPaginator(client, &ec2.DescribeVpcPeeringConnectionsInput{ + Filters: requesterFilters, + }) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(context.Background()) + if err != nil { + logging.Debug(fmt.Sprintf("Failed to describe VPC peering connections for VPC as requester: %s", vpcID)) + return errors.WithStackTrace(err) + } + for _, connection := range page.VpcPeeringConnections { + peerConnections = append(peerConnections, connection.VpcPeeringConnectionId) + } } // check the peering connection as accepter - err = client.DescribeVpcPeeringConnectionsPages( - &ec2.DescribeVpcPeeringConnectionsInput{ - Filters: accepterFilters, - }, - func(page *ec2.DescribeVpcPeeringConnectionsOutput, lastPage bool) bool { - for _, connection := range page.VpcPeeringConnections { - peerConnections = append(peerConnections, connection.VpcPeeringConnectionId) - } - return !lastPage - }, - ) - if err != nil { - logging.Debug(fmt.Sprintf("Failed to describe vpc peering connections for vpc as accepter: %s", vpcID)) - return errors.WithStackTrace(err) + paginator = ec2.NewDescribeVpcPeeringConnectionsPaginator(client, &ec2.DescribeVpcPeeringConnectionsInput{ + Filters: accepterFilters, + }) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(context.Background()) + if err != nil { + logging.Debug(fmt.Sprintf("Failed to describe VPC peering connections for VPC as accepter: %s", vpcID)) + return errors.WithStackTrace(err) + } + for _, connection := range page.VpcPeeringConnections { + peerConnections = append(peerConnections, connection.VpcPeeringConnectionId) + } } logging.Debug(fmt.Sprintf("Found %d VPC Peering connections to Nuke.", len(peerConnections))) for _, connection := range peerConnections { - _, err := client.DeleteVpcPeeringConnection(&ec2.DeleteVpcPeeringConnectionInput{ + _, err := client.DeleteVpcPeeringConnection(context.Background(), &ec2.DeleteVpcPeeringConnectionInput{ VpcPeeringConnectionId: connection, }) if err != nil { @@ -326,17 +322,17 @@ func nukePeeringConnections(client ec2iface.EC2API, vpcID string) error { // nukeVpcInternetGateways // This function is specifically for VPCs. It retrieves all the internet gateways attached to the given VPC ID and nuke them -func nukeInternetGateways(client ec2iface.EC2API, vpcID string) error { +func nukeInternetGateways(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Start nuking Internet Gateway for vpc: %s", vpcID)) input := &ec2.DescribeInternetGatewaysInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("attachment.vpc-id"), - Values: []*string{awsgo.String(vpcID)}, + Values: []string{vpcID}, }, }, } - igw, err := client.DescribeInternetGateways(input) + igw, err := client.DescribeInternetGateways(context.Background(), input) if err != nil { logging.Debug(fmt.Sprintf("Failed to describe internet gateways for vpc: %s", vpcID)) return errors.WithStackTrace(err) @@ -351,33 +347,32 @@ func nukeInternetGateways(client ec2iface.EC2API, vpcID string) error { err = nukeInternetGateway(client, igw.InternetGateways[0].InternetGatewayId, vpcID) if err != nil { logging.Debug(fmt.Sprintf("Failed to delete internet gateway %s", - awsgo.StringValue(igw.InternetGateways[0].InternetGatewayId))) + awsgo.ToString(igw.InternetGateways[0].InternetGatewayId))) return errors.WithStackTrace(err) } return nil } -func nukeEgressOnlyGateways(client ec2iface.EC2API, vpcID string) error { +func nukeEgressOnlyGateways(client EC2VPCAPI, vpcID string) error { var allEgressGateways []*string logging.Debug(fmt.Sprintf("Start nuking Egress Only Internet Gateways for vpc: %s", vpcID)) - err := client.DescribeEgressOnlyInternetGatewaysPages( - &ec2.DescribeEgressOnlyInternetGatewaysInput{}, - func(page *ec2.DescribeEgressOnlyInternetGatewaysOutput, lastPage bool) bool { - for _, gateway := range page.EgressOnlyInternetGateways { - for _, attachment := range gateway.Attachments { - if *attachment.VpcId == vpcID { - allEgressGateways = append(allEgressGateways, gateway.EgressOnlyInternetGatewayId) - break - } + paginator := ec2.NewDescribeEgressOnlyInternetGatewaysPaginator(client, &ec2.DescribeEgressOnlyInternetGatewaysInput{}) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(context.Background()) + if err != nil { + logging.Debug(fmt.Sprintf("Failed to describe Egress Only Internet Gateways for vpc: %s", vpcID)) + return err + } + for _, gateway := range page.EgressOnlyInternetGateways { + for _, attachment := range gateway.Attachments { + if *attachment.VpcId == vpcID { + allEgressGateways = append(allEgressGateways, gateway.EgressOnlyInternetGatewayId) + break } } - return !lastPage - }, - ) - if err != nil { - logging.Debug(fmt.Sprintf("Failed to describe Egress Only Internet Gateways for vpc: %s", vpcID)) - return err + } } logging.Debug(fmt.Sprintf("Found %d Egress Only Internet Gateways to nuke.", len(allEgressGateways))) @@ -395,14 +390,14 @@ func nukeEgressOnlyGateways(client ec2iface.EC2API, vpcID string) error { return nil } -func nukeEndpoints(client ec2iface.EC2API, vpcID string) error { +func nukeEndpoints(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Start nuking VPC endpoints for vpc: %s", vpcID)) - endpoints, _ := client.DescribeVpcEndpoints( + endpoints, _ := client.DescribeVpcEndpoints(context.Background(), &ec2.DescribeVpcEndpointsInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("vpc-id"), - Values: []*string{awsgo.String(vpcID)}, + Values: []string{vpcID}, }, }, }, @@ -412,9 +407,9 @@ func nukeEndpoints(client ec2iface.EC2API, vpcID string) error { var endpointIds []*string for _, endpoint := range endpoints.VpcEndpoints { // Note: sometime the state is all lower cased, sometime it is not. - if strings.ToLower(*endpoint.State) != strings.ToLower(ec2.StateAvailable) { + if strings.ToLower(string(endpoint.State)) != strings.ToLower(string(types.StateAvailable)) { logging.Debug(fmt.Sprintf("Skipping VPC endpoint %s as it is not in available state: %s", - awsgo.StringValue(endpoint.VpcEndpointId), awsgo.StringValue(endpoint.State))) + awsgo.ToString(endpoint.VpcEndpointId), endpoint.State)) continue } @@ -425,14 +420,9 @@ func nukeEndpoints(client ec2iface.EC2API, vpcID string) error { logging.Debug(fmt.Sprintf("No VPC endpoint to nuke.")) return nil } - err := nukeVpcEndpoint(client, endpointIds) if err != nil { - logging.Debug(fmt.Sprintf("Failed to delete VPC endpoints: %s", err.Error())) - return errors.WithStackTrace(err) - } - - if err := waitForVPCEndpointToBeDeleted(client, vpcID); err != nil { + logging.Debug(fmt.Sprintf("Failed to delete VPC endpoints: %v", err)) return errors.WithStackTrace(err) } @@ -440,39 +430,38 @@ func nukeEndpoints(client ec2iface.EC2API, vpcID string) error { return nil } -func nukeNetworkInterfaces(client ec2iface.EC2API, vpcID string) error { +func nukeNetworkInterfaces(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Start nuking network interfaces for vpc: %s", vpcID)) - var allNetworkInterfaces []*ec2.NetworkInterface + var allNetworkInterfaces []types.NetworkInterface vpcIds := []string{vpcID} - filters := []*ec2.Filter{{Name: awsgo.String("vpc-id"), Values: awsgo.StringSlice(vpcIds)}} - err := client.DescribeNetworkInterfacesPages( - &ec2.DescribeNetworkInterfacesInput{ - Filters: filters, - }, - func(page *ec2.DescribeNetworkInterfacesOutput, lastPage bool) bool { - for _, netInterface := range page.NetworkInterfaces { - allNetworkInterfaces = append(allNetworkInterfaces, netInterface) - } - return !lastPage - }, - ) - if err != nil { - logging.Debug(fmt.Sprintf("Failed to describe network interfaces for vpc: %s", vpcID)) - return err + filters := []types.Filter{{Name: awsgo.String("vpc-id"), Values: vpcIds}} + paginator := ec2.NewDescribeNetworkInterfacesPaginator(client, &ec2.DescribeNetworkInterfacesInput{ + Filters: filters, + }) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(context.Background()) + if err != nil { + logging.Debug(fmt.Sprintf("Failed to describe network interfaces for vpc: %s", vpcID)) + return err + } + for _, netInterface := range page.NetworkInterfaces { + allNetworkInterfaces = append(allNetworkInterfaces, netInterface) + } } logging.Debug(fmt.Sprintf("Found %d Elastic Network Interfaces to Nuke.", len(allNetworkInterfaces))) for _, netInterface := range allNetworkInterfaces { - if strings.ToLower(*netInterface.Status) == strings.ToLower(ec2.NetworkInterfaceStatusInUse) { + if strings.ToLower(string(netInterface.Status)) == strings.ToLower(string(types.NetworkInterfaceStatusInUse)) { logging.Debug(fmt.Sprintf("Skipping network interface %s as it is in use", *netInterface.NetworkInterfaceId)) continue } - err = nukeNetworkInterface(client, netInterface.NetworkInterfaceId) + err := nukeNetworkInterface(client, netInterface.NetworkInterfaceId) if err != nil { - logging.Debug(fmt.Sprintf("Failed to delete network interface: %s", *netInterface)) + logging.Debug(fmt.Sprintf("Failed to delete network interface: %v", netInterface)) return errors.WithStackTrace(err) } @@ -482,14 +471,14 @@ func nukeNetworkInterfaces(client ec2iface.EC2API, vpcID string) error { return nil } -func nukeSubnets(client ec2iface.EC2API, vpcID string) error { +func nukeSubnets(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Start nuking subnets for vpc: %s", vpcID)) - subnets, _ := client.DescribeSubnets( + subnets, _ := client.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("vpc-id"), - Values: []*string{awsgo.String(vpcID)}, + Values: []string{vpcID}, }, }, }, @@ -504,7 +493,7 @@ func nukeSubnets(client ec2iface.EC2API, vpcID string) error { for _, subnet := range subnets.Subnets { err := nukeSubnet(client, subnet.SubnetId) if err != nil { - logging.Debug(fmt.Sprintf("Failed to delete subnet %s for vpc %s", awsgo.StringValue(subnet.SubnetId), vpcID)) + logging.Debug(fmt.Sprintf("Failed to delete subnet %s for vpc %s", awsgo.ToString(subnet.SubnetId), vpcID)) return errors.WithStackTrace(err) } } @@ -513,12 +502,12 @@ func nukeSubnets(client ec2iface.EC2API, vpcID string) error { } -func nukeNatGateways(client ec2iface.EC2API, vpcID string) error { - gateways, err := client.DescribeNatGateways(&ec2.DescribeNatGatewaysInput{ - Filter: []*ec2.Filter{ +func nukeNatGateways(client EC2VPCAPI, vpcID string) error { + gateways, err := client.DescribeNatGateways(context.Background(), &ec2.DescribeNatGatewaysInput{ + Filter: []types.Filter{ { Name: awsgo.String("vpc-id"), - Values: []*string{awsgo.String(vpcID)}, + Values: []string{vpcID}, }, }, }) @@ -529,16 +518,16 @@ func nukeNatGateways(client ec2iface.EC2API, vpcID string) error { logging.Debug(fmt.Sprintf("Found %d NAT gateways to delete", len(gateways.NatGateways))) for _, gateway := range gateways.NatGateways { - if *gateway.State != ec2.NatGatewayStateAvailable { + if gateway.State != types.NatGatewayStateAvailable { logging.Debug(fmt.Sprintf("Skipping NAT gateway %s as it is not in available state: %s", - awsgo.StringValue(gateway.NatGatewayId), awsgo.StringValue(gateway.State))) + awsgo.ToString(gateway.NatGatewayId), gateway.State)) continue } err := nukeNATGateway(client, gateway.NatGatewayId) if err != nil { logging.Debug( - fmt.Sprintf("Failed to delete NAT gateway %s for vpc %v", awsgo.StringValue(gateway.NatGatewayId), vpcID)) + fmt.Sprintf("Failed to delete NAT gateway %s for vpc %v", awsgo.ToString(gateway.NatGatewayId), vpcID)) return errors.WithStackTrace(err) } } @@ -547,14 +536,14 @@ func nukeNatGateways(client ec2iface.EC2API, vpcID string) error { return nil } -func nukeRouteTables(client ec2iface.EC2API, vpcID string) error { +func nukeRouteTables(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Start nuking route tables for vpc: %s", vpcID)) - routeTables, err := client.DescribeRouteTables( + routeTables, err := client.DescribeRouteTables(context.Background(), &ec2.DescribeRouteTablesInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("vpc-id"), - Values: []*string{awsgo.String(vpcID)}, + Values: []string{vpcID}, }, }, }, @@ -569,34 +558,34 @@ func nukeRouteTables(client ec2iface.EC2API, vpcID string) error { // Skip main route table if len(routeTable.Associations) > 0 && *routeTable.Associations[0].Main { logging.Debug(fmt.Sprintf("Skipping main route table: %s", - awsgo.StringValue(routeTable.RouteTableId))) + awsgo.ToString(routeTable.RouteTableId))) continue } - logging.Debug(fmt.Sprintf("Start nuking route table: %s", awsgo.StringValue(routeTable.RouteTableId))) + logging.Debug(fmt.Sprintf("Start nuking route table: %s", awsgo.ToString(routeTable.RouteTableId))) for _, association := range routeTable.Associations { - _, err := client.DisassociateRouteTable(&ec2.DisassociateRouteTableInput{ + _, err := client.DisassociateRouteTable(context.Background(), &ec2.DisassociateRouteTableInput{ AssociationId: association.RouteTableAssociationId, }) if err != nil { logging.Debug(fmt.Sprintf("Failed to disassociate route table: %s", - awsgo.StringValue(association.RouteTableAssociationId))) + awsgo.ToString(association.RouteTableAssociationId))) return errors.WithStackTrace(err) } logging.Debug(fmt.Sprintf("Successfully disassociated route table: %s", - awsgo.StringValue(association.RouteTableAssociationId))) + awsgo.ToString(association.RouteTableAssociationId))) } - _, err := client.DeleteRouteTable( + _, err := client.DeleteRouteTable(context.Background(), &ec2.DeleteRouteTableInput{ RouteTableId: routeTable.RouteTableId, }, ) if err != nil { - logging.Debug(fmt.Sprintf("Failed to delete route table: %s", awsgo.StringValue(routeTable.RouteTableId))) + logging.Debug(fmt.Sprintf("Failed to delete route table: %s", awsgo.ToString(routeTable.RouteTableId))) return errors.WithStackTrace(err) } - logging.Debug(fmt.Sprintf("Successfully deleted route table: %s", awsgo.StringValue(routeTable.RouteTableId))) + logging.Debug(fmt.Sprintf("Successfully deleted route table: %s", awsgo.ToString(routeTable.RouteTableId))) } return nil @@ -608,14 +597,14 @@ func nukeRouteTables(client ec2iface.EC2API, vpcID string) error { // You can't delete the default network ACL. // // https://docs.aws.amazon.com/cli/latest/reference/ec2/delete-network-acl.html -func nukeNacls(client ec2iface.EC2API, vpcID string) error { +func nukeNacls(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Start nuking network ACLs for vpc: %s", vpcID)) - networkACLs, _ := client.DescribeNetworkAcls( + networkACLs, _ := client.DescribeNetworkAcls(context.Background(), &ec2.DescribeNetworkAclsInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("vpc-id"), - Values: []*string{awsgo.String(vpcID)}, + Values: []string{vpcID}, }, }, }, @@ -642,32 +631,36 @@ func nukeNacls(client ec2iface.EC2API, vpcID string) error { continue } - logging.Debugf("Start nuking network ACL: %s", awsgo.StringValue(networkACL.NetworkAclId)) - err := replaceNetworkAclAssociation(client, defaultNetworkAclID, networkACL.Associations) + logging.Debugf("Start nuking network ACL: %s", awsgo.ToString(networkACL.NetworkAclId)) + associations := make([]*types.NetworkAclAssociation, len(networkACL.Associations)) + for i := range networkACL.Associations { + associations[i] = &networkACL.Associations[i] + } + err := replaceNetworkAclAssociation(client, defaultNetworkAclID, associations) if err != nil { - logging.Debugf("Failed to replace network ACL associations: %s", awsgo.StringValue(networkACL.NetworkAclId)) + logging.Debugf("Failed to replace network ACL associations: %s", awsgo.ToString(networkACL.NetworkAclId)) return errors.WithStackTrace(err) } err = nukeNetworkAcl(client, networkACL.NetworkAclId) if err != nil { - logging.Debugf("Failed to delete network ACL: %s", awsgo.StringValue(networkACL.NetworkAclId)) + logging.Debugf("Failed to delete network ACL: %s", awsgo.ToString(networkACL.NetworkAclId)) return errors.WithStackTrace(err) } - logging.Debugf("Successfully deleted network ACL: %s", awsgo.StringValue(networkACL.NetworkAclId)) + logging.Debugf("Successfully deleted network ACL: %s", awsgo.ToString(networkACL.NetworkAclId)) } return nil } -func nukeSecurityGroups(client ec2iface.EC2API, vpcID string) error { +func nukeSecurityGroups(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Start nuking security groups for vpc: %s", vpcID)) - securityGroups, _ := client.DescribeSecurityGroups( + securityGroups, _ := client.DescribeSecurityGroups(context.Background(), &ec2.DescribeSecurityGroupsInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("vpc-id"), - Values: []*string{awsgo.String(vpcID)}, + Values: []string{vpcID}, }, }, }, @@ -675,65 +668,65 @@ func nukeSecurityGroups(client ec2iface.EC2API, vpcID string) error { logging.Debug(fmt.Sprintf("Found %d security groups to delete", len(securityGroups.SecurityGroups))) for _, securityGroup := range securityGroups.SecurityGroups { - securityGroupRules, _ := client.DescribeSecurityGroupRules( + securityGroupRules, _ := client.DescribeSecurityGroupRules(context.Background(), &ec2.DescribeSecurityGroupRulesInput{ - Filters: []*ec2.Filter{ + Filters: []types.Filter{ { Name: awsgo.String("group-id"), - Values: []*string{securityGroup.GroupId}, + Values: []string{*securityGroup.GroupId}, }, }, }, ) logging.Debug(fmt.Sprintf("Found %d security rules to delete for security group: %s", - len(securityGroupRules.SecurityGroupRules), awsgo.StringValue(securityGroup.GroupId))) + len(securityGroupRules.SecurityGroupRules), awsgo.ToString(securityGroup.GroupId))) for _, securityGroupRule := range securityGroupRules.SecurityGroupRules { - logging.Debug(fmt.Sprintf("Deleting Security Group Rule %s", awsgo.StringValue(securityGroupRule.SecurityGroupRuleId))) + logging.Debug(fmt.Sprintf("Deleting Security Group Rule %s", awsgo.ToString(securityGroupRule.SecurityGroupRuleId))) if *securityGroupRule.IsEgress { - _, err := client.RevokeSecurityGroupEgress( + _, err := client.RevokeSecurityGroupEgress(context.Background(), &ec2.RevokeSecurityGroupEgressInput{ GroupId: securityGroup.GroupId, - SecurityGroupRuleIds: []*string{securityGroupRule.SecurityGroupRuleId}, + SecurityGroupRuleIds: []string{*securityGroupRule.SecurityGroupRuleId}, }, ) if err != nil { logging.Debug(fmt.Sprintf("Failed to revoke security group egress rule: %s", - awsgo.StringValue(securityGroupRule.SecurityGroupRuleId))) + awsgo.ToString(securityGroupRule.SecurityGroupRuleId))) return errors.WithStackTrace(err) } logging.Debug(fmt.Sprintf("Successfully revoked security group egress rule: %s", - awsgo.StringValue(securityGroupRule.SecurityGroupRuleId))) + awsgo.ToString(securityGroupRule.SecurityGroupRuleId))) } else { - _, err := client.RevokeSecurityGroupIngress( + _, err := client.RevokeSecurityGroupIngress(context.Background(), &ec2.RevokeSecurityGroupIngressInput{ GroupId: securityGroup.GroupId, - SecurityGroupRuleIds: []*string{securityGroupRule.SecurityGroupRuleId}, + SecurityGroupRuleIds: []string{*securityGroupRule.SecurityGroupRuleId}, }, ) if err != nil { logging.Debug(fmt.Sprintf("Failed to revoke security group ingress rule: %s", - awsgo.StringValue(securityGroupRule.SecurityGroupRuleId))) + awsgo.ToString(securityGroupRule.SecurityGroupRuleId))) return errors.WithStackTrace(err) } logging.Debug(fmt.Sprintf("Successfully revoked security group ingress rule: %s", - awsgo.StringValue(securityGroupRule.SecurityGroupRuleId))) + awsgo.ToString(securityGroupRule.SecurityGroupRuleId))) } } } for _, securityGroup := range securityGroups.SecurityGroups { if *securityGroup.GroupName != "default" { - logging.Debug(fmt.Sprintf("Deleting Security Group %s for vpc %s", awsgo.StringValue(securityGroup.GroupId), vpcID)) + logging.Debug(fmt.Sprintf("Deleting Security Group %s for vpc %s", awsgo.ToString(securityGroup.GroupId), vpcID)) err := nukeSecurityGroup(client, securityGroup.GroupId) if err != nil { logging.Debug(fmt.Sprintf( - "Successfully deleted security group %s", awsgo.StringValue(securityGroup.GroupId))) + "Successfully deleted security group %s", awsgo.ToString(securityGroup.GroupId))) return errors.WithStackTrace(err) } logging.Debug(fmt.Sprintf( - "Successfully deleted security group %s", awsgo.StringValue(securityGroup.GroupId))) + "Successfully deleted security group %s", awsgo.ToString(securityGroup.GroupId))) } } @@ -742,14 +735,14 @@ func nukeSecurityGroups(client ec2iface.EC2API, vpcID string) error { // default option is not available for this, and it only supports deleting non-default resources // https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-dhcp-options.html -func nukeDhcpOptions(client ec2iface.EC2API, vpcID string) error { +func nukeDhcpOptions(client EC2VPCAPI, vpcID string) error { // Deletes the specified set of DHCP options. You must disassociate the set of DHCP options before you can delete it. // You can disassociate the set of DHCP options by associating either a new set of options or the default set of // options with the VPC. - vpcs, err := client.DescribeVpcs( + vpcs, err := client.DescribeVpcs(context.Background(), &ec2.DescribeVpcsInput{ - VpcIds: []*string{awsgo.String(vpcID)}, + VpcIds: []string{vpcID}, }, ) if err != nil { @@ -766,7 +759,7 @@ func nukeDhcpOptions(client ec2iface.EC2API, vpcID string) error { } // Disassociates a set of DHCP options from a VPC by setting the options to default. - _, err = client.AssociateDhcpOptions(&ec2.AssociateDhcpOptionsInput{ + _, err = client.AssociateDhcpOptions(context.Background(), &ec2.AssociateDhcpOptionsInput{ DhcpOptionsId: awsgo.String("default"), VpcId: awsgo.String(vpcID), }) @@ -778,12 +771,12 @@ func nukeDhcpOptions(client ec2iface.EC2API, vpcID string) error { return err } -func nukeVpc(client ec2iface.EC2API, vpcID string) error { +func nukeVpc(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Deleting VPC %s", vpcID)) input := &ec2.DeleteVpcInput{ VpcId: awsgo.String(vpcID), } - _, err := client.DeleteVpc(input) + _, err := client.DeleteVpc(context.Background(), input) if err != nil { logging.Debug(fmt.Sprintf("Failed to delete VPC %s", vpcID)) return errors.WithStackTrace(err) @@ -793,11 +786,11 @@ func nukeVpc(client ec2iface.EC2API, vpcID string) error { return nil } -func nukeAttachedLB(client ec2iface.EC2API, elbclient elbv2iface.ELBV2API, vpcID string) error { +func nukeAttachedLB(client EC2VPCAPI, elbclient ELBClientAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Describing load balancers for %s", vpcID)) // get all loadbalancers - output, err := elbclient.DescribeLoadBalancers(nil) + output, err := elbclient.DescribeLoadBalancers(context.Background(), &elasticloadbalancingv2.DescribeLoadBalancersInput{}) if err != nil { logging.Debug(fmt.Sprintf("Failed to describe loadbalancer for %s", vpcID)) return errors.WithStackTrace(err) @@ -806,15 +799,15 @@ func nukeAttachedLB(client ec2iface.EC2API, elbclient elbv2iface.ELBV2API, vpcID // get a list of load balancers which was attached on the vpc for _, lb := range output.LoadBalancers { - if awsgo.StringValue(lb.VpcId) != vpcID { + if awsgo.ToString(lb.VpcId) != vpcID { continue } - attachedLoadBalancers = append(attachedLoadBalancers, awsgo.StringValue(lb.LoadBalancerArn)) + attachedLoadBalancers = append(attachedLoadBalancers, awsgo.ToString(lb.LoadBalancerArn)) } // check the load-balancers are attached with any vpc-endpoint-service, then nuke them first - esoutput, err := client.DescribeVpcEndpointServiceConfigurations(nil) + esoutput, err := client.DescribeVpcEndpointServiceConfigurations(context.Background(), &ec2.DescribeVpcEndpointServiceConfigurationsInput{}) if err != nil { logging.Debug(fmt.Sprintf("Failed to describe vpc endpoint services for %s", vpcID)) return errors.WithStackTrace(err) @@ -825,14 +818,14 @@ func nukeAttachedLB(client ec2iface.EC2API, elbclient elbv2iface.ELBV2API, vpcID for _, config := range esoutput.ServiceConfigurations { // check through gateway load balancer attachments and select the service for nuking for _, gwlb := range config.GatewayLoadBalancerArns { - if slices.Contains(attachedLoadBalancers, awsgo.StringValue(gwlb)) { + if slices.Contains(attachedLoadBalancers, gwlb) { nukableEndpointServices[config.ServiceId] = struct{}{} } } // check through network load balancer attachments and select the service for nuking for _, nwlb := range config.NetworkLoadBalancerArns { - if slices.Contains(attachedLoadBalancers, awsgo.StringValue(nwlb)) { + if slices.Contains(attachedLoadBalancers, nwlb) { nukableEndpointServices[config.ServiceId] = struct{}{} } } @@ -842,11 +835,11 @@ func nukeAttachedLB(client ec2iface.EC2API, elbclient elbv2iface.ELBV2API, vpcID // nuke the endpoint services for endpointService := range nukableEndpointServices { - _, err := client.DeleteVpcEndpointServiceConfigurations(&ec2.DeleteVpcEndpointServiceConfigurationsInput{ - ServiceIds: []*string{endpointService}, + _, err := client.DeleteVpcEndpointServiceConfigurations(context.Background(), &ec2.DeleteVpcEndpointServiceConfigurationsInput{ + ServiceIds: []string{*endpointService}, }) if err != nil { - logging.Debug(fmt.Sprintf("Failed to delete endpoint service %v for %s", awsgo.StringValue(endpointService), vpcID)) + logging.Debug(fmt.Sprintf("Failed to delete endpoint service %v for %s", awsgo.ToString(endpointService), vpcID)) return errors.WithStackTrace(err) } } @@ -854,7 +847,7 @@ func nukeAttachedLB(client ec2iface.EC2API, elbclient elbv2iface.ELBV2API, vpcID // nuke the load-balancers for _, lb := range attachedLoadBalancers { - _, err := elbclient.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{ + _, err := elbclient.DeleteLoadBalancer(context.Background(), &elasticloadbalancingv2.DeleteLoadBalancerInput{ LoadBalancerArn: awsgo.String(lb), }) if err != nil { @@ -867,10 +860,10 @@ func nukeAttachedLB(client ec2iface.EC2API, elbclient elbv2iface.ELBV2API, vpcID return nil } -func nukeTargetGroups(client elbv2iface.ELBV2API, vpcID string) error { +func nukeTargetGroups(client ELBClientAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Describing target groups for %s", vpcID)) - output, err := client.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{}) + output, err := client.DescribeTargetGroups(context.Background(), &elasticloadbalancingv2.DescribeTargetGroupsInput{}) if err != nil { logging.Debug(fmt.Sprintf("Failed to describe target groups for %s", vpcID)) return errors.WithStackTrace(err) @@ -878,8 +871,8 @@ func nukeTargetGroups(client elbv2iface.ELBV2API, vpcID string) error { for _, tg := range output.TargetGroups { // if the target group is not for this vpc, then skip - if tg.VpcId != nil && awsgo.StringValue(tg.VpcId) == vpcID { - _, err := client.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ + if tg.VpcId != nil && awsgo.ToString(tg.VpcId) == vpcID { + _, err := client.DeleteTargetGroup(context.Background(), &elasticloadbalancingv2.DeleteTargetGroupInput{ TargetGroupArn: tg.TargetGroupArn, }) if err != nil { @@ -895,15 +888,13 @@ func nukeTargetGroups(client elbv2iface.ELBV2API, vpcID string) error { return nil } -func nukeEc2Instances(client ec2iface.EC2API, vpcID string) error { +func nukeEc2Instances(client EC2VPCAPI, vpcID string) error { logging.Debug(fmt.Sprintf("Describing instances for %s", vpcID)) - output, err := client.DescribeInstances(&ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ + output, err := client.DescribeInstances(context.Background(), &ec2.DescribeInstancesInput{ + Filters: []types.Filter{ { - Name: awsgo.String("network-interface.vpc-id"), - Values: awsgo.StringSlice([]string{ - vpcID, - }), + Name: awsgo.String("network-interface.vpc-id"), + Values: []string{vpcID}, }, }, }) @@ -923,8 +914,13 @@ func nukeEc2Instances(client ec2iface.EC2API, vpcID string) error { if len(terminateInstances) > 0 { logging.Debug(fmt.Sprintf("Found %d VPC attached instances to Nuke.", len(terminateInstances))) - _, err := client.TerminateInstances(&ec2.TerminateInstancesInput{ - InstanceIds: terminateInstances, + // Convert []*string to []string + terminateInstancesIds := make([]string, len(terminateInstances)) + for i, id := range terminateInstances { + terminateInstancesIds[i] = aws.ToString(id) + } + _, err := client.TerminateInstances(context.Background(), &ec2.TerminateInstancesInput{ + InstanceIds: terminateInstancesIds, }) if err != nil { logging.Debug(fmt.Sprintf("Failed to terminate instances for %s", vpcID)) @@ -933,9 +929,10 @@ func nukeEc2Instances(client ec2iface.EC2API, vpcID string) error { // weight for terminate the instances logging.Debug(fmt.Sprintf("waiting for the instance to be terminated for %s", vpcID)) - err = client.WaitUntilInstanceTerminated(&ec2.DescribeInstancesInput{ - InstanceIds: terminateInstances, - }) + waiter := ec2.NewInstanceTerminatedWaiter(client) + err = waiter.Wait(context.Background(), &ec2.DescribeInstancesInput{ + InstanceIds: terminateInstancesIds, + }, 5*time.Minute) if err != nil { logging.Debug(fmt.Sprintf("Failed to wait instance termination for %s", vpcID)) return errors.WithStackTrace(err) @@ -945,53 +942,3 @@ func nukeEc2Instances(client ec2iface.EC2API, vpcID string) error { logging.Debug(fmt.Sprintf("Successfully deleted instances for %s", vpcID)) 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") - }, - ) -} - -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 -} diff --git a/aws/resources/ec2_vpc_test.go b/aws/resources/ec2_vpc_test.go index 3d5933a3..b5865677 100644 --- a/aws/resources/ec2_vpc_test.go +++ b/aws/resources/ec2_vpc_test.go @@ -6,20 +6,19 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - awsgo "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" - "github.com/aws/aws-sdk-go/service/elbv2" - "github.com/aws/aws-sdk-go/service/elbv2/elbv2iface" + "github.com/aws/aws-sdk-go-v2/aws" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" + elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" + elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/util" "github.com/stretchr/testify/require" ) type mockedEC2VPCs struct { - ec2iface.EC2API + EC2VPCAPI DescribeVpcsOutput ec2.DescribeVpcsOutput DeleteVpcOutput ec2.DeleteVpcOutput DescribeVpcPeeringConnectionsOutput ec2.DescribeVpcPeeringConnectionsOutput @@ -27,34 +26,34 @@ type mockedEC2VPCs struct { TerminateInstancesOutput ec2.TerminateInstancesOutput DescribeVpcEndpointServiceConfigurationsOutput ec2.DescribeVpcEndpointServiceConfigurationsOutput DeleteVpcEndpointServiceConfigurationsOutput ec2.DeleteVpcEndpointServiceConfigurationsOutput + DescribeInstancesError error } -func (m mockedEC2VPCs) DescribeVpcsWithContext(_ awsgo.Context, _ *ec2.DescribeVpcsInput, _ ...request.Option) (*ec2.DescribeVpcsOutput, error) { +func (m mockedEC2VPCs) DescribeVpcs(ctx context.Context, input *ec2.DescribeVpcsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error) { return &m.DescribeVpcsOutput, nil } -func (m mockedEC2VPCs) DeleteVpc(_ *ec2.DeleteVpcInput) (*ec2.DeleteVpcOutput, error) { +func (m mockedEC2VPCs) DeleteVpc(ctx context.Context, input *ec2.DeleteVpcInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVpcOutput, error) { return &m.DeleteVpcOutput, nil } -func (m mockedEC2VPCs) DescribeVpcPeeringConnectionsPages(_ *ec2.DescribeVpcPeeringConnectionsInput, callback func(*ec2.DescribeVpcPeeringConnectionsOutput, bool) bool) error { - callback(&m.DescribeVpcPeeringConnectionsOutput, true) - return nil + +func (m mockedEC2VPCs) DescribeVpcPeeringConnections(ctx context.Context, input *ec2.DescribeVpcPeeringConnectionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcPeeringConnectionsOutput, error) { + return &m.DescribeVpcPeeringConnectionsOutput, nil } -func (m mockedEC2VPCs) DescribeInstances(_ *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { - return &m.DescribeInstancesOutput, nil + +func (m mockedEC2VPCs) DescribeInstances(ctx context.Context, input *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { + return &m.DescribeInstancesOutput, m.DescribeInstancesError } -func (m mockedEC2VPCs) TerminateInstances(_ *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) { +func (m mockedEC2VPCs) TerminateInstances(ctx context.Context, input *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) { return &m.TerminateInstancesOutput, nil } -func (m mockedEC2VPCs) WaitUntilInstanceTerminated(_ *ec2.DescribeInstancesInput) error { - return nil -} -func (m mockedEC2VPCs) DescribeVpcEndpointServiceConfigurations(_ *ec2.DescribeVpcEndpointServiceConfigurationsInput) (*ec2.DescribeVpcEndpointServiceConfigurationsOutput, error) { +func (m mockedEC2VPCs) DescribeVpcEndpointServiceConfigurations(ctx context.Context, input *ec2.DescribeVpcEndpointServiceConfigurationsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcEndpointServiceConfigurationsOutput, error) { return &m.DescribeVpcEndpointServiceConfigurationsOutput, nil } -func (m mockedEC2VPCs) DeleteVpcEndpointServiceConfigurations(_ *ec2.DeleteVpcEndpointServiceConfigurationsInput) (*ec2.DeleteVpcEndpointServiceConfigurationsOutput, error) { + +func (m mockedEC2VPCs) DeleteVpcEndpointServiceConfigurations(ctx context.Context, input *ec2.DeleteVpcEndpointServiceConfigurationsInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVpcEndpointServiceConfigurationsOutput, error) { return &m.DeleteVpcEndpointServiceConfigurationsOutput, nil } func TestEC2VPC_GetAll(t *testing.T) { @@ -72,10 +71,10 @@ func TestEC2VPC_GetAll(t *testing.T) { vpc := EC2VPCs{ Client: mockedEC2VPCs{ DescribeVpcsOutput: ec2.DescribeVpcsOutput{ - Vpcs: []*ec2.Vpc{ + Vpcs: []types.Vpc{ { VpcId: awsgo.String(testId1), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName1), @@ -88,7 +87,7 @@ func TestEC2VPC_GetAll(t *testing.T) { }, { VpcId: awsgo.String(testId2), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: awsgo.String("Name"), Value: awsgo.String(testName2), @@ -144,7 +143,7 @@ func TestEC2VPC_GetAll(t *testing.T) { VPC: tc.configObj, }) require.NoError(t, err) - require.Equal(t, tc.expected, awsgo.StringValueSlice(names)) + require.Equal(t, tc.expected, awsgo.ToStringSlice(names)) }) } } @@ -177,7 +176,7 @@ func TestEC2VPCPeeringConnections_NukeAll(t *testing.T) { } type mockedEC2ELB struct { - elbv2iface.ELBV2API + ELBClientAPI DescribeLoadBalancersOutput elbv2.DescribeLoadBalancersOutput DeleteLoadBalancerOutput elbv2.DeleteLoadBalancerOutput @@ -185,17 +184,19 @@ type mockedEC2ELB struct { DeleteTargetGroupOutput elbv2.DeleteTargetGroupOutput } -func (m mockedEC2ELB) DescribeLoadBalancers(*elbv2.DescribeLoadBalancersInput) (*elbv2.DescribeLoadBalancersOutput, error) { +func (m mockedEC2ELB) DescribeLoadBalancers(ctx context.Context, input *elbv2.DescribeLoadBalancersInput, optFns ...func(*elbv2.Options)) (*elbv2.DescribeLoadBalancersOutput, error) { return &m.DescribeLoadBalancersOutput, nil } -func (m mockedEC2ELB) DescribeTargetGroups(*elbv2.DescribeTargetGroupsInput) (*elbv2.DescribeTargetGroupsOutput, error) { + +func (m mockedEC2ELB) DescribeTargetGroups(ctx context.Context, input *elbv2.DescribeTargetGroupsInput, optFns ...func(*elbv2.Options)) (*elbv2.DescribeTargetGroupsOutput, error) { return &m.DescribeTargetGroupsOutput, nil } -func (m mockedEC2ELB) DeleteLoadBalancer(*elbv2.DeleteLoadBalancerInput) (*elbv2.DeleteLoadBalancerOutput, error) { +func (m mockedEC2ELB) DeleteLoadBalancer(ctx context.Context, input *elbv2.DeleteLoadBalancerInput, optFns ...func(*elbv2.Options)) (*elbv2.DeleteLoadBalancerOutput, error) { return &m.DeleteLoadBalancerOutput, nil } -func (m mockedEC2ELB) DeleteTargetGroup(*elbv2.DeleteTargetGroupInput) (*elbv2.DeleteTargetGroupOutput, error) { + +func (m mockedEC2ELB) DeleteTargetGroup(ctx context.Context, input *elbv2.DeleteTargetGroupInput, optFns ...func(*elbv2.Options)) (*elbv2.DeleteTargetGroupOutput, error) { return &m.DeleteTargetGroupOutput, nil } @@ -206,18 +207,17 @@ func TestAttachedLB_Nuke(t *testing.T) { vpc := EC2VPCs{ Client: mockedEC2VPCs{ DescribeVpcEndpointServiceConfigurationsOutput: ec2.DescribeVpcEndpointServiceConfigurationsOutput{ - ServiceConfigurations: []*ec2.ServiceConfiguration{ + ServiceConfigurations: []types.ServiceConfiguration{ { - GatewayLoadBalancerArns: awsgo.StringSlice([]string{ - "load-balancer-arn-00012", - }), + ServiceId: aws.String("load-balancer-arn-service-id-00012"), + GatewayLoadBalancerArns: []string{"load-balancer-arn-00012"}, }, }, }, }, ELBClient: mockedEC2ELB{ DescribeLoadBalancersOutput: elbv2.DescribeLoadBalancersOutput{ - LoadBalancers: []*elbv2.LoadBalancer{ + LoadBalancers: []elbtypes.LoadBalancer{ { VpcId: awsgo.String(vpcID), LoadBalancerArn: awsgo.String("load-balancer-arn-00012"), @@ -227,6 +227,7 @@ func TestAttachedLB_Nuke(t *testing.T) { }, } + vpc.Context = context.Background() err := nukeAttachedLB(vpc.Client, vpc.ELBClient, vpcID) require.NoError(t, err) } @@ -238,7 +239,7 @@ func TestTargetGroup_Nuke(t *testing.T) { vpc := EC2VPCs{ ELBClient: mockedEC2ELB{ DescribeTargetGroupsOutput: elbv2.DescribeTargetGroupsOutput{ - TargetGroups: []*elbv2.TargetGroup{ + TargetGroups: []elbtypes.TargetGroup{ { VpcId: awsgo.String(vpcID), TargetGroupArn: awsgo.String("arn:aws:elasticloadbalancing:us-east-1:tg-001"), @@ -251,27 +252,3 @@ func TestTargetGroup_Nuke(t *testing.T) { err := nukeTargetGroups(vpc.ELBClient, vpcID) require.NoError(t, err) } - -func TestEc2Instance_Nuke(t *testing.T) { - t.Parallel() - vpcID := "vpc-0e9a3e7c72d9f3a0f" - - vpc := EC2VPCs{ - Client: mockedEC2VPCs{ - DescribeInstancesOutput: ec2.DescribeInstancesOutput{ - Reservations: []*ec2.Reservation{ - { - Instances: []*ec2.Instance{ - { - InstanceId: awsgo.String("instance-001"), - }, - }, - }, - }, - }, - }, - } - - err := nukeEc2Instances(vpc.Client, vpcID) - require.NoError(t, err) -} diff --git a/aws/resources/ec2_vpc_types.go b/aws/resources/ec2_vpc_types.go index f55576d6..a7963771 100644 --- a/aws/resources/ec2_vpc_types.go +++ b/aws/resources/ec2_vpc_types.go @@ -3,29 +3,60 @@ package resources import ( "context" - 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/aws/aws-sdk-go/service/ec2/ec2iface" - "github.com/aws/aws-sdk-go/service/elbv2" - "github.com/aws/aws-sdk-go/service/elbv2/elbv2iface" + "github.com/aws/aws-sdk-go-v2/aws" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/go-commons/errors" ) +type EC2VPCAPI interface { + DescribeVpcs(ctx context.Context, params *ec2.DescribeVpcsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error) + DeleteVpc(ctx context.Context, params *ec2.DeleteVpcInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVpcOutput, error) + DescribeVpcEndpointServiceConfigurations(ctx context.Context, params *ec2.DescribeVpcEndpointServiceConfigurationsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcEndpointServiceConfigurationsOutput, error) + DeleteVpcEndpointServiceConfigurations(ctx context.Context, params *ec2.DeleteVpcEndpointServiceConfigurationsInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVpcEndpointServiceConfigurationsOutput, error) + DeleteVpcPeeringConnection(ctx context.Context, params *ec2.DeleteVpcPeeringConnectionInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVpcPeeringConnectionOutput, error) + DescribeVpcEndpoints(ctx context.Context, params *ec2.DescribeVpcEndpointsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcEndpointsOutput, error) + DescribeRouteTables(ctx context.Context, params *ec2.DescribeRouteTablesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRouteTablesOutput, error) + DisassociateRouteTable(ctx context.Context, params *ec2.DisassociateRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.DisassociateRouteTableOutput, error) + DeleteRouteTable(ctx context.Context, params *ec2.DeleteRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.DeleteRouteTableOutput, error) + DescribeSecurityGroupRules(ctx context.Context, params *ec2.DescribeSecurityGroupRulesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupRulesOutput, error) + DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) + DescribeVpcPeeringConnections(ctx context.Context, params *ec2.DescribeVpcPeeringConnectionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcPeeringConnectionsOutput, error) + AcceptAddressTransfer(ctx context.Context, params *ec2.AcceptAddressTransferInput, optFns ...func(*ec2.Options)) (*ec2.AcceptAddressTransferOutput, error) + NetworkInterfaceAPI + EC2DhcpOptionAPI + NetworkACLAPI + EC2EndpointsAPI + NetworkACLAPI + InternetGatewayAPI + EgressOnlyIGAPI + EC2SubnetAPI + NatGatewaysAPI + SecurityGroupAPI +} +type ELBClientAPI interface { + DescribeLoadBalancers(ctx context.Context, input *elbv2.DescribeLoadBalancersInput, optFns ...func(*elbv2.Options)) (*elbv2.DescribeLoadBalancersOutput, error) + DeleteLoadBalancer(ctx context.Context, input *elbv2.DeleteLoadBalancerInput, optFns ...func(*elbv2.Options)) (*elbv2.DeleteLoadBalancerOutput, error) + DescribeTargetGroups(ctx context.Context, input *elbv2.DescribeTargetGroupsInput, optFns ...func(*elbv2.Options)) (*elbv2.DescribeTargetGroupsOutput, error) + DeleteTargetGroup(ctx context.Context, input *elbv2.DeleteTargetGroupInput, optFns ...func(*elbv2.Options)) (*elbv2.DeleteTargetGroupOutput, error) +} type EC2VPCs struct { BaseAwsResource - Client ec2iface.EC2API - ELBClient elbv2iface.ELBV2API + Client EC2VPCAPI + ELBClient ELBClientAPI Region string VPCIds []string } -func (v *EC2VPCs) Init(session *session.Session) { - v.Client = ec2.New(session) - v.ELBClient = elbv2.New(session) +func (v *EC2VPCs) InitV2(cfg aws.Config) { + v.Client = ec2.NewFromConfig(cfg) + v.ELBClient = elbv2.NewFromConfig(cfg) } +func (v *EC2VPCs) IsUsingV2() bool { return true } + // ResourceName - the simple name of the aws resource func (v *EC2VPCs) ResourceName() string { return "vpc" @@ -51,7 +82,7 @@ func (v *EC2VPCs) GetAndSetIdentifiers(c context.Context, configObj config.Confi return nil, err } - v.VPCIds = awsgo.StringValueSlice(identifiers) + v.VPCIds = awsgo.ToStringSlice(identifiers) return v.VPCIds, nil } diff --git a/aws/resources/nat_gateway.go b/aws/resources/nat_gateway.go index 07ecf514..2afe4b91 100644 --- a/aws/resources/nat_gateway.go +++ b/aws/resources/nat_gateway.go @@ -8,10 +8,10 @@ import ( "github.com/gruntwork-io/cloud-nuke/util" - awsgo "github.com/aws/aws-sdk-go/aws" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go/aws/awserr" - "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" @@ -20,53 +20,53 @@ import ( "github.com/hashicorp/go-multierror" ) -func (ngw *NatGateways) getAll(_ context.Context, configObj config.Config) ([]*string, error) { +func (ngw *NatGateways) getAll(ctx context.Context, configObj config.Config) ([]*string, error) { var allNatGateways []*string - err := ngw.Client.DescribeNatGatewaysPagesWithContext( - ngw.Context, - &ec2.DescribeNatGatewaysInput{}, - func(page *ec2.DescribeNatGatewaysOutput, lastPage bool) bool { - for _, ngw := range page.NatGateways { - if shouldIncludeNatGateway(ngw, configObj) { - allNatGateways = append(allNatGateways, ngw.NatGatewayId) - } + + // Use the paginator for DescribeNatGateways + paginator := ec2.NewDescribeNatGatewaysPaginator(ngw.Client, &ec2.DescribeNatGatewaysInput{}) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, errors.WithStackTrace(err) + } + + for _, gateway := range page.NatGateways { + if shouldIncludeNatGateway(gateway, configObj) { + allNatGateways = append(allNatGateways, gateway.NatGatewayId) } - return !lastPage - }, - ) + } + } // checking the nukable permissions ngw.VerifyNukablePermissions(allNatGateways, func(id *string) error { - _, err := ngw.Client.DeleteNatGateway(&ec2.DeleteNatGatewayInput{ + _, err := ngw.Client.DeleteNatGateway(ctx, &ec2.DeleteNatGatewayInput{ NatGatewayId: id, DryRun: awsgo.Bool(true), }) return err }) - return allNatGateways, errors.WithStackTrace(err) + return allNatGateways, nil } -func shouldIncludeNatGateway(ngw *ec2.NatGateway, configObj config.Config) bool { - if ngw == nil { - return false - } +func shouldIncludeNatGateway(ngw types.NatGateway, configObj config.Config) bool { - ngwState := awsgo.StringValue(ngw.State) - if ngwState == ec2.NatGatewayStateDeleted || ngwState == ec2.NatGatewayStateDeleting { + if ngw.State == types.NatGatewayStateDeleted || ngw.State == types.NatGatewayStateDeleting { return false } return configObj.NatGateway.ShouldInclude(config.ResourceValue{ Time: ngw.CreateTime, Name: getNatGatewayName(ngw), - Tags: util.ConvertEC2TagsToMap(ngw.Tags), + Tags: util.ConvertTypesTagsToMap(ngw.Tags), }) } -func getNatGatewayName(ngw *ec2.NatGateway) *string { +func getNatGatewayName(ngw types.NatGateway) *string { for _, tag := range ngw.Tags { - if awsgo.StringValue(tag.Key) == "Name" { + if awsgo.ToString(tag.Key) == "Name" { return tag.Value } } @@ -134,7 +134,7 @@ func (ngw *NatGateways) nukeAll(identifiers []*string) error { return errors.WithStackTrace(err) } for _, ngwID := range identifiers { - logging.Debugf("[OK] NAT Gateway %s was deleted in %s", awsgo.StringValue(ngwID), ngw.Region) + logging.Debugf("[OK] NAT Gateway %s was deleted in %s", awsgo.ToString(ngwID), ngw.Region) } return nil } @@ -145,7 +145,11 @@ func (ngw *NatGateways) nukeAll(identifiers []*string) error { func (ngw *NatGateways) areAllNatGatewaysDeleted(identifiers []*string) (bool, error) { // NOTE: we don't need to do pagination here, because the pagination is handled by the caller to this function, // based on NatGateways.MaxBatchSize. - resp, err := ngw.Client.DescribeNatGatewaysWithContext(ngw.Context, &ec2.DescribeNatGatewaysInput{NatGatewayIds: identifiers}) + natGatewayIDs := make([]string, len(identifiers)) + for i, id := range identifiers { + natGatewayIDs[i] = awsgo.ToString(id) + } + resp, err := ngw.Client.DescribeNatGateways(ngw.Context, &ec2.DescribeNatGatewaysInput{NatGatewayIds: natGatewayIDs}) if err != nil { if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NatGatewayNotFound" { return true, nil @@ -156,11 +160,7 @@ func (ngw *NatGateways) areAllNatGatewaysDeleted(identifiers []*string) (bool, e return true, nil } for _, ngw := range resp.NatGateways { - if ngw == nil { - continue - } - - if awsgo.StringValue(ngw.State) != ec2.NatGatewayStateDeleted { + if ngw.State != types.NatGatewayStateDeleted { return false, nil } } @@ -173,8 +173,8 @@ func (ngw *NatGateways) areAllNatGatewaysDeleted(identifiers []*string) (bool, e func (ngw *NatGateways) deleteAsync(wg *sync.WaitGroup, errChan chan error, ngwID *string) { defer wg.Done() - if nukable, reason := ngw.IsNukable(awsgo.StringValue(ngwID)); !nukable { - logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(ngwID), reason) + if nukable, reason := ngw.IsNukable(awsgo.ToString(ngwID)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.ToString(ngwID), reason) errChan <- nil return } @@ -182,7 +182,7 @@ func (ngw *NatGateways) deleteAsync(wg *sync.WaitGroup, errChan chan error, ngwI err := nukeNATGateway(ngw.Client, ngwID) // Record status of this resource e := report.Entry{ - Identifier: awsgo.StringValue(ngwID), + Identifier: awsgo.ToString(ngwID), ResourceType: "NAT Gateway", Error: err, } @@ -199,14 +199,14 @@ func (err TooManyNatErr) Error() string { return "Too many NAT Gateways requested at once." } -func nukeNATGateway(client ec2iface.EC2API, gateway *string) error { - logging.Debugf("Deleting NAT gateway %s", awsgo.StringValue(gateway)) +func nukeNATGateway(client NatGatewaysAPI, gateway *string) error { + logging.Debugf("Deleting NAT gateway %s", awsgo.ToString(gateway)) - _, err := client.DeleteNatGateway(&ec2.DeleteNatGatewayInput{NatGatewayId: gateway}) + _, err := client.DeleteNatGateway(context.Background(), &ec2.DeleteNatGatewayInput{NatGatewayId: gateway}) if err != nil { - logging.Debugf("[Failed] Error deleting NAT gateway %s: %s", awsgo.StringValue(gateway), err) + logging.Debugf("[Failed] Error deleting NAT gateway %s: %s", awsgo.ToString(gateway), err) return errors.WithStackTrace(err) } - logging.Debugf("[Ok] NAT Gateway deleted successfully %s", awsgo.StringValue(gateway)) + logging.Debugf("[Ok] NAT Gateway deleted successfully %s", awsgo.ToString(gateway)) return nil } diff --git a/aws/resources/nat_gateway_test.go b/aws/resources/nat_gateway_test.go index 97877144..0875f48d 100644 --- a/aws/resources/nat_gateway_test.go +++ b/aws/resources/nat_gateway_test.go @@ -7,32 +7,26 @@ import ( "time" "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/stretchr/testify/require" ) type mockedNatGateway struct { - ec2iface.EC2API + NatGatewaysAPI DeleteNatGatewayOutput ec2.DeleteNatGatewayOutput DescribeNatGatewaysOutput ec2.DescribeNatGatewaysOutput DescribeNatGatewaysError error } -func (m mockedNatGateway) DeleteNatGateway(input *ec2.DeleteNatGatewayInput) (*ec2.DeleteNatGatewayOutput, error) { +func (m mockedNatGateway) DeleteNatGateway(ctx context.Context, params *ec2.DeleteNatGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNatGatewayOutput, error) { return &m.DeleteNatGatewayOutput, nil } -func (m mockedNatGateway) DescribeNatGatewaysPagesWithContext(_ aws.Context, input *ec2.DescribeNatGatewaysInput, fn func(*ec2.DescribeNatGatewaysOutput, bool) bool, _ ...request.Option) error { - fn(&m.DescribeNatGatewaysOutput, true) - return nil -} - -func (m mockedNatGateway) DescribeNatGatewaysWithContext(_ aws.Context, input *ec2.DescribeNatGatewaysInput, _ ...request.Option) (*ec2.DescribeNatGatewaysOutput, error) { +func (m mockedNatGateway) DescribeNatGateways(ctx context.Context, params *ec2.DescribeNatGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNatGatewaysOutput, error) { return &m.DescribeNatGatewaysOutput, m.DescribeNatGatewaysError } @@ -48,10 +42,10 @@ func TestNatGateway_GetAll(t *testing.T) { ng := NatGateways{ Client: mockedNatGateway{ DescribeNatGatewaysOutput: ec2.DescribeNatGatewaysOutput{ - NatGateways: []*ec2.NatGateway{ + NatGateways: []types.NatGateway{ { NatGatewayId: aws.String(testId1), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: aws.String("Name"), Value: aws.String(testName1), @@ -61,7 +55,7 @@ func TestNatGateway_GetAll(t *testing.T) { }, { NatGatewayId: aws.String(testId2), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: aws.String("Name"), Value: aws.String(testName2), @@ -105,7 +99,7 @@ func TestNatGateway_GetAll(t *testing.T) { NatGateway: tc.configObj, }) require.NoError(t, err) - require.Equal(t, tc.expected, aws.StringValueSlice(names)) + require.Equal(t, tc.expected, aws.ToStringSlice(names)) }) } } diff --git a/aws/resources/nat_gateway_types.go b/aws/resources/nat_gateway_types.go index 20753d21..590e4d09 100644 --- a/aws/resources/nat_gateway_types.go +++ b/aws/resources/nat_gateway_types.go @@ -3,26 +3,31 @@ package resources import ( "context" - 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/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/go-commons/errors" ) +type NatGatewaysAPI interface { + DeleteNatGateway(ctx context.Context, params *ec2.DeleteNatGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNatGatewayOutput, error) + DescribeNatGateways(ctx context.Context, params *ec2.DescribeNatGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNatGatewaysOutput, error) +} + // NatGateways - represents all AWS secrets manager secrets that should be deleted. type NatGateways struct { BaseAwsResource - Client ec2iface.EC2API + Client NatGatewaysAPI Region string NatGatewayIDs []string } -func (ngw *NatGateways) Init(session *session.Session) { - ngw.Client = ec2.New(session) +func (ngw *NatGateways) InitV2(cfg aws.Config) { + ngw.Client = ec2.NewFromConfig(cfg) } +func (ngw *NatGateways) IsUsingV2() bool { return true } + // ResourceName - the simple name of the aws resource func (ngw *NatGateways) ResourceName() string { return "nat-gateway" @@ -50,13 +55,13 @@ func (ngw *NatGateways) GetAndSetIdentifiers(c context.Context, configObj config return nil, err } - ngw.NatGatewayIDs = awsgo.StringValueSlice(identifiers) + ngw.NatGatewayIDs = aws.ToStringSlice(identifiers) return ngw.NatGatewayIDs, nil } // Nuke - nuke 'em all!!! func (ngw *NatGateways) Nuke(identifiers []string) error { - if err := ngw.nukeAll(awsgo.StringSlice(identifiers)); err != nil { + if err := ngw.nukeAll(aws.StringSlice(identifiers)); err != nil { return errors.WithStackTrace(err) } diff --git a/aws/resources/security_group.go b/aws/resources/security_group.go index 95599a05..291f54cd 100644 --- a/aws/resources/security_group.go +++ b/aws/resources/security_group.go @@ -5,9 +5,10 @@ import ( "fmt" "time" - 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" + awsgo "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" r "github.com/gruntwork-io/cloud-nuke/report" // Alias the package as 'r' @@ -16,7 +17,7 @@ import ( ) // shouldIncludeSecurityGroup determines whether a security group should be included for deletion based on the provided configuration. -func shouldIncludeSecurityGroup(sg *ec2.SecurityGroup, firstSeenTime *time.Time, configObj config.Config) bool { +func shouldIncludeSecurityGroup(sg types.SecurityGroup, firstSeenTime *time.Time, configObj config.Config) bool { var groupName = sg.GroupName if !configObj.SecurityGroup.DefaultOnly && *groupName == "default" { @@ -26,7 +27,7 @@ func shouldIncludeSecurityGroup(sg *ec2.SecurityGroup, firstSeenTime *time.Time, return configObj.SecurityGroup.ShouldInclude(config.ResourceValue{ Name: groupName, - Tags: util.ConvertEC2TagsToMap(sg.Tags), + Tags: util.ConvertTypesTagsToMap(sg.Tags), Time: firstSeenTime, }) } @@ -37,22 +38,22 @@ func (sg *SecurityGroup) getAll(c context.Context, configObj config.Config) ([]* var firstSeenTime *time.Time var err error - var filters []*ec2.Filter + var filters []types.Filter if configObj.SecurityGroup.DefaultOnly { // Note : we can't simply remove the default security groups. Instead, we're only able to revoke permissions on the security group rules. // Setting a flag that can be accessed within the nuke method to check if the nuking is for default or not. sg.NukeOnlyDefault = configObj.SecurityGroup.DefaultOnly logging.Debugf("[default only] Retrieving the default security-groups") - filters = []*ec2.Filter{ + filters = []types.Filter{ { Name: awsgo.String("group-name"), - Values: awsgo.StringSlice([]string{"default"}), + Values: []string{"default"}, }, } } - resp, err := sg.Client.DescribeSecurityGroupsWithContext(sg.Context, &ec2.DescribeSecurityGroupsInput{ + resp, err := sg.Client.DescribeSecurityGroups(sg.Context, &ec2.DescribeSecurityGroupsInput{ Filters: filters, }) if err != nil { @@ -61,7 +62,7 @@ func (sg *SecurityGroup) getAll(c context.Context, configObj config.Config) ([]* } for _, group := range resp.SecurityGroups { - firstSeenTime, err = util.GetOrCreateFirstSeen(c, sg.Client, group.GroupId, util.ConvertEC2TagsToMap(group.Tags)) + firstSeenTime, err = util.GetOrCreateFirstSeen(c, sg.Client, group.GroupId, util.ConvertTypesTagsToMap(group.Tags)) if err != nil { logging.Error("unable to retrieve first seen tag") return nil, errors.WithStackTrace(err) @@ -74,7 +75,7 @@ func (sg *SecurityGroup) getAll(c context.Context, configObj config.Config) ([]* // Check and verify the list of allowed nuke actions sg.VerifyNukablePermissions(identifiers, func(id *string) error { - _, err := sg.Client.DeleteSecurityGroupWithContext(sg.Context, &ec2.DeleteSecurityGroupInput{ + _, err := sg.Client.DeleteSecurityGroup(sg.Context, &ec2.DeleteSecurityGroupInput{ GroupId: id, DryRun: awsgo.Bool(true), }) @@ -85,9 +86,9 @@ func (sg *SecurityGroup) getAll(c context.Context, configObj config.Config) ([]* } func (sg *SecurityGroup) detachAssociatedSecurityGroups(id *string) error { - logging.Debugf("[Security Group detach from dependancy] detaching the security group %s from dependant", awsgo.StringValue(id)) + logging.Debugf("[Security Group detach from dependancy] detaching the security group %s from dependant", awsgo.ToString(id)) - resp, err := sg.Client.DescribeSecurityGroupsWithContext(sg.Context, &ec2.DescribeSecurityGroupsInput{}) + resp, err := sg.Client.DescribeSecurityGroups(sg.Context, &ec2.DescribeSecurityGroupsInput{}) if err != nil { logging.Debugf("[Security Group] Failed to list security groups: %s", err) return errors.WithStackTrace(err) @@ -95,17 +96,18 @@ func (sg *SecurityGroup) detachAssociatedSecurityGroups(id *string) error { for _, securityGroup := range resp.SecurityGroups { // omit the check for current security group - if awsgo.StringValue(id) == awsgo.StringValue(securityGroup.GroupId) { + if awsgo.ToString(id) == awsgo.ToString(securityGroup.GroupId) { continue } hasMatching, revokeIpPermissions := hasMatchingGroupIdRule(id, securityGroup.IpPermissions) if hasMatching && len(revokeIpPermissions) > 0 { - logging.Debugf("[Security Group revoke ingress] revoking the ingress rules of %s", awsgo.StringValue(securityGroup.GroupId)) - _, err := sg.Client.RevokeSecurityGroupIngress(&ec2.RevokeSecurityGroupIngressInput{ + logging.Debugf("[Security Group revoke ingress] revoking the ingress rules of %s", awsgo.ToString(securityGroup.GroupId)) + _, err := sg.Client.RevokeSecurityGroupIngress(sg.Context, &ec2.RevokeSecurityGroupIngressInput{ GroupId: securityGroup.GroupId, IpPermissions: revokeIpPermissions, }) + if err != nil { logging.Debugf("[Security Group] Failed to revoke ingress rules: %s", err) return errors.WithStackTrace(err) @@ -115,8 +117,8 @@ func (sg *SecurityGroup) detachAssociatedSecurityGroups(id *string) error { // check egress rule hasMatchingEgress, revokeIpPermissions := hasMatchingGroupIdRule(id, securityGroup.IpPermissionsEgress) if hasMatchingEgress && len(revokeIpPermissions) > 0 { - logging.Debugf("[Security Group revoke ingress] revoking the egress rules of %s", awsgo.StringValue(securityGroup.GroupId)) - _, err := sg.Client.RevokeSecurityGroupEgress(&ec2.RevokeSecurityGroupEgressInput{ + logging.Debugf("[Security Group revoke ingress] revoking the egress rules of %s", awsgo.ToString(securityGroup.GroupId)) + _, err := sg.Client.RevokeSecurityGroupEgress(sg.Context, &ec2.RevokeSecurityGroupEgressInput{ GroupId: securityGroup.GroupId, IpPermissions: revokeIpPermissions, }) @@ -130,16 +132,16 @@ func (sg *SecurityGroup) detachAssociatedSecurityGroups(id *string) error { return nil } -func hasMatchingGroupIdRule(checkingGroup *string, IpPermission []*ec2.IpPermission) (bool, []*ec2.IpPermission) { +func hasMatchingGroupIdRule(checkingGroup *string, IpPermission []types.IpPermission) (bool, []types.IpPermission) { var hasMatching bool - var revokeIpPermissions []*ec2.IpPermission + var revokeIpPermissions []types.IpPermission for _, ipPermission := range IpPermission { - revokeIdGroupPairs := make([]*ec2.UserIdGroupPair, 0) // Create a new slice to store filtered pairs + revokeIdGroupPairs := make([]types.UserIdGroupPair, 0) // Create a new slice to store filtered pairs for _, pair := range ipPermission.UserIdGroupPairs { // Check if GroupId match the checkingGroup - if awsgo.StringValue(pair.GroupId) == awsgo.StringValue(checkingGroup) { + if awsgo.ToString(pair.GroupId) == awsgo.ToString(checkingGroup) { revokeIdGroupPairs = append(revokeIdGroupPairs, pair) // Append to the filtered slice hasMatching = true // Set the flag if a match is found } @@ -191,16 +193,16 @@ func (sg *SecurityGroup) nuke(id *string) error { return nil } -func revokeSecurityGroupIngress(client ec2iface.EC2API, id *string) error { - logging.Debug(fmt.Sprintf("Start revoking security groups ingress rule : %s", awsgo.StringValue(id))) - _, err := client.RevokeSecurityGroupIngress(&ec2.RevokeSecurityGroupIngressInput{ +func revokeSecurityGroupIngress(client SecurityGroupAPI, id *string) error { + logging.Debug(fmt.Sprintf("Start revoking security groups ingress rule : %s", awsgo.ToString(id))) + _, err := client.RevokeSecurityGroupIngress(context.TODO(), &ec2.RevokeSecurityGroupIngressInput{ GroupId: id, - IpPermissions: []*ec2.IpPermission{ + IpPermissions: []types.IpPermission{ { IpProtocol: awsgo.String("-1"), - FromPort: awsgo.Int64(0), - ToPort: awsgo.Int64(0), - UserIdGroupPairs: []*ec2.UserIdGroupPair{{GroupId: id}}, + FromPort: awsgo.Int32(0), + ToPort: awsgo.Int32(0), + UserIdGroupPairs: []types.UserIdGroupPair{{GroupId: id}}, }, }, }) @@ -210,24 +212,24 @@ func revokeSecurityGroupIngress(client ec2iface.EC2API, id *string) error { return nil } - logging.Debugf("[Security Group] Failed to revoke security group ingress associated with security group %s: %s", awsgo.StringValue(id), err) + logging.Debugf("[Security Group] Failed to revoke security group ingress associated with security group %s: %s", awsgo.ToString(id), err) return errors.WithStackTrace(err) } - logging.Debugf("Successfully revoked security group ingress rule: %s", awsgo.StringValue(id)) + logging.Debugf("Successfully revoked security group ingress rule: %s", awsgo.ToString(id)) return nil } -func revokeSecurityGroupEgress(client ec2iface.EC2API, id *string) error { - logging.Debugf("Start revoking security groups ingress rule : %s", awsgo.StringValue(id)) +func revokeSecurityGroupEgress(client SecurityGroupAPI, id *string) error { + logging.Debugf("Start revoking security groups ingress rule : %s", awsgo.ToString(id)) - _, err := client.RevokeSecurityGroupEgress(&ec2.RevokeSecurityGroupEgressInput{ + _, err := client.RevokeSecurityGroupEgress(context.TODO(), &ec2.RevokeSecurityGroupEgressInput{ GroupId: (id), - IpPermissions: []*ec2.IpPermission{ + IpPermissions: []types.IpPermission{ { IpProtocol: awsgo.String("-1"), - FromPort: awsgo.Int64(0), - ToPort: awsgo.Int64(0), - IpRanges: []*ec2.IpRange{{CidrIp: awsgo.String("0.0.0.0/0")}}, + FromPort: awsgo.Int32(0), + ToPort: awsgo.Int32(0), + IpRanges: []types.IpRange{{CidrIp: awsgo.String("0.0.0.0/0")}}, }, }, }) @@ -237,24 +239,24 @@ func revokeSecurityGroupEgress(client ec2iface.EC2API, id *string) error { return nil } - logging.Debugf("[Security Group] Failed to revoke security group egress associated with security group %s: %s", awsgo.StringValue(id), err) + logging.Debugf("[Security Group] Failed to revoke security group egress associated with security group %s: %s", awsgo.ToString(id), err) return errors.WithStackTrace(err) } - logging.Debugf("Successfully revoked security group egress rule: %s", awsgo.StringValue(id)) + logging.Debugf("Successfully revoked security group egress rule: %s", awsgo.ToString(id)) return nil } func (sg *SecurityGroup) RevokeIPv6SecurityGroupEgress(id string) error { - _, err := sg.Client.RevokeSecurityGroupEgressWithContext(sg.Context, &ec2.RevokeSecurityGroupEgressInput{ + _, err := sg.Client.RevokeSecurityGroupEgress(sg.Context, &ec2.RevokeSecurityGroupEgressInput{ GroupId: awsgo.String(id), - IpPermissions: []*ec2.IpPermission{ + IpPermissions: []types.IpPermission{ { IpProtocol: awsgo.String("-1"), - FromPort: awsgo.Int64(0), - ToPort: awsgo.Int64(0), - Ipv6Ranges: []*ec2.Ipv6Range{{CidrIpv6: awsgo.String("::/0")}}, + FromPort: awsgo.Int32(0), + ToPort: awsgo.Int32(0), + Ipv6Ranges: []types.Ipv6Range{{CidrIpv6: awsgo.String("::/0")}}, }, }, }) @@ -273,11 +275,11 @@ func (sg *SecurityGroup) RevokeIPv6SecurityGroupEgress(id string) error { func (sg *SecurityGroup) terminateInstancesAssociatedWithSecurityGroup(id string) error { - resp, err := sg.Client.DescribeInstancesWithContext(sg.Context, &ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ + resp, err := sg.Client.DescribeInstances(sg.Context, &ec2.DescribeInstancesInput{ + Filters: []types.Filter{ { Name: awsgo.String("instance.group-id"), - Values: []*string{awsgo.String(id)}, + Values: []string{id}, }, }, }) @@ -288,7 +290,7 @@ func (sg *SecurityGroup) terminateInstancesAssociatedWithSecurityGroup(id string for _, reservation := range resp.Reservations { for _, instance := range reservation.Instances { - instanceID := awsgo.StringValue(instance.InstanceId) + instanceID := awsgo.ToString(instance.InstanceId) // Needs to release the elastic ips attached on the instance before nuking if err := sg.releaseEIPs([]*string{instance.InstanceId}); err != nil { @@ -297,19 +299,21 @@ func (sg *SecurityGroup) terminateInstancesAssociatedWithSecurityGroup(id string } // terminating the instances which used this security group - if _, err := sg.Client.TerminateInstancesWithContext(sg.Context, &ec2.TerminateInstancesInput{ - InstanceIds: []*string{awsgo.String(instanceID)}, + if _, err := sg.Client.TerminateInstances(sg.Context, &ec2.TerminateInstancesInput{ + InstanceIds: []string{instanceID}, }); err != nil { logging.Debugf("[Failed] Ec2 termination %s", err) return errors.WithStackTrace(err) } logging.Debugf("[Instance Termination] waiting to terminate instance %s", instanceID) - + ec2Client, ok := sg.Client.(*ec2.Client) + if !ok { + return errors.WithStackTrace(err) + } // wait until the instance terminated. - if err := sg.Client.WaitUntilInstanceTerminatedWithContext(sg.Context, &ec2.DescribeInstancesInput{ - InstanceIds: []*string{awsgo.String(instanceID)}, - }); err != nil { + err = waitUntilInstanceTerminated(ec2Client, sg.Context, instanceID) + if err != nil { logging.Debugf("[Security Group] Failed to terminate instance %s associated with security group %s: %s", instanceID, id, err) return errors.WithStackTrace(err) } @@ -326,12 +330,12 @@ func (sg *SecurityGroup) releaseEIPs(instanceIds []*string) error { for _, instanceID := range instanceIds { // get the elastic ip's associated with the EC2's - output, err := sg.Client.DescribeAddressesWithContext(sg.Context, &ec2.DescribeAddressesInput{ - Filters: []*ec2.Filter{ + output, err := sg.Client.DescribeAddresses(sg.Context, &ec2.DescribeAddressesInput{ + Filters: []types.Filter{ { Name: awsgo.String("instance-id"), - Values: []*string{ - instanceID, + Values: []string{ + *instanceID, }, }, }, @@ -341,7 +345,7 @@ func (sg *SecurityGroup) releaseEIPs(instanceIds []*string) error { } for _, address := range output.Addresses { - if _, err := sg.Client.ReleaseAddressWithContext(sg.Context, &ec2.ReleaseAddressInput{ + if _, err := sg.Client.ReleaseAddress(sg.Context, &ec2.ReleaseAddressInput{ AllocationId: address.AllocationId, }); err != nil { logging.Debugf("An error happened while releasing the elastic ip address %s, error %v", *address.AllocationId, err) @@ -355,16 +359,16 @@ func (sg *SecurityGroup) releaseEIPs(instanceIds []*string) error { return nil } -func nukeSecurityGroup(client ec2iface.EC2API, id *string) error { - logging.Debugf("Deleting security group %s", awsgo.StringValue(id)) +func nukeSecurityGroup(client SecurityGroupAPI, id *string) error { + logging.Debugf("Deleting security group %s", awsgo.ToString(id)) - if _, err := client.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{ + if _, err := client.DeleteSecurityGroup(context.TODO(), &ec2.DeleteSecurityGroupInput{ GroupId: id, }); err != nil { - logging.Debugf("[Security Group] Failed to delete security group %s: %s", awsgo.StringValue(id), err) + logging.Debugf("[Security Group] Failed to delete security group %s: %s", awsgo.ToString(id), err) return errors.WithStackTrace(err) } - logging.Debugf("Deleted security group %s", awsgo.StringValue(id)) + logging.Debugf("Deleted security group %s", awsgo.ToString(id)) return nil } @@ -387,7 +391,7 @@ func (sg *SecurityGroup) nukeAll(identifiers []*string) error { err := sg.nuke(id) // Record status of this resource e := r.Entry{ - Identifier: awsgo.StringValue(id), + Identifier: awsgo.ToString(id), ResourceType: "Security Group", Error: err, } @@ -405,3 +409,21 @@ func (sg *SecurityGroup) nukeAll(identifiers []*string) error { return nil } + +// waitUntilInstanceTerminated checks the status of the instance until it is terminated. +func waitUntilInstanceTerminated(client *ec2.Client, ctx context.Context, instanceID string) error { + waiter := ec2.NewInstanceTerminatedWaiter(client) + + // Configure the maximum wait time + maxWaitTime := 5 * time.Minute + + // Call the waiter + err := waiter.Wait(ctx, &ec2.DescribeInstancesInput{ + InstanceIds: []string{instanceID}, + }, maxWaitTime) + if err != nil { + return err + } + + return nil +} diff --git a/aws/resources/security_group_test.go b/aws/resources/security_group_test.go index 47e6a9d1..cedb865a 100644 --- a/aws/resources/security_group_test.go +++ b/aws/resources/security_group_test.go @@ -6,34 +6,65 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" - awsgo "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/util" "github.com/stretchr/testify/require" ) type mockedSecurityGroup struct { - BaseAwsResource - ec2iface.EC2API - DescribeSecurityGroupsOutput ec2.DescribeSecurityGroupsOutput - DeleteSecurityGroupOutput ec2.DeleteSecurityGroupOutput - DescribeInstancesOutput ec2.DescribeInstancesOutput + DescribeSecurityGroupsOutput *ec2.DescribeSecurityGroupsOutput + DeleteSecurityGroupOutput *ec2.DeleteSecurityGroupOutput + DescribeInstancesOutput *ec2.DescribeInstancesOutput + AuthorizeSecurityGroupEgressOutput *ec2.AuthorizeSecurityGroupEgressOutput + AuthorizeSecurityGroupIngressOutput *ec2.AuthorizeSecurityGroupIngressOutput + CreateSecurityGroupOutput *ec2.CreateSecurityGroupOutput + DescribeAddressesOutput *ec2.DescribeAddressesOutput + ReleaseAddressOutput *ec2.ReleaseAddressOutput + RevokeSecurityGroupEgressOutput *ec2.RevokeSecurityGroupEgressOutput + RevokeSecurityGroupIngressOutput *ec2.RevokeSecurityGroupIngressOutput + TerminateInstancesOutput *ec2.TerminateInstancesOutput } -func (m mockedSecurityGroup) DescribeSecurityGroupsWithContext(_ awsgo.Context, _ *ec2.DescribeSecurityGroupsInput, _ ...request.Option) (*ec2.DescribeSecurityGroupsOutput, error) { - return &m.DescribeSecurityGroupsOutput, nil +func (m *mockedSecurityGroup) DescribeSecurityGroups(ctx context.Context, params *ec2.DescribeSecurityGroupsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error) { + return m.DescribeSecurityGroupsOutput, nil } -func (m mockedSecurityGroup) DescribeInstancesWithContext(_ awsgo.Context, _ *ec2.DescribeInstancesInput, _ ...request.Option) (*ec2.DescribeInstancesOutput, error) { - return &m.DescribeInstancesOutput, nil +func (m *mockedSecurityGroup) DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { + return m.DescribeInstancesOutput, nil } -func (m mockedSecurityGroup) DeleteSecurityGroupWithContext(_ awsgo.Context, _ *ec2.DeleteSecurityGroupInput, _ ...request.Option) (*ec2.DeleteSecurityGroupOutput, error) { - return &m.DeleteSecurityGroupOutput, nil +func (m *mockedSecurityGroup) DeleteSecurityGroup(ctx context.Context, params *ec2.DeleteSecurityGroupInput, optFns ...func(*ec2.Options)) (*ec2.DeleteSecurityGroupOutput, error) { + return m.DeleteSecurityGroupOutput, nil +} + +func (m *mockedSecurityGroup) AuthorizeSecurityGroupEgress(ctx context.Context, params *ec2.AuthorizeSecurityGroupEgressInput, optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupEgressOutput, error) { + return m.AuthorizeSecurityGroupEgressOutput, nil +} + +func (m *mockedSecurityGroup) AuthorizeSecurityGroupIngress(ctx context.Context, params *ec2.AuthorizeSecurityGroupIngressInput, optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupIngressOutput, error) { + return m.AuthorizeSecurityGroupIngressOutput, nil +} + +func (m *mockedSecurityGroup) CreateSecurityGroup(ctx context.Context, params *ec2.CreateSecurityGroupInput, optFns ...func(*ec2.Options)) (*ec2.CreateSecurityGroupOutput, error) { + return m.CreateSecurityGroupOutput, nil +} +func (m *mockedSecurityGroup) DescribeAddresses(ctx context.Context, params *ec2.DescribeAddressesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error) { + return m.DescribeAddressesOutput, nil +} +func (m *mockedSecurityGroup) ReleaseAddress(ctx context.Context, params *ec2.ReleaseAddressInput, optFns ...func(*ec2.Options)) (*ec2.ReleaseAddressOutput, error) { + return m.ReleaseAddressOutput, nil +} +func (m *mockedSecurityGroup) RevokeSecurityGroupEgress(ctx context.Context, params *ec2.RevokeSecurityGroupEgressInput, optFns ...func(*ec2.Options)) (*ec2.RevokeSecurityGroupEgressOutput, error) { + return m.RevokeSecurityGroupEgressOutput, nil +} +func (m *mockedSecurityGroup) RevokeSecurityGroupIngress(ctx context.Context, params *ec2.RevokeSecurityGroupIngressInput, optFns ...func(*ec2.Options)) (*ec2.RevokeSecurityGroupIngressOutput, error) { + return m.RevokeSecurityGroupIngressOutput, nil +} +func (m *mockedSecurityGroup) TerminateInstances(ctx context.Context, params *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) { + return m.TerminateInstancesOutput, nil } func TestSecurityGroup_GetAll(t *testing.T) { @@ -57,12 +88,12 @@ func TestSecurityGroup_GetAll(t *testing.T) { }, }, Client: &mockedSecurityGroup{ - DescribeSecurityGroupsOutput: ec2.DescribeSecurityGroupsOutput{ - SecurityGroups: []*ec2.SecurityGroup{ + DescribeSecurityGroupsOutput: &ec2.DescribeSecurityGroupsOutput{ + SecurityGroups: []types.SecurityGroup{ { GroupId: aws.String(testId1), GroupName: aws.String(testName1), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: aws.String("Name"), Value: aws.String(testName1), @@ -76,7 +107,7 @@ func TestSecurityGroup_GetAll(t *testing.T) { { GroupId: aws.String(testId2), GroupName: aws.String(testName2), - Tags: []*ec2.Tag{ + Tags: []types.Tag{ { Key: aws.String("Name"), Value: aws.String(testName2), @@ -133,7 +164,7 @@ func TestSecurityGroup_GetAll(t *testing.T) { SecurityGroup: tc.configObj, }) require.NoError(t, err) - require.Equal(t, tc.expected, aws.StringValueSlice(names)) + require.Equal(t, tc.expected, aws.ToStringSlice(names)) }) } } @@ -144,7 +175,7 @@ func Test_NukeAll(t *testing.T) { er := SecurityGroup{ Client: &mockedSecurityGroup{ - DeleteSecurityGroupOutput: ec2.DeleteSecurityGroupOutput{}, + DeleteSecurityGroupOutput: &ec2.DeleteSecurityGroupOutput{}, }, } diff --git a/aws/resources/security_group_types.go b/aws/resources/security_group_types.go index 7909824d..fcd7a5a2 100644 --- a/aws/resources/security_group_types.go +++ b/aws/resources/security_group_types.go @@ -3,27 +3,40 @@ package resources import ( "context" - "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/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/go-commons/errors" ) +type SecurityGroupAPI interface { + DescribeSecurityGroups(ctx context.Context, input *ec2.DescribeSecurityGroupsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error) + DeleteSecurityGroup(ctx context.Context, input *ec2.DeleteSecurityGroupInput, optFns ...func(*ec2.Options)) (*ec2.DeleteSecurityGroupOutput, error) + CreateSecurityGroup(ctx context.Context, input *ec2.CreateSecurityGroupInput, optFns ...func(*ec2.Options)) (*ec2.CreateSecurityGroupOutput, error) + AuthorizeSecurityGroupIngress(ctx context.Context, input *ec2.AuthorizeSecurityGroupIngressInput, optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupIngressOutput, error) + RevokeSecurityGroupIngress(ctx context.Context, input *ec2.RevokeSecurityGroupIngressInput, optFns ...func(*ec2.Options)) (*ec2.RevokeSecurityGroupIngressOutput, error) + AuthorizeSecurityGroupEgress(ctx context.Context, input *ec2.AuthorizeSecurityGroupEgressInput, optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupEgressOutput, error) + RevokeSecurityGroupEgress(ctx context.Context, input *ec2.RevokeSecurityGroupEgressInput, optFns ...func(*ec2.Options)) (*ec2.RevokeSecurityGroupEgressOutput, error) + DescribeInstances(ctx context.Context, input *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) + TerminateInstances(ctx context.Context, input *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) + DescribeAddresses(ctx context.Context, input *ec2.DescribeAddressesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error) + ReleaseAddress(ctx context.Context, params *ec2.ReleaseAddressInput, optFns ...func(*ec2.Options)) (*ec2.ReleaseAddressOutput, error) +} + type SecurityGroup struct { BaseAwsResource - Client ec2iface.EC2API + Client SecurityGroupAPI Region string SecurityGroups []string NukeOnlyDefault bool } -func (sg *SecurityGroup) Init(session *session.Session) { - sg.BaseAwsResource.Init(session) - sg.Client = ec2.New(session) +func (sg *SecurityGroup) InitV2(cfg aws.Config) { + sg.Client = ec2.NewFromConfig(cfg) } +func (sg *SecurityGroup) IsUsingV2() bool { return true } + func (sg *SecurityGroup) ResourceName() string { return "security-group" } @@ -42,7 +55,7 @@ func (sg *SecurityGroup) GetAndSetIdentifiers(c context.Context, configObj confi return nil, err } - sg.SecurityGroups = aws.StringValueSlice(identifiers) + sg.SecurityGroups = aws.ToStringSlice(identifiers) return sg.SecurityGroups, nil } diff --git a/util/error.go b/util/error.go index 07644f23..4b9aacbf 100644 --- a/util/error.go +++ b/util/error.go @@ -6,6 +6,7 @@ import ( "time" "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/smithy-go" commonErr "github.com/gruntwork-io/go-commons/errors" ) @@ -50,6 +51,12 @@ func TransformAWSError(err error) error { if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DryRunOperation" && awsErr.Message() == AwsDryRunSuccess { return nil } + var apiErr smithy.APIError + if errors.As(err, &apiErr) { + if apiErr.ErrorCode() == "DryRunOperation" && apiErr.ErrorMessage() == AwsDryRunSuccess { + return nil + } + } if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "InvalidPermission.NotFound" { return ErrInvalidPermisionNotFound } diff --git a/util/time.go b/util/time.go index d2eceea9..4d82d399 100644 --- a/util/time.go +++ b/util/time.go @@ -5,6 +5,11 @@ import ( "fmt" "time" + awsv2 "github.com/aws/aws-sdk-go-v2/aws" + ec2v2 "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + nwfwall "github.com/aws/aws-sdk-go-v2/service/networkfirewall" + nwfTypes "github.com/aws/aws-sdk-go-v2/service/networkfirewall/types" "github.com/aws/aws-sdk-go/aws" awsgo "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -99,6 +104,26 @@ func GetOrCreateFirstSeen(ctx context.Context, client interface{}, identifier *s }, }, }) + case *ec2v2.Client: + _, err = v.CreateTags(ctx, &ec2v2.CreateTagsInput{ + Resources: []string{*identifier}, + Tags: []ec2types.Tag{ + { + Key: awsv2.String(FirstSeenTagKey), + Value: awsv2.String(FormatTimestamp(now)), + }, + }, + }) + case *nwfwall.Client: + _, err = v.TagResource(ctx, &nwfwall.TagResourceInput{ + ResourceArn: identifier, + Tags: []nwfTypes.Tag{ + { + Key: awsv2.String(FirstSeenTagKey), + Value: awsv2.String(FormatTimestamp(now)), + }, + }, + }) default: return nil, errors.WithStackTrace(fmt.Errorf("invalid type %v for first seen tag", v)) } diff --git a/v2_migration_report/output.md b/v2_migration_report/output.md index db904aea..38a6d3f8 100644 --- a/v2_migration_report/output.md +++ b/v2_migration_report/output.md @@ -31,13 +31,13 @@ run `go generate ./...` to refresh this report. | ec2-endpoint | :white_check_mark: | | ec2-keypairs | :white_check_mark: | | ec2-placement-groups | :white_check_mark: | -| ec2-subnet | | +| ec2-subnet | :white_check_mark: | | ec2_dhcp_option | :white_check_mark: | | ecr | :white_check_mark: | | ecscluster | :white_check_mark: | | ecsserv | :white_check_mark: | | efs | :white_check_mark: | -| egress-only-internet-gateway | | +| egress-only-internet-gateway | :white_check_mark: | | eip | :white_check_mark: | | ekscluster | :white_check_mark: | | elastic-beanstalk | :white_check_mark: | @@ -59,7 +59,7 @@ run `go generate ./...` to refresh this report. | iam-policy | :white_check_mark: | | iam-role | :white_check_mark: | | iam-service-linked-role | :white_check_mark: | -| internet-gateway | | +| internet-gateway | :white_check_mark: | | ipam | :white_check_mark: | | ipam-byoasn | :white_check_mark: | | ipam-custom-allocation | :white_check_mark: | @@ -76,14 +76,14 @@ run `go generate ./...` to refresh this report. | macie-member | :white_check_mark: | | managed-prometheus | :white_check_mark: | | msk-cluster | :white_check_mark: | -| nat-gateway | | -| network-acl | | +| nat-gateway | :white_check_mark: | +| network-acl | :white_check_mark: | | network-firewall | :white_check_mark: | | network-firewall-policy | :white_check_mark: | | network-firewall-resource-policy | :white_check_mark: | | network-firewall-rule-group | :white_check_mark: | | network-firewall-tls-config | :white_check_mark: | -| network-interface | | +| network-interface | :white_check_mark: | | oidcprovider | :white_check_mark: | | opensearchdomain | :white_check_mark: | | rds | :white_check_mark: | @@ -104,7 +104,7 @@ run `go generate ./...` to refresh this report. | s3-olap | :white_check_mark: | | sagemaker-notebook-smni | :white_check_mark: | | secretsmanager | :white_check_mark: | -| security-group | | +| security-group | :white_check_mark: | | security-hub | :white_check_mark: | | ses-configuration-set | :white_check_mark: | | ses-email-template | :white_check_mark: | @@ -117,7 +117,7 @@ run `go generate ./...` to refresh this report. | transit-gateway | :white_check_mark: | | transit-gateway-attachment | :white_check_mark: | | transit-gateway-route-table | | -| vpc | | +| vpc | :white_check_mark: | | vpc-lattice-service | :white_check_mark: | | vpc-lattice-service-network | :white_check_mark: | | vpc-lattice-target-group | :white_check_mark: |