Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for nuking dataSync Task(s) & dataSync Location(s). #729

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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