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

Implement nuking for RDS Proxy. #720

Merged
merged 1 commit into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Cloud-nuke suppports 🔎 inspecting and 🔥💀 deleting the following AWS res
| RDS | Neptune |
| RDS | Document DB instances |
| RDS | RDS parameter group |
| RDS | RDS Proxy |
| Security Hub | Hubs |
| Security Hub | Members |
| Security Hub | Administrators |
Expand Down Expand Up @@ -602,6 +603,7 @@ of the file that are supported are listed here.
| rds | DBInstances | ✅ (DB Name) | ✅ (Creation Time) | ✅ | ✅ |
| rds-parameter-group | RdsParameterGroup | ✅ (Group Name) | ❌ | ❌ | ✅ |
| rds-subnet-group | DBSubnetGroups | ✅ (DB Subnet Group Name) | ❌ | ❌ | ✅ |
| rds-proxy | RDSProxy | ✅ (proxy Name) | ✅ (Creation Time) | ❌ | ✅ |
| s3 | s3 | ✅ (Bucket Name) | ✅ (Creation Time) | ✅ | ✅ |
| s3-ap | s3AccessPoint | ✅ (Access point Name) | ❌ | ❌ | ✅ |
| s3-olap | S3ObjectLambdaAccessPoint | ✅ (Object Lambda Access point Name) | ❌ | ❌ | ✅ |
Expand Down
1 change: 1 addition & 0 deletions aws/resource_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func getRegisteredRegionalResources() []AwsResource {
&resources.DBInstances{},
&resources.DBSubnetGroups{},
&resources.DBClusters{},
&resources.RdsProxy{},
&resources.RdsSnapshot{},
&resources.RdsParameterGroup{},
&resources.RedshiftClusters{},
Expand Down
73 changes: 73 additions & 0 deletions aws/resources/rds_proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package resources

import (
"context"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/rds"
"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 (rdp *RdsProxy) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
var names []*string
err := rdp.Client.DescribeDBProxiesPagesWithContext(
rdp.Context,
&rds.DescribeDBProxiesInput{},
func(page *rds.DescribeDBProxiesOutput, lastPage bool) bool {
for _, proxy := range page.DBProxies {
if configObj.RdsProxy.ShouldInclude(config.ResourceValue{
Name: proxy.DBProxyName,
Time: proxy.CreatedDate,
}) {
names = append(names, proxy.DBProxyName)
}
}

return !lastPage
})
if err != nil {
return nil, errors.WithStackTrace(err)
}

return names, nil
}

func (rdp *RdsProxy) nukeAll(identifiers []*string) error {
if len(identifiers) == 0 {
logging.Debugf("No RDS proxy in region %s", rdp.Region)
return nil
}

logging.Debugf("Deleting all DB Proxies in region %s", rdp.Region)
var deleted []*string

for _, identifier := range identifiers {
logging.Debugf("[RDS Proxy] Deleting %s in region %s", *identifier, rdp.Region)

_, err := rdp.Client.DeleteDBProxyWithContext(
rdp.Context,
&rds.DeleteDBProxyInput{
DBProxyName: identifier,
})
if err != nil {
logging.Errorf("[RDS Proxy] Error deleting RDS Proxy %s: %s", *identifier, err)
} else {
deleted = append(deleted, identifier)
logging.Debugf("[RDS Proxy] Deleted RDS Proxy %s", *identifier)
}

// Record status of this resource
e := report.Entry{
Identifier: aws.StringValue(identifier),
ResourceType: rdp.ResourceName(),
Error: err,
}
report.Record(e)
}

logging.Debugf("[OK] %d RDS DB proxi(s) nuked in %s", len(deleted), rdp.Region)
return nil
}
106 changes: 106 additions & 0 deletions aws/resources/rds_proxy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package resources

import (
"context"
"regexp"
"testing"
"time"

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

type mockedRdsProxy struct {
rdsiface.RDSAPI
DescribeDBProxiesOutput rds.DescribeDBProxiesOutput
DeleteDBProxyOutput rds.DeleteDBProxyOutput
}

func (m mockedRdsProxy) DescribeDBProxiesPagesWithContext(_ aws.Context, _ *rds.DescribeDBProxiesInput, callback func(*rds.DescribeDBProxiesOutput, bool) bool, _ ...request.Option) error {
callback(&m.DescribeDBProxiesOutput, true)
return nil
}

func (m mockedRdsProxy) DeleteDBProxyWithContext(aws.Context, *rds.DeleteDBProxyInput, ...request.Option) (*rds.DeleteDBProxyOutput, error) {
return &m.DeleteDBProxyOutput, nil
}

func TestRdsProxy_GetAll(t *testing.T) {

t.Parallel()

testName1 := "test-name1"
testName2 := "test-name2"
now := time.Now()
snapshot := RdsProxy{
Client: mockedRdsProxy{
DescribeDBProxiesOutput: rds.DescribeDBProxiesOutput{
DBProxies: []*rds.DBProxy{
{
DBProxyName: &testName1,
CreatedDate: &now,
},
{
DBProxyName: &testName2,
CreatedDate: aws.Time(now.Add(1)),
},
},
},
},
}

tests := map[string]struct {
configObj config.ResourceType
expected []string
}{
"emptyFilter": {
configObj: config.ResourceType{},
expected: []string{testName1, testName2},
},
"nameExclusionFilter": {
configObj: config.ResourceType{
ExcludeRule: config.FilterRule{
NamesRegExp: []config.Expression{{
RE: *regexp.MustCompile(testName1),
}}},
},
expected: []string{testName2},
},
"timeAfterExclusionFilter": {
configObj: config.ResourceType{
ExcludeRule: config.FilterRule{
TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
}},
expected: []string{},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
names, err := snapshot.getAll(context.Background(), config.Config{
RdsProxy: tc.configObj,
})
require.NoError(t, err)
require.Equal(t, tc.expected, aws.StringValueSlice(names))
})
}
}

func TestRdsProxy_NukeAll(t *testing.T) {

t.Parallel()

testName := "test-db-proxy"
snapshot := RdsProxy{
Client: mockedRdsProxy{
DeleteDBProxyOutput: rds.DeleteDBProxyOutput{},
},
}

err := snapshot.nukeAll([]*string{&testName})
assert.NoError(t, err)
}
60 changes: 60 additions & 0 deletions aws/resources/rds_proxy_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,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/rds"
"github.com/aws/aws-sdk-go/service/rds/rdsiface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/go-commons/errors"
)

