Skip to content

Commit fa785ac

Browse files
committed
refactor purge to ensure all stacks are deleted for a namespace
1 parent 40ce29b commit fa785ac

File tree

4 files changed

+120
-117
lines changed

4 files changed

+120
-117
lines changed

cli/app.go

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func NewApp() *cli.App {
6868
return nil
6969
}
7070
}
71+
context.Config.DryRun = c.Bool("dryrun")
7172

7273
// Allow overriding the `DisableIAM` in config via `--disable-iam` or `-I`
7374
if c.Bool("disable-iam") {

common/types.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Context struct {
3333

3434
// Config defines the structure of the yml file for the mu config
3535
type Config struct {
36+
DryRun bool `yaml:"-"`
3637
Namespace string `yaml:"namespace,omitempty" validate:"validateAlphaNumericDash"`
3738
Environments []Environment `yaml:"environments,omitempty"`
3839
Service Service `yaml:"service,omitempty"`
@@ -512,8 +513,7 @@ func StringValue(v *string) string {
512513
return ""
513514
}
514515

515-
// StringRef returns the string pointer to the string passed in or
516-
// "" if the pointer is nil.
516+
// StringRef returns the string pointer to the string passed in
517517
func StringRef(v string) *string {
518518
return &v
519519
}

provider/aws/cloudformation.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ func (cfnMgr *cloudformationStackManager) ListStacks(stackType common.StackType,
526526

527527
var stacks []*common.Stack
528528

529-
log.Debugf("Searching for stacks of type '%s'", stackType)
529+
log.Debugf("Searching for stacks of type '%s' in namespace '%s'", stackType, namespace)
530530

531531
err := cfnAPI.DescribeStacksPages(params,
532532
func(page *cloudformation.DescribeStacksOutput, lastPage bool) bool {
@@ -641,7 +641,11 @@ func (cfnMgr *cloudformationStackManager) DeleteStack(stackName string) error {
641641
log.Debugf("Deleting stack named '%s'", stackName)
642642

643643
_, err := cfnAPI.DeleteStack(params)
644-
return err
644+
if err != nil {
645+
return err
646+
}
647+
cfnMgr.logInfo(" Deleted stack '%s'", stackName)
648+
return nil
645649
}
646650

647651
func writeTemplateAndConfig(cfnDirectory string, stackName string, templateBodyBytes *bytes.Buffer, parameters map[string]string) error {

workflows/purge_confirm.go

+111-113
Original file line numberDiff line numberDiff line change
@@ -6,150 +6,148 @@ import (
66
"github.com/stelligent/mu/common"
77
)
88

9-
type purgeWorkflow struct{}
9+
type purgeWorkflow struct {
10+
context *common.Context
11+
}
1012

1113
// NewPurge create a new workflow for purging mu resources
1214
func NewPurge(ctx *common.Context) Executor {
1315
workflow := new(purgeWorkflow)
16+
workflow.context = ctx
17+
18+
iamCommonStackName := fmt.Sprintf("%s-iam-common", ctx.Config.Namespace)
1419

1520
return newPipelineExecutor(
16-
workflow.terminatePipelines(ctx),
17-
workflow.terminateEnvironments(ctx),
18-
workflow.terminateRepos(ctx),
19-
workflow.terminateApps(ctx),
20-
workflow.terminateBuckets(ctx),
21-
workflow.terminateIAM(ctx),
21+
ctx.RolesetManager.UpsertCommonRoleset,
22+
workflow.newStackStream(common.StackTypePipeline).foreach(workflow.terminatePipeline),
23+
workflow.newStackStream(common.StackTypeEnv).foreach(workflow.terminateEnvironment),
24+
workflow.newStackStream(common.StackTypeSchedule).foreach(workflow.deleteStack),
25+
workflow.newStackStream(common.StackTypeService).foreach(workflow.deleteStack),
26+
workflow.newStackStream(common.StackTypeDatabase).foreach(workflow.deleteStack),
27+
workflow.newStackStream(common.StackTypeLoadBalancer).foreach(workflow.deleteStack),
28+
workflow.newStackStream(common.StackTypeEnv).foreach(workflow.deleteStack),
29+
workflow.newStackStream(common.StackTypeVpc).foreach(workflow.deleteStack),
30+
workflow.newStackStream(common.StackTypeTarget).foreach(workflow.deleteStack),
31+
workflow.newStackStream(common.StackTypeRepo).foreach(workflow.cleanupRepo, workflow.deleteStack),
32+
workflow.newStackStream(common.StackTypeApp).foreach(workflow.deleteStack),
33+
workflow.newStackStream(common.StackTypeBucket).foreach(workflow.cleanupBucket, workflow.deleteStack),
34+
workflow.newStackStream(common.StackTypeIam).filter(excludeStackName(iamCommonStackName)).foreach(workflow.deleteStack),
35+
workflow.terminateCommonRoleset(),
2236
)
2337
}
2438

25-
func (workflow *purgeWorkflow) terminateIAM(ctx *common.Context) Executor {
39+
func (workflow *purgeWorkflow) terminatePipeline(stack *common.Stack) Executor {
40+
return NewPipelineTerminator(workflow.context, stack.Tags["service"])
41+
}
42+
func (workflow *purgeWorkflow) terminateEnvironment(stack *common.Stack) Executor {
43+
return NewEnvironmentsTerminator(workflow.context, []string{stack.Tags["environment"]})
44+
}
45+
func (workflow *purgeWorkflow) terminateCommonRoleset() Executor {
46+
return func() error {
47+
workflow.context.StackManager.AllowDataLoss(true)
48+
return workflow.context.RolesetManager.DeleteCommonRoleset()
49+
}
50+
}
51+
func excludeStackName(stackName string) stackFilter {
52+
return func(stack *common.Stack) bool {
53+
return stack.Name != stackName
54+
}
55+
}
56+
57+
func (workflow *purgeWorkflow) deleteStack(stack *common.Stack) Executor {
58+
stackName := stack.Name
2659
return func() error {
27-
namespace := ctx.Config.Namespace
28-
stacks, err := ctx.StackManager.ListStacks(common.StackTypeIam, namespace)
60+
err := workflow.context.StackManager.DeleteStack(stackName)
2961
if err != nil {
3062
return err
3163
}
32-
executors := make([]Executor, 0)
33-
for _, stack := range stacks {
34-
stackName := stack.Name
35-
if stackName != fmt.Sprintf("%s-iam-common", namespace) {
36-
executors = append(executors, func() error {
37-
err = ctx.StackManager.DeleteStack(stackName)
38-
if err != nil {
39-
return err
40-
}
41-
ctx.StackManager.AwaitFinalStatus(stackName)
42-
return nil
43-
})
44-
}
45-
}
46-
iamExecutors := newParallelExecutor(executors...)
47-
err = iamExecutors()
48-
if err != nil {
49-
return err
64+
status := workflow.context.StackManager.AwaitFinalStatus(stackName)
65+
if status != nil && !workflow.context.Config.DryRun {
66+
return fmt.Errorf("Unable to delete stack '%s'", stackName)
5067
}
51-
52-
ctx.StackManager.AllowDataLoss(true)
53-
return ctx.RolesetManager.DeleteCommonRoleset()
68+
return nil
5469
}
5570
}
5671

57-
func (workflow *purgeWorkflow) terminatePipelines(ctx *common.Context) Executor {
58-
namespace := ctx.Config.Namespace
59-
stacks, err := ctx.StackManager.ListStacks(common.StackTypePipeline, namespace)
60-
if err != nil {
61-
return newErrorExecutor(err)
62-
}
63-
executors := make([]Executor, 0)
64-
for _, stack := range stacks {
65-
executors = append(executors, NewPipelineTerminator(ctx, stack.Tags["service"]))
72+
func (workflow *purgeWorkflow) cleanupBucket(stack *common.Stack) Executor {
73+
bucketName := stack.Outputs["Bucket"]
74+
return func() error {
75+
return workflow.context.ArtifactManager.EmptyBucket(bucketName)
6676
}
67-
return newParallelExecutor(executors...)
6877
}
6978

70-
func (workflow *purgeWorkflow) terminateEnvironments(ctx *common.Context) Executor {
71-
stackLister := ctx.StackManager
72-
namespace := ctx.Config.Namespace
73-
stacks, err := stackLister.ListStacks(common.StackTypeEnv, namespace)
74-
if err != nil {
75-
return newErrorExecutor(err)
76-
}
77-
envNames := make([]string, 0)
78-
for _, stack := range stacks {
79-
envNames = append(envNames, stack.Tags["environment"])
79+
func (workflow *purgeWorkflow) cleanupRepo(stack *common.Stack) Executor {
80+
repoName := stack.Parameters["RepoName"]
81+
return func() error {
82+
return workflow.context.ClusterManager.DeleteRepository(repoName)
8083
}
81-
return NewEnvironmentsTerminator(ctx, envNames)
8284
}
8385

84-
func (workflow *purgeWorkflow) terminateRepos(ctx *common.Context) Executor {
85-
namespace := ctx.Config.Namespace
86-
stacks, err := ctx.StackManager.ListStacks(common.StackTypeRepo, namespace)
87-
if err != nil {
88-
return newErrorExecutor(err)
89-
}
90-
executors := make([]Executor, 0)
91-
for _, stack := range stacks {
92-
stackName := stack.Name
93-
repoName := stack.Parameters["RepoName"]
94-
executors = append(executors, func() error {
95-
err := ctx.ClusterManager.DeleteRepository(repoName)
96-
if err != nil {
97-
return err
98-
}
99-
err = ctx.StackManager.DeleteStack(stackName)
100-
if err != nil {
101-
return err
102-
}
103-
ctx.StackManager.AwaitFinalStatus(stackName)
104-
return nil
105-
})
86+
type stackStream struct {
87+
namespace string
88+
stackType common.StackType
89+
stackLister common.StackLister
90+
filters []stackFilter
91+
}
92+
93+
func (workflow *purgeWorkflow) newStackStream(stackType common.StackType) *stackStream {
94+
return &stackStream{
95+
namespace: workflow.context.Config.Namespace,
96+
stackType: stackType,
97+
stackLister: workflow.context.StackManager,
98+
filters: make([]stackFilter, 0),
10699
}
107-
return newParallelExecutor(executors...)
108100
}
109101

110-
func (workflow *purgeWorkflow) terminateApps(ctx *common.Context) Executor {
111-
namespace := ctx.Config.Namespace
112-
stacks, err := ctx.StackManager.ListStacks(common.StackTypeApp, namespace)
113-
if err != nil {
114-
return newErrorExecutor(err)
102+
type stackExecutor func(stack *common.Stack) Executor
103+
type stackFilter func(stack *common.Stack) bool
104+
105+
func (stream *stackStream) filter(filter stackFilter) *stackStream {
106+
stream.filters = append(stream.filters, filter)
107+
return stream
108+
}
109+
110+
// Check if the filters for the stream to see if the stack should be included
111+
func (stream *stackStream) included(stack *common.Stack) bool {
112+
for _, filter := range stream.filters {
113+
if !filter(stack) {
114+
return false
115+
}
115116
}
117+
return true
118+
}
119+
120+
// Create a pipeline executor consiting of the resolved stackExecutors for a given stack
121+
func applyStackExecutors(stack *common.Stack, stackExecutors ...stackExecutor) Executor {
116122
executors := make([]Executor, 0)
117-
for _, stack := range stacks {
118-
stackName := stack.Name
119-
executors = append(executors, func() error {
120-
err = ctx.StackManager.DeleteStack(stackName)
121-
if err != nil {
122-
return err
123-
}
124-
ctx.StackManager.AwaitFinalStatus(stackName)
125-
return nil
126-
})
123+
for _, stackExecutor := range stackExecutors {
124+
executor := stackExecutor(stack)
125+
if executor != nil {
126+
executors = append(executors, executor)
127+
}
127128
}
128-
return newParallelExecutor(executors...)
129+
return newPipelineExecutor(executors...)
129130
}
130131

131-
func (workflow *purgeWorkflow) terminateBuckets(ctx *common.Context) Executor {
132-
namespace := ctx.Config.Namespace
133-
stacks, err := ctx.StackManager.ListStacks(common.StackTypeBucket, namespace)
134-
if err != nil {
135-
return newErrorExecutor(err)
136-
}
137-
executors := make([]Executor, 0)
138-
for _, stack := range stacks {
139-
stackName := stack.Name
140-
bucketName := stack.Outputs["Bucket"]
141-
executors = append(executors, func() error {
142-
err := ctx.ArtifactManager.EmptyBucket(bucketName)
143-
if err != nil {
144-
return err
145-
}
146-
err = ctx.StackManager.DeleteStack(stackName)
147-
if err != nil {
148-
return err
132+
// Create an executor that can iterate over all stacks and run executors against the stacks
133+
func (stream *stackStream) foreach(stackExecutors ...stackExecutor) Executor {
134+
return func() error {
135+
log.Noticef("Purging '%s' stacks in namespace '%s'", stream.stackType, stream.namespace)
136+
stacks, err := stream.stackLister.ListStacks(stream.stackType, stream.namespace)
137+
if err != nil {
138+
return err
139+
}
140+
executors := make([]Executor, 0)
141+
for _, stack := range stacks {
142+
if !stream.included(stack) {
143+
log.Debugf("skipping %s", stack.Name)
144+
continue
149145
}
150-
ctx.StackManager.AwaitFinalStatus(stackName)
151-
return nil
152-
})
146+
log.Debugf("adding %s", stack.Name)
147+
148+
executors = append(executors, applyStackExecutors(stack, stackExecutors...))
149+
}
150+
executor := newParallelExecutor(executors...)
151+
return executor()
153152
}
154-
return newParallelExecutor(executors...)
155153
}

0 commit comments

Comments
 (0)