This repository has been archived by the owner on Sep 30, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 295
/
cfnstack.go
127 lines (112 loc) · 4.45 KB
/
cfnstack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package cfnstack
import (
"fmt"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/kubernetes-incubator/kube-aws/logger"
)
var CFN_TEMPLATE_SIZE_LIMIT = 51200
type CreationService interface {
CreateStack(*cloudformation.CreateStackInput) (*cloudformation.CreateStackOutput, error)
}
type UpdateService interface {
UpdateStack(input *cloudformation.UpdateStackInput) (*cloudformation.UpdateStackOutput, error)
}
type CRUDService interface {
CreateStack(*cloudformation.CreateStackInput) (*cloudformation.CreateStackOutput, error)
UpdateStack(input *cloudformation.UpdateStackInput) (*cloudformation.UpdateStackOutput, error)
DescribeStacks(input *cloudformation.DescribeStacksInput) (*cloudformation.DescribeStacksOutput, error)
DescribeStackEvents(input *cloudformation.DescribeStackEventsInput) (*cloudformation.DescribeStackEventsOutput, error)
EstimateTemplateCost(input *cloudformation.EstimateTemplateCostInput) (*cloudformation.EstimateTemplateCostOutput, error)
}
type S3ObjectPutterService interface {
PutObject(input *s3.PutObjectInput) (*s3.PutObjectOutput, error)
}
// Used for querying existance of stacks and nested stacks.
type CFInterrogator interface {
ListStackResources(input *cloudformation.ListStackResourcesInput) (*cloudformation.ListStackResourcesOutput, error)
DescribeStacks(input *cloudformation.DescribeStacksInput) (*cloudformation.DescribeStacksOutput, error)
}
func StackEventErrMsgs(events []*cloudformation.StackEvent) []string {
var errMsgs []string
for _, event := range events {
if aws.StringValue(event.ResourceStatus) == cloudformation.ResourceStatusCreateFailed {
// Only show actual failures, not cancelled dependent resources.
if aws.StringValue(event.ResourceStatusReason) != "Resource creation cancelled" {
errMsgs = append(errMsgs,
strings.TrimSpace(
strings.Join([]string{
aws.StringValue(event.ResourceStatus),
aws.StringValue(event.ResourceType),
aws.StringValue(event.LogicalResourceId),
aws.StringValue(event.ResourceStatusReason),
}, " ")))
}
}
}
return errMsgs
}
func NestedStackExists(cf CFInterrogator, parentStackName, stackName string) (bool, error) {
logger.Debugf("testing whether nested stack '%s' is present in parent stack '%s'", stackName, parentStackName)
parentExists, err := StackExists(cf, parentStackName)
if err != nil {
return false, err
}
if !parentExists {
logger.Debugf("parent stack '%s' does not exist, so nested stack can not exist either", parentStackName)
return false, nil
}
req := &cloudformation.ListStackResourcesInput{StackName: &parentStackName}
logger.Debugf("calling AWS cloudformation ListStackResources for stack %s ->", parentStackName)
out, err := cf.ListStackResources(req)
if err != nil {
return false, fmt.Errorf("Could not read cf stack %s: %v", parentStackName, err)
}
if out == nil {
return false, nil
}
logger.Debugf("<- AWS responded with %d stack resources", len(out.StackResourceSummaries))
for _, resource := range out.StackResourceSummaries {
if *resource.LogicalResourceId == stackName {
logger.Debugf("match! resource id '%s' exists", stackName)
return true, nil
}
}
logger.Debugf("no match! resource id '%s' does not exist", stackName)
return false, nil
}
func StackExists(cf CFInterrogator, stackName string) (bool, error) {
logger.Debugf("testing whether cf stack %s exists", stackName)
req := &cloudformation.DescribeStacksInput{}
req.StackName = &stackName
logger.Debug("calling AWS cloudformation DescribeStacks ->")
stacks, err := cf.DescribeStacks(req)
if err != nil {
if strings.HasPrefix(err.Error(), "ValidationError: Stack with id "+stackName+" does not exist") {
return false, nil
}
return false, fmt.Errorf("could not list cloudformation stacks: %v", err)
}
if stacks == nil {
logger.Debugf("<- AWS Responded with empty stacks object")
return false, nil
}
if stacks.Stacks != nil {
logger.Debugf("<- AWS Responded with %d stacks", len(stacks.Stacks))
for _, summary := range stacks.Stacks {
if *summary.StackName == stackName {
logger.Debugf("found matching stack %s: %+v", *summary.StackName, *summary)
if summary.DeletionTime == nil {
logger.Debugf("stack is active - matched!")
return true, nil
} else {
logger.Debugf("stack is not active, ignoring")
}
}
}
}
logger.Debugf("found no active stacks with id %s", stackName)
return false, nil
}