Skip to content

Commit

Permalink
add support for nuking dataSync Task(s) & dataSync Location(s). (#729)
Browse files Browse the repository at this point in the history
  • Loading branch information
wakeful authored Jun 26, 2024
1 parent 1ab0333 commit a96a10f
Show file tree
Hide file tree
Showing 10 changed files with 551 additions and 117 deletions.
237 changes: 120 additions & 117 deletions README.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions aws/resource_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ func getRegisteredRegionalResources() []AwsResource {
&resources.CodeDeployApplications{},
&resources.ConfigServiceRecorders{},
&resources.ConfigServiceRule{},
&resources.DataSyncTask{},
&resources.DataSyncLocation{},
&resources.DynamoDB{},
&resources.EBSVolumes{},
&resources.EBApplications{},
Expand Down
68 changes: 68 additions & 0 deletions aws/resources/datasync_location.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package resources

import (
"context"

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

func (dsl *DataSyncLocation) nukeAll(identifiers []*string) error {
if len(identifiers) == 0 {
logging.Debugf("[Data Sync Location] No Data Sync Locations found in region %s", dsl.Region)
return nil
}

logging.Debugf("[Data Sync Location] Deleting all Data Sync Locations in region %s", dsl.Region)
var deleted []*string

for _, identifier := range identifiers {
logging.Debugf("[Data Sync Location] Deleting Data Sync Location %s in region %s", *identifier, dsl.Region)
_, err := dsl.Client.DeleteLocationWithContext(dsl.Context, &datasync.DeleteLocationInput{
LocationArn: identifier,
})
if err != nil {
logging.Debugf("[Data Sync Location] Error deleting Data Sync Location %s in region %s", *identifier, dsl.Region)
return err
} else {
deleted = append(deleted, identifier)
logging.Debugf("[Data Sync Location] Deleted Data Sync Location %s in region %s", *identifier, dsl.Region)
}

e := report.Entry{
Identifier: aws.StringValue(identifier),
ResourceType: dsl.ResourceName(),
Error: err,
}
report.Record(e)
}

logging.Debugf("[OK] %d Data Sync Location(s) nuked in %s", len(deleted), dsl.Region)
return nil
}

func (dsl *DataSyncLocation) getAll(c context.Context, _ config.Config) ([]*string, error) {
var identifiers []*string
paginator := func(output *datasync.ListLocationsOutput, lastPage bool) bool {
for _, location := range output.Locations {
identifiers = append(identifiers, location.LocationArn)
}

return !lastPage
}

param := &datasync.ListLocationsInput{
MaxResults: aws.Int64(100),
}

if err := dsl.Client.ListLocationsPagesWithContext(c, param, paginator); err != nil {
logging.Debugf("[Data Sync Location] Failed to list Data Sync Locations: %s", err)
return nil, errors.WithStackTrace(err)
}

return identifiers, nil
}
86 changes: 86 additions & 0 deletions aws/resources/datasync_location_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package resources

import (
"context"
"fmt"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/datasync"
"github.com/aws/aws-sdk-go/service/datasync/datasynciface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type mockDatasyncLocation struct {
datasynciface.DataSyncAPI
ListLocationsOutput datasync.ListLocationsOutput
DeleteLocationOutput datasync.DeleteLocationOutput
}

func (m mockDatasyncLocation) ListLocationsPagesWithContext(_ aws.Context, _ *datasync.ListLocationsInput, callback func(*datasync.ListLocationsOutput, bool) bool, _ ...request.Option) error {
callback(&m.ListLocationsOutput, true)
return nil
}

func (m mockDatasyncLocation) DeleteLocationWithContext(aws.Context, *datasync.DeleteLocationInput, ...request.Option) (*datasync.DeleteLocationOutput, error) {
return &m.DeleteLocationOutput, nil
}

func TestDataSyncLocation_NukeAll(t *testing.T) {

t.Parallel()

testName := "test-datasync-location"
service := DataSyncLocation{
Client: mockDatasyncLocation{
DeleteLocationOutput: datasync.DeleteLocationOutput{},
},
}

err := service.nukeAll([]*string{&testName})
assert.NoError(t, err)
}

func TestDataSyncLocation_GetAll(t *testing.T) {

t.Parallel()

testName1 := "test-datasync-location-1"
testName2 := "test-datasync-location-2"
location := DataSyncLocation{
Client: mockDatasyncLocation{
ListLocationsOutput: datasync.ListLocationsOutput{
Locations: []*datasync.LocationListEntry{
{
LocationArn: aws.String(fmt.Sprintf("arn::location/%s", testName1)),
},
{
LocationArn: aws.String(fmt.Sprintf("arn::location/%s", testName2)),
},
},
},
},
}

tests := map[string]struct {
configObj config.ResourceType
expected []string
}{
"emptyFilter": {
configObj: config.ResourceType{},
expected: []string{fmt.Sprintf("arn::location/%s", testName1), fmt.Sprintf("arn::location/%s", testName2)},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
names, err := location.getAll(context.Background(), config.Config{
DataSyncLocation: tc.configObj,
})
require.NoError(t, err)
require.Equal(t, tc.expected, aws.StringValueSlice(names))
})
}
}
51 changes: 51 additions & 0 deletions aws/resources/datasync_location_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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/datasync"
"github.com/aws/aws-sdk-go/service/datasync/datasynciface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/go-commons/errors"
)

type DataSyncLocation struct {
BaseAwsResource
Client datasynciface.DataSyncAPI
Region string
DataSyncLocations []string
}

func (dsl *DataSyncLocation) GetAndSetResourceConfig(configObj config.Config) config.ResourceType {
return configObj.DataSyncLocation
}

func (dsl *DataSyncLocation) Init(session *session.Session) {
dsl.Client = datasync.New(session)
}

func (dsl *DataSyncLocation) ResourceName() string { return "data-sync-location" }

func (dsl *DataSyncLocation) ResourceIdentifiers() []string { return dsl.DataSyncLocations }

func (dsl *DataSyncLocation) MaxBatchSize() int { return 19 }

func (dsl *DataSyncLocation) Nuke(identifiers []string) error {
if err := dsl.nukeAll(aws.StringSlice(identifiers)); err != nil {
return errors.WithStackTrace(err)
}

return nil
}

func (dsl *DataSyncLocation) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
identifiers, err := dsl.getAll(c, configObj)
if err != nil {
return nil, err
}

dsl.DataSyncLocations = aws.StringValueSlice(identifiers)
return dsl.DataSyncLocations, nil
}
72 changes: 72 additions & 0 deletions aws/resources/datasync_task.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package resources