type RdsProxy struct {
BaseAwsResource
Client rdsiface.RDSAPI
Region string
GroupNames []string
}

func (pg *RdsProxy) Init(session *session.Session) {
pg.Client = rds.New(session)
}

func (pg *RdsProxy) ResourceName() string {
return "rds-proxy"
}

// ResourceIdentifiers - The names of the rds parameter group
func (pg *RdsProxy) ResourceIdentifiers() []string {
return pg.GroupNames
}

func (pg *RdsProxy) MaxBatchSize() int {
// Tentative batch size to ensure AWS doesn't throttle
return 49
}

func (pg *RdsProxy) GetAndSetResourceConfig(configObj config.Config) config.ResourceType {
return configObj.RdsProxy
}

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

pg.GroupNames = awsgo.StringValueSlice(identifiers)
return pg.GroupNames, nil
}

// Nuke - nuke 'em all!!!
func (pg *RdsProxy) Nuke(identifiers []string) error {
if err := pg.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
return errors.WithStackTrace(err)
}

return nil
}
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type Config struct {
Redshift ResourceType `yaml:"Redshift"`
RdsSnapshot ResourceType `yaml:"RdsSnapshot"`
RdsParameterGroup ResourceType `yaml:"RdsParameterGroup"`
RdsProxy ResourceType `yaml:"RdsProxy"`
S3 ResourceType `yaml:"s3"`
S3AccessPoint ResourceType `yaml:"S3AccessPoint"`
S3ObjectLambdaAccessPoint ResourceType `yaml:"S3ObjectLambdaAccessPoint"`
Expand Down
1 change: 1 addition & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func emptyConfig() *Config {
ResourceType{FilterRule{}, FilterRule{}, ""},
ResourceType{FilterRule{}, FilterRule{}, ""},
ResourceType{FilterRule{}, FilterRule{}, ""},
ResourceType{FilterRule{}, FilterRule{}, ""},
EC2ResourceType{false, ResourceType{FilterRule{}, FilterRule{}, ""}},
ResourceType{FilterRule{}, FilterRule{}, ""},
ResourceType{FilterRule{}, FilterRule{}, ""},
Expand Down