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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15f7ca4dd9a56c71678b984eee089dea7b320bf9e0e7b3d02f72f6d52c3b0182
4
- data.tar.gz: 519a9758535c8323f472b512128a96f676eaa611147a0202a09542eb5bee8418
3
+ metadata.gz: c6667d6553e4525f298bb9b2fd517a7045d50f8ed88273fe7bf43ca1e5b588ed
4
+ data.tar.gz: f8b8ebc3884b92222e8fe67e60f6fdea325cc60b0130eecb36b45edb83e7f209
5
5
  SHA512:
6
- metadata.gz: f3b0164a2992ee13b4a0278f17fd396f7e4e49718b2c5b7a524894fc1a6002ea070295d3127d646d782b4f3b6d0de85641791ad91932d63b099a1e23b30d79b9
7
- data.tar.gz: 41bc98433f5ec0eae331b6733c1f6b26dbdaf25e320b0c57c1763148efd3b62798808f1db612f84c8817210db0555e8caf7c6c709a3960849462a2052fd1baf6
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 = logger.default_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:38
145
+ logical_resource_id:42
144
146
  resource_status
145
147
  ).collect { |field|
146
148
  (name,size) = field.split(":")
147
149
  size ||= 1
148
- ev.send(name.to_sym).ljust(size.to_i, ' ')
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
- done = (ev.resource_type == "AWS::CloudFormation::Stack" and ev.resource_status =~ /(_COMPLETE|_FAILED)$/)
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: name, next_token: token
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
- raise CloudFormationTool::Errors::StackDoesNotExistError, "Stack does not exist"
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
@@ -1,3 +1,3 @@
1
1
  module CloudFormationTool
2
- VERSION = '1.2.2'
2
+ VERSION = '1.3.1'
3
3
  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.2.2
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: 2018-12-23 00:00:00.000000000 Z
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.1
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