import (
"context"

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

func (dst *DataSyncTask) nukeAll(identifiers []*string) error {
if len(identifiers) == 0 {
logging.Debugf("[Data Sync Task] No Data Sync Tasks found in region %s", dst.Region)
return nil
}

logging.Debugf("[Data Sync Task] Deleting all Data Sync Tasks in region %s", dst.Region)
var deleted []*string

for _, identifier := range identifiers {
logging.Debugf("[Data Sync Task] Deleting Data Sync Task %s in region %s", *identifier, dst.Region)
_, err := dst.Client.DeleteTaskWithContext(dst.Context, &datasync.DeleteTaskInput{
TaskArn: identifier,
})
if err != nil {
logging.Debugf("[Data Sync Task] Error deleting Data Sync Task %s in region %s", *identifier, dst.Region)
return err
} else {
deleted = append(deleted, identifier)
logging.Debugf("[Data Sync Task] Deleted Data Sync Task %s in region %s", *identifier, dst.Region)
}

e := report.Entry{
Identifier: aws.StringValue(identifier),
ResourceType: dst.ResourceName(),
Error: err,
}
report.Record(e)
}

logging.Debugf("[OK] %d Data Sync Task(s) nuked in %s", len(deleted), dst.Region)
return nil
}

func (dst *DataSyncTask) getAll(c context.Context, configObj config.Config) ([]*string, error) {
var identifiers []*string
paginator := func(output *datasync.ListTasksOutput, lastPage bool) bool {
for _, task := range output.Tasks {
if configObj.DataSyncTask.ShouldInclude(config.ResourceValue{
Name: task.Name,
}) {
identifiers = append(identifiers, task.TaskArn)
}
}

return !lastPage
}

param := &datasync.ListTasksInput{
MaxResults: aws.Int64(100),
}

if err := dst.Client.ListTasksPagesWithContext(c, param, paginator); err != nil {
logging.Debugf("[Data Sync Task] Failed to list data sync tasks: %s", err)
return nil, errors.WithStackTrace(err)
}

return identifiers, nil
}
97 changes: 97 additions & 0 deletions aws/resources/datasync_task_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package resources

import (
"context"
"fmt"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/datasync"
"github.com/aws/aws-sdk-go/service/datasync/datasynciface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type mockDataSyncTask struct {
datasynciface.DataSyncAPI
ListTasksOutput datasync.ListTasksOutput
DeleteTaskOutput datasync.DeleteTaskOutput
}

func (m mockDataSyncTask) ListTasksPagesWithContext(_ aws.Context, _ *datasync.ListTasksInput, callback func(*datasync.ListTasksOutput, bool) bool, _ ...request.Option) error {
callback(&m.ListTasksOutput, true)
return nil
}

func (m mockDataSyncTask) DeleteTaskWithContext(aws.Context, *datasync.DeleteTaskInput, ...request.Option) (*datasync.DeleteTaskOutput, error) {
return &m.DeleteTaskOutput, nil
}

func TestDataSyncTask_NukeAll(t *testing.T) {

t.Parallel()

testName := "test-datasync-task"
task := DataSyncTask{
Client: mockDataSyncTask{
DeleteTaskOutput: datasync.DeleteTaskOutput{},
},
}

err := task.nukeAll([]*string{&testName})
assert.NoError(t, err)
}

func TestDataSyncTask_GetAll(t *testing.T) {

t.Parallel()

testName1 := "test-task-1"
testName2 := "test-task-2"

task := DataSyncTask{Client: mockDataSyncTask{
ListTasksOutput: datasync.ListTasksOutput{
Tasks: []*datasync.TaskListEntry{
{
Name: &testName1,
TaskArn: aws.String(fmt.Sprintf("arn::%s", testName1)),
},
{
Name: &testName2,
TaskArn: aws.String(fmt.Sprintf("arn::%s", testName2)),
},
},
},
}}

tests := map[string]struct {
configObj config.ResourceType
expected []string
}{
"emptyFilter": {
configObj: config.ResourceType{},
expected: []string{fmt.Sprintf("arn::%s", testName1), fmt.Sprintf("arn::%s", testName2)},
},
"nameExclusionFilter": {
configObj: config.ResourceType{
ExcludeRule: config.FilterRule{
NamesRegExp: []config.Expression{{
RE: *regexp.MustCompile(testName1),
}},
}},
expected: []string{fmt.Sprintf("arn::%s", testName2)},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
names, err := task.getAll(context.Background(), config.Config{
DataSyncTask: tc.configObj,
})
require.NoError(t, err)
require.Equal(t, tc.expected, aws.StringValueSlice(names))
})
}
}
Loading

0 comments on commit a96a10f

Please sign in to comment.