Skip to content

Commit d9562e2

Browse files
authored
Merge pull request #3 from aidenkeating/add-s3-support
add s3 bucket deletion via an action engine
2 parents 7adf293 + 76f853b commit d9562e2

77 files changed

Lines changed: 52415 additions & 399 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/cli/cleanup.go

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package main
33
import (
44
"fmt"
55
"os"
6+
"time"
7+
8+
"github.com/sirupsen/logrus"
69

710
"github.com/olekukonko/tablewriter"
811

@@ -21,6 +24,7 @@ var cleanupCmd = &cobra.Command{
2124
Short: "delete aws resources for an rhmi cluster",
2225
Args: cobra.MinimumNArgs(1),
2326
Run: func(cmd *cobra.Command, args []string) {
27+
//pre-req checks
2428
clusterId := args[0]
2529
region, err := cmd.Flags().GetString("region")
2630
if err != nil {
@@ -34,7 +38,19 @@ var cleanupCmd = &cobra.Command{
3438
if err != nil {
3539
exitError(fmt.Sprintf("failed to get dry run from flag: %+v", err), exitCodeErrUnknown)
3640
}
37-
41+
watch, err := cmd.Flags().GetBool("watch")
42+
if err != nil {
43+
exitError(fmt.Sprintf("failed to get watch from flag: %+v", err), exitCodeErrUnknown)
44+
}
45+
types, err := cmd.Flags().GetStringSlice("types")
46+
if err != nil {
47+
exitError(fmt.Sprintf("failed to get types from flag: %+v", err), exitCodeErrUnknown)
48+
}
49+
//ensure the output format is supported
50+
if outputFormat != "table" {
51+
exitError(fmt.Sprintf("output format %s not supported, use table", outputFormat), exitCodeErrKnown)
52+
}
53+
//setup aws session
3854
awsKeyID := os.Getenv("AWS_ACCESS_KEY_ID")
3955
if awsKeyID == "" {
4056
exitError("AWS_ACCESS_KEY_ID env var must be defined", exitCodeErrKnown)
@@ -47,19 +63,53 @@ var cleanupCmd = &cobra.Command{
4763
Region: aws.String(region),
4864
Credentials: credentials.NewStaticCredentials(awsKeyID, awsSecretKey, ""),
4965
}))
50-
clusterService := awsclusterservice.NewDefaultClient(awsSession, logger)
51-
report, err := clusterService.DeleteResourcesForCluster(clusterId, map[string]string{}, dryRun)
52-
if err != nil {
53-
exitError(fmt.Sprintf("failed to cleanup resources for cluster, clusterId=%s: %+v", clusterId, err), exitCodeErrUnknown)
66+
clusterService := buildAWSClientFromTypes(awsSession, types, logger)
67+
//this could probably leverage channels
68+
var currentReport *clusterservice.Report
69+
for {
70+
newReport, err := clusterService.DeleteResourcesForCluster(clusterId, map[string]string{}, dryRun)
71+
if err != nil {
72+
exitError(fmt.Sprintf("failed to cleanup resources for cluster, clusterId=%s: %+v", clusterId, err), exitCodeErrUnknown)
73+
}
74+
if currentReport == nil {
75+
currentReport = newReport
76+
}
77+
currentReport.MergeForward(newReport)
78+
printReportTable(currentReport)
79+
if !watch {
80+
break
81+
}
82+
logger.Debug("watch is enabled, waiting 10 seconds before re-invoking")
83+
time.Sleep(time.Second * 10)
5484
}
55-
// we only support table here...
56-
if outputFormat != "table" {
57-
exitError(fmt.Sprintf("output format %s not supported, use table", outputFormat), exitCodeErrKnown)
58-
}
59-
printReportTable(report)
6085
},
6186
}
6287

88+
func buildAWSClientFromTypes(awsSession *session.Session, types []string, logger *logrus.Entry) *awsclusterservice.Client {
89+
if types == nil || len(types) == 0 {
90+
return awsclusterservice.NewDefaultClient(awsSession, logger)
91+
}
92+
client := &awsclusterservice.Client{
93+
Logger: logger,
94+
ResourceManagers: make([]awsclusterservice.ClusterResourceManager, 0),
95+
}
96+
for _, t := range types {
97+
switch t {
98+
case "rds:instance":
99+
client.ResourceManagers = append(client.ResourceManagers, awsclusterservice.NewDefaultRDSInstanceManager(awsSession, logger))
100+
case "rds:snapshot":
101+
client.ResourceManagers = append(client.ResourceManagers, awsclusterservice.NewDefaultRDSSnapshotManager(awsSession, logger))
102+
case "s3":
103+
client.ResourceManagers = append(client.ResourceManagers, awsclusterservice.NewDefaultS3Engine(awsSession, logger))
104+
case "elasticache:replicationgroup":
105+
client.ResourceManagers = append(client.ResourceManagers, awsclusterservice.NewDefaultElastiCacheEngine(awsSession, logger))
106+
default:
107+
logger.Debugf("could not find resource manager for specified type %s", t)
108+
}
109+
}
110+
return client
111+
}
112+
63113
func printReportTable(report *clusterservice.Report) {
64114
table := tablewriter.NewWriter(os.Stdout)
65115
table.SetHeader([]string{"ID", "Name", "Action", "Status"})
@@ -74,4 +124,6 @@ func init() {
74124
cleanupCmd.Flags().StringP("output", "o", "table", "set output format")
75125
cleanupCmd.Flags().StringP("region", "r", "eu-west-1", "region to delete resources in")
76126
cleanupCmd.Flags().BoolP("dry-run", "d", true, "skip performing actions")
127+
cleanupCmd.Flags().BoolP("watch", "w", false, "poll actions being performed indefinitely")
128+
cleanupCmd.Flags().StringSliceP("types", "t", []string{}, "resource types to cleanup")
77129
}

hack/aws-ec2-create-subnet.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
3+
REGION="us-east-1"
4+
VPC_ID=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true --region "$REGION" | jq -r '.Vpcs[0].VpcId')
5+
6+
echo "creating subnet in vpc $VPC_ID in region $REGION"
7+
SUBNET_ID=$(aws ec2 create-subnet --region "$REGION" --cidr-block 172.31.128.0/20 --vpc-id "$VPC_ID" | jq -r '.Subnet.SubnetId')
8+
aws ec2 create-tags --resources "$SUBNET_ID" --tags Key=integreatly.org/clusterID,Value=cluster-service --region us-east-1
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
REGION="us-east-1"
4+
RG_ID=${1:-cluster-service-deleteme}
5+
6+
echo "creating replication group in region $REGION"
7+
aws elasticache create-replication-group --region "$REGION" \
8+
--replication-group-id "$RG_ID" --tags "Key=integreatly.org/clusterID,Value=cluster-service" \
9+
--replication-group-description "$RG_ID" --engine redis \
10+
--cache-node-type cache.t2.micro

hack/aws-rds-create-db.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
DB_ID=${1:-cluster-service-deleteme}
4+
REGION="us-east-1"
5+
PASSWORD=$(cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
6+
7+
echo "creating test rds instance"
8+
aws rds create-db-instance --db-instance-identifier "$DB_ID" --db-instance-class db.t2.micro \
9+
--engine postgres --allocated-storage=20 --region "$REGION" --master-username postgres \
10+
--master-user-password "$PASSWORD" --copy-tags-to-snapshot --tags Key=integreatly.org/clusterID,Value=cluster-service

hack/aws-rds-create-snapshot.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
3+
DB_ID=${1:-cluster-service-deleteme}
4+
SNAPSHOT_ID=${1:-cluster-service-deleteme}
5+
REGION="us-east-1"
6+
SNAPSHOT_SUFFIX=$(cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 4 | head -n 1)
7+
8+
echo "creating test rds instance"
9+
aws rds create-db-snapshot --db-instance-identifier "$DB_ID" --db-snapshot-identifier "$SNAPSHOT_ID-$SNAPSHOT_SUFFIX" --region "$REGION"

hack/aws-s3-create-bucket.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
3+
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
4+
BUCKET_ID=${1:-cluster-service-deleteme}
5+
REGION="us-east-1"
6+
7+
echo "creating test bucket"
8+
aws s3api create-bucket --bucket="$BUCKET_ID" --region="$REGION"
9+
echo "tagging bucket with cluster id tag"
10+
aws s3api put-bucket-tagging --bucket="$BUCKET_ID" --region="$REGION" --tagging 'TagSet=[{Key=integreatly.org/clusterID,Value=cluster-service}]'
11+
echo "syncing test files from $DIR to bucket"
12+
aws s3 sync "$DIR/files" "s3://$BUCKET_ID" --region="$REGION"

hack/files/test-file.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
i am a test file for use with hack scripts

pkg/aws/client.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,30 @@ import (
1212
var _ clusterservice.Client = &Client{}
1313

1414
type Client struct {
15-
actionEngines []ActionEngine
16-
logger *logrus.Entry
15+
ResourceManagers []ClusterResourceManager
16+
Logger *logrus.Entry
1717
}
1818

1919
func NewDefaultClient(awsSession *session.Session, logger *logrus.Entry) *Client {
2020
log := logger.WithField("cluster_service_provider", "aws")
21-
rdsEngine := NewDefaultRDSEngine(awsSession, logger)
21+
rdsEngine := NewDefaultRDSInstanceManager(awsSession, logger)
22+
rdsSnapshotManager := NewDefaultRDSSnapshotManager(awsSession, logger)
23+
s3Engine := NewDefaultS3Engine(awsSession, logger)
2224
elasticacheEngine := NewDefaultElastiCacheEngine(awsSession, logger)
2325
return &Client{
24-
actionEngines: []ActionEngine{rdsEngine, elasticacheEngine},
25-
logger: log,
26+
ResourceManagers: []ClusterResourceManager{rdsEngine, elasticacheEngine, s3Engine, rdsSnapshotManager},
27+
Logger: log,
2628
}
2729
}
2830

2931
//DeleteResourcesForCluster Delete AWS resources based on tags using provided action engines
3032
func (c *Client) DeleteResourcesForCluster(clusterId string, tags map[string]string, dryRun bool) (*clusterservice.Report, error) {
31-
logger := c.logger.WithField("clusterId", clusterId)
33+
logger := c.Logger.WithFields(logrus.Fields{loggingKeyClusterID: clusterId, loggingKeyDryRun: dryRun})
3234
logger.Debugf("deleting resources for cluster")
3335
report := &clusterservice.Report{}
34-
for _, engine := range c.actionEngines {
35-
engineLogger := logger.WithField("engine", engine.GetName())
36-
engineLogger.Debugf("found logger")
36+
for _, engine := range c.ResourceManagers {
37+
engineLogger := logger.WithField(loggingKeyEngine, engine.GetName())
38+
engineLogger.Debugf("found Logger")
3739
reportItems, err := engine.DeleteResourcesForCluster(clusterId, tags, dryRun)
3840
if err != nil {
3941
return nil, errors.WrapLog(err, fmt.Sprintf("failed to run engine %s", engine.GetName()), engineLogger)

pkg/aws/client_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func TestClient_DeleteResourcesForCluster(t *testing.T) {
1818
}
1919

2020
type fields struct {
21-
actionEngines func() []ActionEngine
21+
actionEngines func() []ClusterResourceManager
2222
logger *logrus.Entry
2323
}
2424
type args struct {
@@ -36,7 +36,7 @@ func TestClient_DeleteResourcesForCluster(t *testing.T) {
3636
{
3737
name: "error when an engine fails",
3838
fields: fields{
39-
actionEngines: func() []ActionEngine {
39+
actionEngines: func() []ClusterResourceManager {
4040
fakeEngine, err := fakeActionEngine(func(e *ActionEngineMock) error {
4141
e.DeleteResourcesForClusterFunc = func(clusterId string, tags map[string]string, dryRun bool) (items []*clusterservice.ReportItem, e error) {
4242
return nil, errors.New("")
@@ -46,7 +46,7 @@ func TestClient_DeleteResourcesForCluster(t *testing.T) {
4646
if err != nil {
4747
t.Fatal(err)
4848
}
49-
return []ActionEngine{fakeEngine}
49+
return []ClusterResourceManager{fakeEngine}
5050
},
5151
logger: fakeLogger,
5252
},
@@ -60,7 +60,7 @@ func TestClient_DeleteResourcesForCluster(t *testing.T) {
6060
{
6161
name: "multiple engine report items are appended",
6262
fields: fields{
63-
actionEngines: func() []ActionEngine {
63+
actionEngines: func() []ClusterResourceManager {
6464
fakeEngine, err := fakeActionEngine(func(e *ActionEngineMock) error {
6565
return nil
6666
})
@@ -78,7 +78,7 @@ func TestClient_DeleteResourcesForCluster(t *testing.T) {
7878
if err != nil {
7979
t.Fatal(err)
8080
}
81-
return []ActionEngine{fakeEngine, fakeDryRunEngine}
81+
return []ClusterResourceManager{fakeEngine, fakeDryRunEngine}
8282
},
8383
logger: fakeLogger,
8484
},
@@ -98,8 +98,8 @@ func TestClient_DeleteResourcesForCluster(t *testing.T) {
9898
for _, tt := range tests {
9999
t.Run(tt.name, func(t *testing.T) {
100100
c := &Client{
101-
actionEngines: tt.fields.actionEngines(),
102-
logger: tt.fields.logger,
101+
ResourceManagers: tt.fields.actionEngines(),
102+
Logger: tt.fields.logger,
103103
}
104104
got, err := c.DeleteResourcesForCluster(tt.args.clusterId, tt.args.tags, tt.args.dryRun)
105105
if err != nil && err.Error() != tt.wantErr {

pkg/aws/elasticache.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package aws
22

33
import (
4+
"strings"
5+
46
"github.com/aws/aws-sdk-go/aws"
57
"github.com/aws/aws-sdk-go/aws/session"
68
"github.com/aws/aws-sdk-go/service/elasticache"
@@ -10,10 +12,9 @@ import (
1012
"github.com/integr8ly/cluster-service/pkg/clusterservice"
1113
"github.com/integr8ly/cluster-service/pkg/errors"
1214
"github.com/sirupsen/logrus"
13-
"strings"
1415
)
1516

16-
var _ ActionEngine = &ElasticacheEngine{}
17+
var _ ClusterResourceManager = &ElasticacheEngine{}
1718

1819
type ElasticacheEngine struct {
1920
elasticacheClient elasticacheiface.ElastiCacheAPI

0 commit comments

Comments
 (0)