-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcloudformation.go
187 lines (139 loc) · 5.2 KB
/
cloudformation.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package awsutil
import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
// "github.com/y0ssar1an/q"
"strings"
)
// CloudformationUtil wraps the AWS Cloudformation SDK API and provides additional utilities
type CloudformationUtil struct {
cfnApi cloudformationiface.CloudFormationAPI
ec2Api ec2iface.EC2API
}
// NewCloudformationUtilFromRegion creates a CloudformationUtil from a region
func NewCloudformationUtilFromRegion(region string) (*CloudformationUtil, error) {
session, err := session.NewSession()
if err != nil {
return nil, err
}
cfnApi := newCloudformationAPI(session, region)
ec2Api := newEC2API(session, region)
return NewCloudformationUtil(cfnApi, ec2Api)
}
// NewCloudformationUtil creates a CloudformationUtil given the underlying AWS api implementations (or mocks)
func NewCloudformationUtil(cfnApi cloudformationiface.CloudFormationAPI, ec2Api ec2iface.EC2API) (*CloudformationUtil, error) {
cnfUtil := &CloudformationUtil{
cfnApi: cfnApi,
ec2Api: ec2Api,
}
return cnfUtil, nil
}
// StopEC2Instances stops all EC2 Instances in a cloudformation stack
func (cfnu CloudformationUtil) StopEC2Instances(stackname string) error {
f := func(s *cloudformation.StackResource, c CloudformationUtil) error {
err := cfnu.StopEc2InstanceForStackResource(*s)
return err
}
err := cfnu.startOrStop(stackname, f)
return err
}
// StartEC2Instances starts/restarts all EC2 Instances in a cloudformation stack
func (cfnu CloudformationUtil) StartEC2Instances(stackname string) error {
f := func(s *cloudformation.StackResource, c CloudformationUtil) error {
err := cfnu.StartEc2InstanceForStackResource(*s)
return err
}
err := cfnu.startOrStop(stackname, f)
return err
}
// StopEc2InstanceForStackResource stops the EC2 Instance for the given Stack Resource
func (cfnu CloudformationUtil) StopEc2InstanceForStackResource(stackResource cloudformation.StackResource) error {
if !IsStackResourceEc2Instance(stackResource) {
return fmt.Errorf("Stack Resource [%+v] is not an EC2 instance.", stackResource)
}
stopInstancesInput := ec2.StopInstancesInput{
InstanceIds: []*string{
stackResource.PhysicalResourceId,
},
}
_, err := cfnu.ec2Api.StopInstances(&stopInstancesInput)
if err != nil {
return err
}
return nil
}
// StartEc2InstanceForStackResource starts the EC2 Instance in given Stack Resource
func (cfnu CloudformationUtil) StartEc2InstanceForStackResource(stackResource cloudformation.StackResource) error {
if !IsStackResourceEc2Instance(stackResource) {
return fmt.Errorf("Stack Resource [%+v] is not an EC2 instance.", stackResource)
}
startInstancesInput := ec2.StartInstancesInput{
InstanceIds: []*string{
stackResource.PhysicalResourceId,
},
}
_, err := cfnu.ec2Api.StartInstances(&startInstancesInput)
if err != nil {
return err
}
return nil
}
// InCloudformation checks whether the given instance id is part of a
// Cloudformation. If it is, it returns a nil error and the stack Id and stack name.
// Otherwise it returns a n
func (cfnu CloudformationUtil) InCloudformation(instanceId string) (in bool, stackId string, stackName string, err error) {
params := &cloudformation.DescribeStackResourcesInput{
PhysicalResourceId: StringPointer(instanceId),
}
dsrOutput, err := cfnu.cfnApi.DescribeStackResources(params)
if err != nil {
// if we get a "does not exist" then just absorb the error and don't treat it as an error condition
// since it's expected if the instance ID is not present in any cloudformation stacks
if strings.Contains(err.Error(), "does not exist") {
return false, "", "", nil
}
// it's an unexpected error, let's return it to the caller to bring to attention
return false, "", "", err
}
if len(dsrOutput.StackResources) == 0 {
return false, "", "", nil
}
stackResource := dsrOutput.StackResources[0]
if stackResource.StackId == nil {
return false, "", "", fmt.Errorf("StackResource is missing StackId: %+v", *stackResource)
}
if stackResource.StackName == nil {
return false, "", "", fmt.Errorf("StackResource is missing StackName: %+v", *stackResource)
}
return true, *stackResource.StackId, *stackResource.StackName, nil
}
// IsStackResourceEc2Instance checks whether the StackResource parameter an EC2 instance
func IsStackResourceEc2Instance(stackResource cloudformation.StackResource) bool {
return *stackResource.ResourceType == AWS_EC2_INSTANCE
}
func (cfnu CloudformationUtil) startOrStop(stackname string, f func(*cloudformation.StackResource, CloudformationUtil) error) error {
params := &cloudformation.DescribeStackResourcesInput{
StackName: aws.String(stackname),
}
describeStackResourcesOut, err := cfnu.cfnApi.DescribeStackResources(params)
if err != nil {
return err
}
for _, stackResource := range describeStackResourcesOut.StackResources {
// if it's not an EC2 instance, ignore it
if !IsStackResourceEc2Instance(*stackResource) {
continue
}
// otherwise stop it
err := f(stackResource, cfnu)
if err != nil {
return err
}
}
return nil
}