Skip to content

Commit

Permalink
Merge pull request #6 from HubertStefanski/INTLY-5006-refactoring-and…
Browse files Browse the repository at this point in the history
…-tests

Intly 5006 refactoring and tests
  • Loading branch information
aidenkeating authored Mar 18, 2020
2 parents 7d5c3b3 + 4584b89 commit 004c059
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 69 deletions.
4 changes: 1 addition & 3 deletions cmd/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package main

import (
"fmt"
"os"

"github.com/sirupsen/logrus"

"github.com/spf13/cobra"
"os"
)

const (
Expand Down
92 changes: 31 additions & 61 deletions pkg/aws/manager_elasticache_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package aws
import (
"strings"

"github.com/aws/aws-sdk-go/aws/awserr"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/elasticache"
Expand All @@ -14,6 +16,10 @@ import (
"github.com/sirupsen/logrus"
)

const (
resourceTypeElasticacheSnapshot = "elasticache:snapshot"
)

var _ ClusterResourceManager = &ElasticacheSnapshotManager{}

type ElasticacheSnapshotManager struct {
Expand All @@ -38,89 +44,53 @@ func (r *ElasticacheSnapshotManager) DeleteResourcesForCluster(clusterId string,
logger := r.logger.WithFields(logrus.Fields{"clusterId": clusterId, "dryRun": dryRun})
logger.Debug("deleting resources for cluster")

var reportItems []*clusterservice.ReportItem
//collection of clusterID's for respective snapshots
var snapshotsToDeleteCacheClusterId []string
var snapshotsToDelete []*basicResource

resourceInput := &resourcegroupstaggingapi.GetResourcesInput{
ResourceTypeFilters: aws.StringSlice([]string{"elasticache:cluster"}),
ResourceTypeFilters: aws.StringSlice([]string{resourceTypeElasticacheSnapshot}),
TagFilters: convertClusterTagsToAWSTagFilter(clusterId, tags),
}
resourceOutput, err := r.taggingClient.GetResources(resourceInput)
if err != nil {
return nil, errors.WrapLog(err, "failed to describe cache clusters", logger)
return nil, errors.WrapLog(err, "failed to get tagged snapshots", logger)
}
//convert response to standardised resource
for _, resourceTagMapping := range resourceOutput.ResourceTagMappingList {
arn := aws.StringValue(resourceTagMapping.ResourceARN)
arnSplit := strings.Split(arn, ":")
cacheClusterId := arnSplit[len(arnSplit)-1]
cacheClusterInput := &elasticache.DescribeCacheClustersInput{
CacheClusterId: aws.String(cacheClusterId),
}
cacheClusterOutput, err := r.elasticacheClient.DescribeCacheClusters(cacheClusterInput)
if err != nil {
return nil, errors.WrapLog(err, "cannot get cacheCluster output", logger)
}
for _, cacheCluster := range cacheClusterOutput.CacheClusters {
rgLogger := logger.WithField("snapshotCacheClusterID", cacheCluster.CacheClusterId)
if contains(snapshotsToDeleteCacheClusterId, *cacheCluster.CacheClusterId) {
rgLogger.Debugf("cacheCluster already exists in deletion list (%s=%s)", *cacheCluster.CacheClusterId, clusterId)
break
}
snapshotsToDeleteCacheClusterId = append(snapshotsToDeleteCacheClusterId, *cacheCluster.CacheClusterId)
}
snapshotARN := aws.StringValue(resourceTagMapping.ResourceARN)
snapshotARNElements := strings.Split(snapshotARN, ":")
snapshotsToDelete = append(snapshotsToDelete, &basicResource{
Name: snapshotARNElements[len(snapshotARNElements)-1],
ARN: snapshotARN,
})
}
var reportItems []*clusterservice.ReportItem
for _, snapshot := range snapshotsToDelete {
snapshotLogger := r.logger.WithField(loggingKeySnapshot, snapshot.Name)
snapshotLogger.Debug("handling deletion for snapshot")

logger.Debugf("filtering complete, %d cacheclusters matched", len(snapshotsToDeleteCacheClusterId))
for _, cacheClusterId := range snapshotsToDeleteCacheClusterId {
//delete each cacheCluster in the list
ssLogger := logger.WithField("cacheClusterID", aws.String(cacheClusterId))
ssLogger.Debugf("building report for database")
reportItem := &clusterservice.ReportItem{
ID: cacheClusterId,
Name: "elasticache snapshot",
ID: snapshot.ARN,
Name: snapshot.Name,
Action: clusterservice.ActionDelete,
ActionStatus: clusterservice.ActionStatusInProgress,
}
reportItems = append(reportItems, reportItem)
if dryRun {
ssLogger.Debug("dry run enabled, skipping deletion step")
snapshotLogger.Debug("dry run is enabled, skipping")
reportItem.ActionStatus = clusterservice.ActionStatusDryRun
continue
}
ssLogger.Debug("performing deletion of snapshotOutput cachecluster")
snapshotInput := &elasticache.DescribeSnapshotsInput{
CacheClusterId: aws.String(cacheClusterId),
}
snapshotOutput, err := r.elasticacheClient.DescribeSnapshots(snapshotInput)
if err != nil {
return nil, errors.WrapLog(err, "cannot Describe snapshotInput", logger)
}
if len(snapshotOutput.Snapshots) > 0 && aws.StringValue(snapshotOutput.Snapshots[0].SnapshotStatus) == statusDeleting {
ssLogger.Debugf("deletion of snapshots already in progress")
reportItem.ActionStatus = clusterservice.ActionStatusInProgress
continue
}
var snapshotNamesToDelete []string
for _, snapshot := range snapshotOutput.Snapshots {
ssLogger := logger.WithField("snapshotName", snapshot.SnapshotName)
if contains(snapshotNamesToDelete, *snapshot.SnapshotName) {
ssLogger.Debugf("snapshot already exists in deletion list (%s=%s)", *snapshot.SnapshotName, clusterId)
break
}
snapshotNamesToDelete = append(snapshotNamesToDelete, *snapshot.SnapshotName)
deleteSnapshotInput := &elasticache.DeleteSnapshotInput{
SnapshotName: aws.String(snapshot.Name),
}
for _, snapshotName := range snapshotNamesToDelete {
deleteSnapshotInput := &elasticache.DeleteSnapshotInput{
SnapshotName: aws.String(snapshotName),
}
if _, err := r.elasticacheClient.DeleteSnapshot(deleteSnapshotInput); err != nil {
return nil, errors.WrapLog(err, "failed to delete snapshot", logger)
if _, err := r.elasticacheClient.DeleteSnapshot(deleteSnapshotInput); err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == elasticache.ErrCodeInvalidSnapshotStateFault {
snapshotLogger.Debug("snapshot is in a deleting state, ignoring error")
continue
}
return nil, errors.WrapLog(err, "failed to delete snapshot", r.logger)
}
}
if reportItems != nil {
return reportItems, nil
}
return nil, nil
return reportItems, nil
}
209 changes: 209 additions & 0 deletions pkg/aws/manager_elasticache_snapshot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package aws

import (
"github.com/aws/aws-sdk-go/service/elasticache"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
"github.com/integr8ly/cluster-service/pkg/clusterservice"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"

//"reflect"
"testing"
)

func TestElasticacheSnapshotManager_DeleteResourcesForCluster(t *testing.T) {
fakeClusterId := fakeClusterID
fakeLogger, err := fakeLogger(func(l *logrus.Entry) error {
return nil
})
if err != nil {
t.Fatal(err)
}

type fields struct {
elasticacheClient func() *elasticacheClientMock
taggingClient func() *taggingClientMock
logger *logrus.Entry
}
type args struct {
clusterId string
tags map[string]string
dryRun bool
}
tests := []struct {
name string
fields fields
args args
want []*clusterservice.ReportItem
wantFn func(mock *elasticacheClientMock) error
wantErr string
}{
{
name: "error when delete snapshot fails",
fields: fields{
elasticacheClient: func() *elasticacheClientMock {
fakeClient, err := fakeElasticacheClient(func(c *elasticacheClientMock) error {
c.DeleteSnapshotFunc = func(in1 *elasticache.DeleteSnapshotInput) (output *elasticache.DeleteSnapshotOutput, e error) {
return nil, errors.New("")
}
return nil
})
if err != nil {
t.Fatal(err)
}
return fakeClient

},
taggingClient: func() *taggingClientMock {
fakeTaggingClient, err := fakeTaggingClient(func(c *taggingClientMock) error {
return nil
})
if err != nil {
t.Fatal(err)
}
return fakeTaggingClient
},
logger: fakeLogger,
},
args: args{
clusterId: fakeClusterId,
dryRun: false,
},
wantErr: "failed to delete snapshot: ",
}, {
name: "pass when no report is returned if no snapshots deleted ",
fields: fields{
elasticacheClient: func() *elasticacheClientMock {
fakeClient, err := fakeElasticacheClient(func(c *elasticacheClientMock) error {
return nil
})
if err != nil {
t.Fatal(err)
}
return fakeClient
},
taggingClient: func() *taggingClientMock {
fakeTaggingClient, err := fakeTaggingClient(func(c *taggingClientMock) error {
c.GetResourcesFunc = func(in1 *resourcegroupstaggingapi.GetResourcesInput) (output *resourcegroupstaggingapi.GetResourcesOutput, err error) {
return nil, errors.New("")
}
return nil
})
if err != nil {
t.Fatal(err)
}
return fakeTaggingClient
},
logger: fakeLogger,
},
args: args{
clusterId: fakeClusterId,
dryRun: true,
},
want: []*clusterservice.ReportItem{},
wantErr: "",
}, {
name: "pass when snapshots deleted and reportItem has status set to deleting",
fields: fields{
elasticacheClient: func() *elasticacheClientMock {
fakeClient, err := fakeElasticacheClient(func(c *elasticacheClientMock) error {
return nil
})
if err != nil {
t.Fatal(err)
}
return fakeClient
},
taggingClient: func() *taggingClientMock {
fakeTaggingClient, err := fakeTaggingClient(func(c *taggingClientMock) error {
return nil
})
if err != nil {
t.Fatal(err)
}
return fakeTaggingClient
},
logger: fakeLogger,
},
args: args{
clusterId: fakeClusterId,
dryRun: false,
},
want: []*clusterservice.ReportItem{
fakeReportItemElasticacheSnapshotDeleting(),
},
wantErr: "",
},
{
name: "pass when no snapshots are deleted if dry run is true",
fields: fields{
elasticacheClient: func() *elasticacheClientMock {
fakeClient, err := fakeElasticacheClient(func(c *elasticacheClientMock) error {
c.DescribeSnapshotsFunc = func(in1 *elasticache.DescribeSnapshotsInput) (output *elasticache.DescribeSnapshotsOutput, err error) {
return &elasticache.DescribeSnapshotsOutput{
Snapshots: []*elasticache.Snapshot{
fakeElasticacheSnapshot(),
},
}, nil
}
return nil
})
if err != nil {
t.Fatal(err)
}
return fakeClient
},
taggingClient: func() *taggingClientMock {
fakeTaggingClient, err := fakeTaggingClient(func(c *taggingClientMock) error {
return nil
})
if err != nil {
t.Fatal(err)
}
return fakeTaggingClient
},
logger: fakeLogger,
},
args: args{
clusterId: fakeClusterId,
dryRun: true,
},
want: []*clusterservice.ReportItem{
fakeReportItemElasticacheSnapshotDryRun(),
},
wantFn: func(mock *elasticacheClientMock) error {
if len(mock.DeleteSnapshotCalls()) != 0 {
return errors.New("delete snapshot call count should be 0 as dry run is true")
}
return nil
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient := tt.fields.elasticacheClient()
r := &ElasticacheSnapshotManager{
elasticacheClient: fakeClient,
taggingClient: tt.fields.taggingClient(),
logger: tt.fields.logger,
}
got, err := r.DeleteResourcesForCluster(tt.args.clusterId, nil, tt.args.dryRun)
if tt.wantErr != "" && err.Error() != tt.wantErr {
t.Errorf("DeleteResourcesForCluster() error = %v, wantErr %v", err, tt.wantErr)
return

}

if !equalReportItems(got, tt.want) {
t.Errorf("DeleteResourcesForCluster() got = %v, want %v", got, tt.want)
}

if tt.wantFn != nil {
if err := tt.wantFn(fakeClient); err != nil {
t.Errorf("DeleteResourcesForCluster() err = %v", err)
}
}
})
}
}
2 changes: 2 additions & 0 deletions pkg/aws/manager_elasticache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,13 @@ func TestElasticacheEngine_DeleteResourcesForCluster(t *testing.T) {
if tt.wantErr != "" && err.Error() != tt.wantErr {
t.Errorf("DeleteResourcesForCluster() error = %v, wantErr %v", err, tt.wantErr)
return

}

if !equalReportItems(got, tt.want) {
t.Errorf("DeleteResourcesForCluster() got = %v, want %v", got, tt.want)
}

if tt.wantFn != nil {
if err := tt.wantFn(fakeClient); err != nil {
t.Errorf("DeleteResourcesForCluster() err = %v", err)
Expand Down
Loading

0 comments on commit 004c059

Please sign in to comment.