cloudformation-tool 1.2.2 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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