cloudformation-tool 1.2.2 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +96 -0
- data/lib/cloud_formation_tool/cli/main.rb +1 -1
- data/lib/cloud_formation_tool/cloud_formation.rb +6 -0
- data/lib/cloud_formation_tool/cloud_formation/nested_stack.rb +25 -0
- data/lib/cloud_formation_tool/cloud_formation/stack.rb +50 -5
- data/lib/cloud_formation_tool/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6667d6553e4525f298bb9b2fd517a7045d50f8ed88273fe7bf43ca1e5b588ed
|
4
|
+
data.tar.gz: f8b8ebc3884b92222e8fe67e60f6fdea325cc60b0130eecb36b45edb83e7f209
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1db7a23fd37bb5ac7f5ab7437b5a62b8fe3d57e0c26a9ac48389641f08042c683f6ecf8eea482a88e08bf68e9ed7fc1c51415db6da54feb1f434a6ab41fb0d42
|
7
|
+
data.tar.gz: ade2265d6bf3652212ecf11125028239494e4ce0318345a88366a2f53440e7b3efde861eb0cfdb946e262438931a5968d3a0e6354376fb17c6aded89a590dfc8
|
data/README.md
CHANGED
@@ -216,6 +216,102 @@ specifying the S3 bucket and object key, either of the following fields may be u
|
|
216
216
|
Role: !GetAtt [ LambdaExecutionRole, Arn ]
|
217
217
|
```
|
218
218
|
|
219
|
+
### Nested Stacks Modules
|
220
|
+
|
221
|
+
The CloudFormation pre-compiler supports loading local templates as "nested stacks" using the
|
222
|
+
CloudFormation `AWS::CloudFormation::Stack` resource type.
|
223
|
+
|
224
|
+
Instead of first pre-deploying a template to S3 to be used for a nested stack, use the
|
225
|
+
`Template` property (instead of the `TemplateURL` property) to point to a local
|
226
|
+
sub-template. The sub-template will be compiled separately and deployed automatically to
|
227
|
+
an S3 bucket before deploying the compiled template to CloudFormation.
|
228
|
+
|
229
|
+
The `monitor` tool (also used during `create` operation) supports nested stacks by
|
230
|
+
automatically detecting nested stack updates in the main stack's event stream and will
|
231
|
+
start streaming the nested stack events - this allows the user to more easily locate problems
|
232
|
+
with nested stacks.
|
233
|
+
|
234
|
+
Currently there's no automatic resolution of references between nested and parent stacks, so
|
235
|
+
make sure to set up nested stack parameters for all resources that should be referenced from
|
236
|
+
the parent stack.
|
237
|
+
|
238
|
+
#### Example
|
239
|
+
|
240
|
+
`cloud-formation.yaml`:
|
241
|
+
|
242
|
+
```
|
243
|
+
AWSTemplateFormatVersion: "2010-09-09"
|
244
|
+
Description: "CloudFormation template with nested stacks"
|
245
|
+
Parameters:
|
246
|
+
DomainName:
|
247
|
+
Description: "The DNS domain name for the system"
|
248
|
+
Type: String
|
249
|
+
Default: example.com
|
250
|
+
AMI:
|
251
|
+
Description: "The AMI ID for the image to deploy"
|
252
|
+
Type: String
|
253
|
+
Default: ami-af4333cf
|
254
|
+
|
255
|
+
Resources:
|
256
|
+
VPC:
|
257
|
+
Type: AWS::EC2::VPC
|
258
|
+
Properties:
|
259
|
+
CidrBlock: 172.20.0.0/16
|
260
|
+
EnableDnsSupport: true
|
261
|
+
EnableDnsHostnames: true
|
262
|
+
SecurityGroupExample:
|
263
|
+
Type: AWS::EC2::SecurityGroup
|
264
|
+
Properties:
|
265
|
+
VpcId: !Ref VPC
|
266
|
+
GroupDescription: example security group
|
267
|
+
SecurityGroupIngress:
|
268
|
+
- { IpProtocol: icmp, CidrIp: 0.0.0.0/0, FromPort: -1, ToPort: -1 }
|
269
|
+
- { IpProtocol: tcp, CidrIp: 0.0.0.0/0, FromPort: 22, ToPort: 22 }
|
270
|
+
ServiceStack:
|
271
|
+
Type: AWS::CloudFormation::Stack
|
272
|
+
Properties:
|
273
|
+
Template: service.yaml
|
274
|
+
Parameters:
|
275
|
+
DomainName: !Ref DomainName
|
276
|
+
AMI: !Ref AMI
|
277
|
+
VPC: !Ref VPC
|
278
|
+
```
|
279
|
+
|
280
|
+
`service.yaml`:
|
281
|
+
|
282
|
+
```
|
283
|
+
AWSTemplateFormatVersion: "2010-09-09"
|
284
|
+
Description: "Service nested stack"
|
285
|
+
Parameters:
|
286
|
+
DomainName:
|
287
|
+
Description: "The DNS domain name for the system"
|
288
|
+
Type: String
|
289
|
+
AMI:
|
290
|
+
Description: "The AMI ID for the image to deploy"
|
291
|
+
Type: String
|
292
|
+
VPC:
|
293
|
+
Description: "The VPC into which to deploy the service"
|
294
|
+
Type: String
|
295
|
+
|
296
|
+
Resources:
|
297
|
+
Subnet:
|
298
|
+
Type: AWS::EC2::Subnet
|
299
|
+
Properties:
|
300
|
+
AvailabilityZone: !Select [ 0, !GetAZs { Ref: "AWS::Region" } ]
|
301
|
+
CidrBlock: 172.20.0.0/24
|
302
|
+
MapPublicIpOnLaunch: true
|
303
|
+
VpcId: !Ref VPC
|
304
|
+
Ec2Instance:
|
305
|
+
Type: AWS::EC2::Instance
|
306
|
+
Properties:
|
307
|
+
ImageId: !Ref AMI
|
308
|
+
KeyName: "secret"
|
309
|
+
NetworkInterfaces:
|
310
|
+
- AssociatePublicIpAddress: "true"
|
311
|
+
DeviceIndex: "0"
|
312
|
+
SubnetId: !Ref Subnet
|
313
|
+
```
|
314
|
+
|
219
315
|
## Caching
|
220
316
|
|
221
317
|
Some resource compilation may require uploading to S3, such as Lambda code or cloud-init setup
|
@@ -31,7 +31,7 @@ module CloudFormationTool
|
|
31
31
|
|
32
32
|
option [ "-d", "--debug" ], :flag, "Enable debug logging" do
|
33
33
|
logger.level = Logger::Severity::DEBUG
|
34
|
-
logger.formatter =
|
34
|
+
logger.formatter = nil
|
35
35
|
end
|
36
36
|
|
37
37
|
option [ "-q", "--quiet" ], :flag, "Enable debug logging" do
|
@@ -183,6 +183,12 @@ module CloudFormationTool
|
|
183
183
|
else
|
184
184
|
load_files(val, restype)
|
185
185
|
end
|
186
|
+
when 'AWS::CloudFormation::Stack'
|
187
|
+
if key == 'Properties' and val.key?('Template')
|
188
|
+
NestedStack.new(val, self).to_cloudformation
|
189
|
+
else
|
190
|
+
load_files(val, restype)
|
191
|
+
end
|
186
192
|
else
|
187
193
|
load_files(val, restype)
|
188
194
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CloudFormationTool
|
2
|
+
class CloudFormation
|
3
|
+
|
4
|
+
class NestedStack
|
5
|
+
include Storable
|
6
|
+
|
7
|
+
def initialize(props, tpl)
|
8
|
+
@tpl = tpl
|
9
|
+
@data = props
|
10
|
+
if props.key?('Template')
|
11
|
+
path = props['Template']
|
12
|
+
path = if path.start_with? "/" then path else "#{@tpl.basedir}/#{path}" end
|
13
|
+
@content = CloudFormation.new(path).to_yaml
|
14
|
+
@data['TemplateURL'] = upload(make_filename('yaml'), @content, mime_type: 'text/yaml', gzip: false)
|
15
|
+
@data.delete('Template')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_cloudformation
|
20
|
+
@data
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -15,6 +15,7 @@ module CloudFormationTool
|
|
15
15
|
@name = name
|
16
16
|
@seenev = Set.new
|
17
17
|
@watch_timeouts = 0
|
18
|
+
@nested_stacks = Hash[]
|
18
19
|
end
|
19
20
|
|
20
21
|
def delete
|
@@ -133,6 +134,7 @@ module CloudFormationTool
|
|
133
134
|
end
|
134
135
|
|
135
136
|
def monitor(start_time = nil)
|
137
|
+
@nested_stacks = Hash[]
|
136
138
|
done = false
|
137
139
|
begin
|
138
140
|
until done
|
@@ -140,26 +142,65 @@ module CloudFormationTool
|
|
140
142
|
next if @seenev.add?(ev.event_id).nil?
|
141
143
|
text = "#{ev.timestamp.strftime "%Y-%m-%d %H:%M:%S"}| " + %w(
|
142
144
|
resource_type:40
|
143
|
-
logical_resource_id:
|
145
|
+
logical_resource_id:42
|
144
146
|
resource_status
|
145
147
|
).collect { |field|
|
146
148
|
(name,size) = field.split(":")
|
147
149
|
size ||= 1
|
148
|
-
|
150
|
+
(if name == 'logical_resource_id' and ev.stack_name != self.name
|
151
|
+
logical_nested_stack_name(ev.stack_name) + "|"
|
152
|
+
else
|
153
|
+
''
|
154
|
+
end + ev.send(name.to_sym)).ljust(size.to_i, ' ')
|
149
155
|
}.join(" ")
|
150
156
|
text += " " + ev.resource_status_reason if ev.resource_status =~ /_FAILED/
|
151
157
|
if start_time.nil? or start_time < ev.timestamp
|
152
158
|
puts text
|
153
159
|
end
|
154
|
-
|
160
|
+
check_nested_stack(ev)
|
161
|
+
done = is_final_event(ev)
|
155
162
|
end
|
163
|
+
sleep 1
|
156
164
|
end
|
157
165
|
rescue CloudFormationTool::Errors::StackDoesNotExistError => e
|
158
166
|
puts "Stack #{name} does not exist"
|
159
167
|
end
|
160
168
|
end
|
161
169
|
|
170
|
+
def logical_nested_stack_name(phys_name)
|
171
|
+
@nested_stacks[phys_name] || 'unknown'
|
172
|
+
end
|
173
|
+
|
174
|
+
def nested_stack_name(ev)
|
175
|
+
ev.physical_resource_id.split('/')[1]
|
176
|
+
end
|
177
|
+
|
178
|
+
def check_nested_stack(ev)
|
179
|
+
return unless ev.resource_type == "AWS::CloudFormation::Stack" and
|
180
|
+
ev.logical_resource_id != self.name # not nested stack
|
181
|
+
return if @nested_stacks.has_key? ev.logical_resource_id # seeing the first or last nested stack event - ignoring
|
182
|
+
@nested_stacks[nested_stack_name(ev)] = ev.logical_resource_id
|
183
|
+
end
|
184
|
+
|
185
|
+
def is_final_event(ev)
|
186
|
+
ev.resource_type == "AWS::CloudFormation::Stack" and
|
187
|
+
ev.resource_status =~ /(_COMPLETE|_FAILED)$/ and
|
188
|
+
ev.logical_resource_id == self.name
|
189
|
+
end
|
190
|
+
|
191
|
+
def tracked_stacks
|
192
|
+
[ self.name ] + @nested_stacks.keys.compact
|
193
|
+
end
|
194
|
+
|
162
195
|
def each
|
196
|
+
tracked_stacks.each do |name|
|
197
|
+
events_for(name) do |ev|
|
198
|
+
yield ev
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def events_for(stack_name)
|
163
204
|
token = nil
|
164
205
|
sleep(if @_last_poll_time.nil?
|
165
206
|
0
|
@@ -172,7 +213,7 @@ module CloudFormationTool
|
|
172
213
|
end
|
173
214
|
end)
|
174
215
|
begin
|
175
|
-
resp = awscf.describe_stack_events stack_name:
|
216
|
+
resp = awscf.describe_stack_events stack_name: stack_name, next_token: token
|
176
217
|
@watch_timeouts = 0
|
177
218
|
resp.stack_events.each do |ev|
|
178
219
|
yield ev
|
@@ -188,7 +229,11 @@ module CloudFormationTool
|
|
188
229
|
end
|
189
230
|
rescue Aws::CloudFormation::Errors::ValidationError => e
|
190
231
|
if e.message =~ /does not exist/
|
191
|
-
|
232
|
+
if stack_name == self.name
|
233
|
+
raise CloudFormationTool::Errors::StackDoesNotExistError, "Stack does not exist"
|
234
|
+
end
|
235
|
+
# ignore "does not exist" errors on nested stacks - we may try to poll them before
|
236
|
+
# they actually exist. We'll just try later
|
192
237
|
else
|
193
238
|
raise e
|
194
239
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudformation-tool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oded Arbel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -168,6 +168,7 @@ files:
|
|
168
168
|
- lib/cloud_formation_tool/cloud_formation.rb
|
169
169
|
- lib/cloud_formation_tool/cloud_formation/cloud_front_distribution.rb
|
170
170
|
- lib/cloud_formation_tool/cloud_formation/lambda_code.rb
|
171
|
+
- lib/cloud_formation_tool/cloud_formation/nested_stack.rb
|
171
172
|
- lib/cloud_formation_tool/cloud_formation/stack.rb
|
172
173
|
- lib/cloud_formation_tool/cloud_init.rb
|
173
174
|
- lib/cloud_formation_tool/errors.rb
|
@@ -192,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
193
|
- !ruby/object:Gem::Version
|
193
194
|
version: '0'
|
194
195
|
requirements: []
|
195
|
-
rubygems_version: 3.0.
|
196
|
+
rubygems_version: 3.0.2
|
196
197
|
signing_key:
|
197
198
|
specification_version: 4
|
198
199
|
summary: A pre-compiler tool for CloudFormation YAML templates
|