cfndk 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +23 -14
  3. data/.gitignore +0 -1
  4. data/.rspec_parallel +6 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +811 -0
  7. data/README.md +122 -10
  8. data/cfndk.gemspec +1 -0
  9. data/lib/cfndk/change_set_command.rb +97 -0
  10. data/lib/cfndk/command.rb +15 -181
  11. data/lib/cfndk/config_file_loadable.rb +13 -0
  12. data/lib/cfndk/global_config.rb +15 -0
  13. data/lib/cfndk/key_pair.rb +7 -4
  14. data/lib/cfndk/key_pair_command.rb +53 -0
  15. data/lib/cfndk/key_pairs.rb +2 -1
  16. data/lib/cfndk/logger.rb +1 -1
  17. data/lib/cfndk/stack.rb +382 -103
  18. data/lib/cfndk/stack_command.rb +110 -0
  19. data/lib/cfndk/stacks.rb +40 -14
  20. data/lib/cfndk/subcommand_help_returnable.rb +16 -0
  21. data/lib/cfndk/version.rb +1 -1
  22. data/lib/cfndk.rb +6 -0
  23. data/skel/cfndk.yml +4 -0
  24. data/spec/cfndk_change_set_create_spec.rb +436 -0
  25. data/spec/cfndk_change_set_destroy_spec.rb +160 -0
  26. data/spec/cfndk_change_set_execute_spec.rb +179 -0
  27. data/spec/cfndk_change_set_report_spec.rb +107 -0
  28. data/spec/cfndk_change_set_spec.rb +37 -0
  29. data/spec/cfndk_create_spec.rb +56 -141
  30. data/spec/cfndk_destroy_spec.rb +4 -2
  31. data/spec/cfndk_keypiar_spec.rb +11 -9
  32. data/spec/cfndk_report_spec.rb +3 -1
  33. data/spec/cfndk_spec.rb +5 -3
  34. data/spec/cfndk_stack_create_spec.rb +454 -0
  35. data/spec/cfndk_stack_destroy_spec.rb +161 -0
  36. data/spec/cfndk_stack_report_spec.rb +181 -0
  37. data/spec/cfndk_stack_spec.rb +6 -1146
  38. data/spec/cfndk_stack_update_spec.rb +467 -0
  39. data/spec/spec_helper.rb +4 -1
  40. data/spec/support/aruba.rb +1 -0
  41. metadata +42 -2
data/lib/cfndk/stack.rb CHANGED
@@ -1,16 +1,20 @@
1
1
  module CFnDK
2
2
  class Stack
3
- attr_reader :template_file, :parameter_input, :capabilities, :depends, :timeout_in_minutes
4
- def initialize(name, data, option, credentials)
3
+ attr_reader :template_file, :parameter_input, :capabilities, :depends, :timeout_in_minutes, :region
4
+ def initialize(name, data, option, global_config, credentials)
5
+ @global_config = global_config
5
6
  @name = name
6
7
  @template_file = data['template_file'] || ''
7
8
  @parameter_input = data['parameter_input'] || ''
8
9
  @capabilities = data['capabilities'] || []
9
10
  @depends = data['depends'] || []
10
- @timeout_in_minutes = data['timeout_in_minutes'] || 1
11
+ @region = data['region'] || @global_config.region
12
+ @timeout_in_minutes = data['timeout_in_minutes'] || @global_config.timeout_in_minutes
11
13
  @override_parameters = data['parameters'] || {}
12
14
  @option = option
13
- @client = Aws::CloudFormation::Client.new(credentials: credentials)
15
+ @client = Aws::CloudFormation::Client.new(credentials: credentials, region: @region)
16
+ @s3_client = Aws::S3::Client.new(credentials: credentials, region: @region)
17
+ @sts_client = Aws::STS::Client.new(credentials: credentials, region: @region)
14
18
  end
15
19
 
16
20
  def create
@@ -20,26 +24,50 @@ module CFnDK
20
24
  CFnDK.logger.debug('Parametres :' + parameters.inspect)
21
25
  CFnDK.logger.debug('Capabilities:' + capabilities.inspect)
22
26
  CFnDK.logger.debug('Timeout :' + timeout_in_minutes.to_s)
23
- @client.create_stack(
27
+ CFnDK.logger.debug('Region :' + region)
28
+ tags = [
29
+ {
30
+ key: 'origina_name',
31
+ value: @name,
32
+ },
33
+ ]
34
+ tags.push(
35
+ key: 'UUID',
36
+ value: @option[:uuid]
37
+ ) if @option[:uuid]
38
+ hash = {
24
39
  stack_name: name,
25
- template_body: template_body,
26
40
  parameters: parameters,
27
41
  capabilities: capabilities,
28
- timeout_in_minutes: timeout_in_minutes
42
+ timeout_in_minutes: timeout_in_minutes,
43
+ tags: tags,
44
+ }
45
+ if large_template?
46
+ hash[:template_url] = upload_template_file()
47
+ else
48
+ hash[:template_body] = template_body()
49
+ end
50
+ @client.create_stack(
51
+ hash
29
52
  )
30
53
  end
31
54
 
32
55
  def wait_until_create
33
56
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
57
+ CFnDK.logger.info(('waiting create stack: ' + name).color(:green))
34
58
  begin
35
59
  @client.wait_until(
36
60
  :stack_create_complete,
37
61
  stack_name: name
38
- )
62
+ ) do |w|
63
+ w.max_attempts = 360
64
+ w.delay = 10
65
+ end
39
66
  CFnDK.logger.info(('created stack: ' + name).color(:green))
40
67
  rescue Aws::Waiters::Errors::FailureStateError => ex
41
- CFnDK.logger.error ex.message
42
- report_event
68
+ CFnDK.logger.error "#{ex.class}: #{ex.message}".color(:red)
69
+ @option[:type] = %w(tag output parameter resource event)
70
+ report
43
71
  raise ex
44
72
  end
45
73
  end
@@ -51,12 +79,20 @@ module CFnDK
51
79
  CFnDK.logger.debug('Parametres :' + parameters.inspect)
52
80
  CFnDK.logger.debug('Capabilities:' + capabilities.inspect)
53
81
  CFnDK.logger.debug('Timeout :' + timeout_in_minutes.to_s)
82
+ CFnDK.logger.debug('Region :' + region)
54
83
  begin
55
- @client.update_stack(
84
+ hash = {
56
85
  stack_name: name,
57
- template_body: template_body,
58
86
  parameters: parameters,
59
- capabilities: capabilities
87
+ capabilities: capabilities,
88
+ }
89
+ if large_template?
90
+ hash[:template_url] = upload_template_file()
91
+ else
92
+ hash[:template_body] = template_body()
93
+ end
94
+ @client.update_stack(
95
+ hash
60
96
  )
61
97
  true
62
98
  rescue Aws::CloudFormation::Errors::ValidationError => ex
@@ -65,17 +101,21 @@ module CFnDK
65
101
  CFnDK.logger.warn "#{ex.message}: #{name}".color(:red)
66
102
  false
67
103
  else
68
- raise ex
104
+ raise ex
69
105
  end
70
106
  end
71
107
  end
72
108
 
73
109
  def wait_until_update
74
110
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
111
+ CFnDK.logger.info(('waiting update stack: ' + name).color(:green))
75
112
  @client.wait_until(
76
113
  :stack_update_complete,
77
114
  stack_name: name
78
- )
115
+ ) do |w|
116
+ w.max_attempts = 360
117
+ w.delay = 10
118
+ end
79
119
  CFnDK.logger.info(('updated stack: ' + name).color(:green))
80
120
  end
81
121
 
@@ -84,6 +124,7 @@ module CFnDK
84
124
  if exits?
85
125
  CFnDK.logger.info(('deleting stack: ' + name).color(:green))
86
126
  CFnDK.logger.debug('Name :' + name)
127
+ CFnDK.logger.debug('Region :' + region)
87
128
  @client.delete_stack(
88
129
  stack_name: name
89
130
  )
@@ -95,63 +136,190 @@ module CFnDK
95
136
  def wait_until_destroy
96
137
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
97
138
  return unless exits?
139
+ CFnDK.logger.info(('waiting delete stack: ' + name).color(:green))
98
140
  @client.wait_until(
99
141
  :stack_delete_complete,
100
142
  stack_name: name
101
- )
143
+ ) do |w|
144
+ w.max_attempts = 360
145
+ w.delay = 10
146
+ end
102
147
  CFnDK.logger.info(('deleted stack: ' + name).color(:green))
103
148
  end
104
149
 
105
150
  def create_change_set
106
- return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
107
- CFnDK.logger.info(('creating change set: ' + name).color(:green))
108
- CFnDK.logger.debug('Name :' + name)
151
+ return nil if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
152
+ CFnDK.logger.info(('creating change set: ' + change_set_name).color(:green))
109
153
  CFnDK.logger.debug('Parametres :' + parameters.inspect)
110
154
  CFnDK.logger.debug('Capabilities:' + capabilities.inspect)
111
- @client.create_change_set(
155
+ CFnDK.logger.debug('Region :' + region)
156
+ tags = [
157
+ {
158
+ key: 'origina_name',
159
+ value: @name,
160
+ },
161
+ ]
162
+ tags.push(
163
+ key: 'UUID',
164
+ value: @option[:uuid]
165
+ ) if @option[:uuid]
166
+ tags.push(
167
+ key: 'CHANGE_SET_UUID',
168
+ value: @option[:change_set_uuid]
169
+ ) if @option[:change_set_uuid]
170
+ hash = {
112
171
  stack_name: name,
113
- template_body: template_body,
114
172
  parameters: parameters,
115
173
  capabilities: capabilities,
116
- change_set_name: name
174
+ change_set_name: change_set_name,
175
+ change_set_type: exits? ? 'UPDATE' : 'CREATE',
176
+ tags: tags,
177
+ }
178
+ if large_template?
179
+ hash[:template_url] = upload_template_file()
180
+ else
181
+ hash[:template_body] = template_body()
182
+ end
183
+ @client.create_change_set(
184
+ hash
117
185
  )
186
+ @name
187
+ rescue Aws::CloudFormation::Errors::ValidationError => ex
188
+ if review_in_progress?
189
+ CFnDK.logger.warn("failed create change set because the stack on REVIEW_IN_PROGRESS already exist : #{change_set_name}".color(:orange))
190
+ nil
191
+ else
192
+ CFnDK.logger.error("failed create change set: #{change_set_name}".color(:red))
193
+ raise ex
194
+ end
118
195
  end
119
196
 
120
197
  def wait_until_create_change_set
121
198
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
122
- begin
123
- @client.wait_until(
124
- :change_set_create_complete,
199
+ return unless exits?
200
+ CFnDK.logger.info(('waiting create change set: ' + change_set_name).color(:green))
201
+ @client.wait_until(
202
+ :change_set_create_complete,
203
+ stack_name: name,
204
+ change_set_name: change_set_name
205
+ ) do |w|
206
+ w.max_attempts = 360
207
+ w.delay = 10
208
+ end
209
+ CFnDK.logger.info("created change set: #{change_set_name}".color(:green))
210
+ rescue Aws::Waiters::Errors::FailureStateError => ex
211
+ case ex.message
212
+ when 'stopped waiting, encountered a failure state'
213
+ unless available_change_set?
214
+ delete_change_set
215
+ CFnDK.logger.warn("failed create change set because this change set is UNAVAILABLE: #{change_set_name}".color(:orange))
216
+ return
217
+ end
218
+ end
219
+ raise ex
220
+ end
221
+
222
+ def execute_change_set
223
+ return nil if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
224
+ if available_change_set?
225
+ CFnDK.logger.info(('executing change set: ' + change_set_name).color(:green))
226
+ @client.execute_change_set(
125
227
  stack_name: name,
126
- change_set_name: name
127
- )
128
- CFnDK.logger.info(('created chnage set: ' + name).color(:green))
129
- rescue Aws::Waiters::Errors::FailureStateError => ex
130
- resp = @client.describe_change_set(
131
- change_set_name: name,
132
- stack_name: name
228
+ change_set_name: change_set_name
133
229
  )
134
- if resp.status_reason != "The submitted information didn't contain changes. Submit different information to create a change set."
135
- CFnDK.logger.error ex.message.color(:red)
136
- raise ex
137
- else
138
- CFnDK.logger.error(('failed create change set: ' + name).color(:red))
139
- CFnDK.logger.error resp.status_reason
140
- @client.delete_change_set(
141
- change_set_name: name,
142
- stack_name: name
143
- )
144
- CFnDK.logger.info(('deleted change set: ' + name).color(:red))
230
+ CFnDK.logger.info(('execute change set: ' + change_set_name).color(:green))
231
+ @name
232
+ else
233
+ CFnDK.logger.warn("failed execute change set because this change set is not AVAILABLE: #{change_set_name}".color(:orange))
234
+ nil
235
+ end
236
+ end
237
+
238
+ def delete_change_set
239
+ return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
240
+ CFnDK.logger.info(('deleting change set: ' + change_set_name).color(:green))
241
+ @client.delete_change_set(
242
+ stack_name: name,
243
+ change_set_name: change_set_name
244
+ )
245
+ CFnDK.logger.info(('deleted change set: ' + change_set_name).color(:green))
246
+ end
247
+
248
+ def report_change_set
249
+ return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
250
+ CFnDK.logger.info('*****************************************************'.color(:green))
251
+ CFnDK.logger.info(('change set: ' + change_set_name).color(:green))
252
+ CFnDK.logger.info('*****************************************************'.color(:green))
253
+ CFnDK.logger.info('')
254
+ resp = @client.describe_change_set(
255
+ change_set_name: change_set_name,
256
+ stack_name: name
257
+ )
258
+ CFnDK.logger.info('Execution Status: '.color(:green) + colored_status(resp.execution_status))
259
+ CFnDK.logger.info('Status: '.color(:green) + colored_status(resp.status))
260
+ CFnDK.logger.info('Reason: '.color(:green) + resp.status_reason) if resp.status_reason
261
+ if @option[:types].instance_of?(Array) && @option[:types].include?('tag')
262
+ CFnDK.logger.info('Tags:'.color(:green))
263
+ tags_rows = resp.tags.map do |item|
264
+ [
265
+ item.key,
266
+ item.value,
267
+ ]
268
+ end
269
+ unless tags_rows.empty?
270
+ table = Terminal::Table.new headings: %w(Key Value), rows: tags_rows
271
+ CFnDK.logger.info table
272
+ end
273
+ end
274
+ if @option[:types].instance_of?(Array) && @option[:types].include?('parameter')
275
+ CFnDK.logger.info('Parameters:'.color(:green))
276
+ parameter_rows = resp.parameters.map do |item|
277
+ [
278
+ item.parameter_key,
279
+ item.parameter_value,
280
+ item.use_previous_value,
281
+ item.resolved_value,
282
+ ]
283
+ end
284
+ unless parameter_rows.empty?
285
+ table = Terminal::Table.new headings: ['Key', 'Value', 'Use Previous Value', 'Resolved Value'], rows: parameter_rows
286
+ CFnDK.logger.info table
145
287
  end
146
288
  end
289
+ if @option[:types].instance_of?(Array) && @option[:types].include?('changes')
290
+ CFnDK.logger.info('Changes:'.color(:green))
291
+ changes_rows = resp.changes.map do |item|
292
+ [
293
+ item.resource_change.action,
294
+ item.resource_change.logical_resource_id,
295
+ item.resource_change.physical_resource_id,
296
+ item.resource_change.resource_type,
297
+ item.resource_change.replacement,
298
+ ]
299
+ end
300
+ unless changes_rows.empty?
301
+ table = Terminal::Table.new headings: %w(Action Logical Physical Type Replacement), rows: changes_rows
302
+ CFnDK.logger.info table
303
+ end
304
+ end
305
+ rescue Aws::CloudFormation::Errors::ValidationError => ex
306
+ CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red)
307
+ rescue Aws::CloudFormation::Errors::ChangeSetNotFound => ex
308
+ CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red)
147
309
  end
148
310
 
149
311
  def validate
150
312
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
151
313
  CFnDK.logger.info(('validate stack: ' + name).color(:green))
152
314
  CFnDK.logger.debug('Name :' + @name)
315
+ hash = {}
316
+ if large_template?
317
+ hash[:template_url] = upload_template_file()
318
+ else
319
+ hash[:template_body] = template_body()
320
+ end
153
321
  @client.validate_template(
154
- template_body: template_body
322
+ hash
155
323
  )
156
324
  end
157
325
 
@@ -164,77 +332,139 @@ module CFnDK
164
332
  false
165
333
  end
166
334
 
167
- def report
168
- report_stack
169
- report_stack_resource
170
- report_event
335
+ def created?
336
+ resp = @client.describe_stacks(
337
+ stack_name: name
338
+ )
339
+ return false if resp.stacks[0].stack_status == 'REVIEW_IN_PROGRESS'
340
+ true
341
+ rescue Aws::CloudFormation::Errors::ValidationError
342
+ false
171
343
  end
172
344
 
173
- def report_stack
174
- return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
175
- CFnDK.logger.info(('stack: ' + name).color(:green))
176
- CFnDK.logger.debug('Name :' + name)
177
- begin
178
- rows = @client.describe_stacks(
179
- stack_name: name
180
- ).stacks.map do |item|
181
- [
182
- item.stack_name,
183
- item.creation_time,
184
- item.deletion_time,
185
- colored_status(item.stack_status),
186
- item.stack_status_reason]
187
- end
188
- table = Terminal::Table.new headings: %w(Name Creation Deletion Status Reason), rows: rows
189
- CFnDK.logger.info table
190
- rescue Aws::CloudFormation::Errors::ValidationError => ex
191
- CFnDK.logger.warn ex.message
192
- end
345
+ def review_in_progress?
346
+ resp = @client.describe_stacks(
347
+ stack_name: name
348
+ )
349
+ return true if resp.stacks[0].stack_status == 'REVIEW_IN_PROGRESS'
350
+ false
351
+ rescue Aws::CloudFormation::Errors::ValidationError
352
+ false
193
353
  end
194
354
 
195
- def report_event
196
- return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
197
- CFnDK.logger.info(('stack: ' + name).color(:green))
198
- CFnDK.logger.debug('Name :' + name)
199
- begin
200
- rows = @client.describe_stack_events(
201
- stack_name: name
202
- ).stack_events.map do |item|
203
- [
204
- item.resource_type,
205
- item.timestamp,
206
- colored_status(item.resource_status),
207
- item.resource_status_reason]
208
- end
209
- table = Terminal::Table.new headings: %w(Type Time Status Reason), rows: rows
210
- CFnDK.logger.info table
211
- rescue Aws::CloudFormation::Errors::ValidationError => ex
212
- CFnDK.logger.warn ex.message
213
- end
355
+ def available_change_set?
356
+ resp = @client.describe_change_set(
357
+ change_set_name: change_set_name,
358
+ stack_name: name
359
+ )
360
+ return true if resp.execution_status == 'AVAILABLE'
361
+ false
362
+ rescue Aws::CloudFormation::Errors::ChangeSetNotFound
363
+ false
214
364
  end
215
365
 
216
- def report_stack_resource
366
+ def report
217
367
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
368
+ CFnDK.logger.info('*****************************************************'.color(:green))
218
369
  CFnDK.logger.info(('stack: ' + name).color(:green))
219
- CFnDK.logger.debug('Name :' + name)
370
+ CFnDK.logger.info('*****************************************************'.color(:green))
371
+ CFnDK.logger.info('')
220
372
  begin
221
- rows = @client.describe_stack_resources(
373
+ resp = @client.describe_stacks(
222
374
  stack_name: name
223
- ).stack_resources.map do |item|
224
- [
225
- item.logical_resource_id,
226
- item.physical_resource_id,
227
- item.resource_type,
228
- item.timestamp,
229
- colored_status(item.resource_status),
230
- item.resource_status_reason,
231
- item.description,
232
- ]
375
+ ).stacks[0]
376
+ CFnDK.logger.info('Status: '.color(:green) + colored_status(resp.stack_status))
377
+ CFnDK.logger.info('Reason: '.color(:green) + resp.stack_status_reason) if resp.stack_status_reason
378
+ if @option[:types].instance_of?(Array) && @option[:types].include?('tag')
379
+ CFnDK.logger.info('Tags:'.color(:green))
380
+ tags_rows = resp.tags.map do |item|
381
+ [
382
+ item.key,
383
+ item.value,
384
+ ]
385
+ end
386
+ unless tags_rows.empty?
387
+ table = Terminal::Table.new headings: %w(Key Value), rows: tags_rows
388
+ CFnDK.logger.info table
389
+ end
390
+ end
391
+ if @option[:types].instance_of?(Array) && @option[:types].include?('parameter')
392
+ CFnDK.logger.info('Parameters:'.color(:green))
393
+ parameter_rows = resp.parameters.map do |item|
394
+ [
395
+ item.parameter_key,
396
+ item.parameter_value,
397
+ item.use_previous_value,
398
+ item.resolved_value,
399
+ ]
400
+ end
401
+ unless parameter_rows.empty?
402
+ table = Terminal::Table.new headings: ['Key', 'Value', 'Use Previous Value', 'Resolved Value'], rows: parameter_rows
403
+ CFnDK.logger.info table
404
+ end
405
+ end
406
+ if @option[:types].instance_of?(Array) && @option[:types].include?('output')
407
+ CFnDK.logger.info('Outputs:'.color(:green))
408
+ output_rows = resp.outputs.map do |item|
409
+ [
410
+ item.output_key,
411
+ item.output_value,
412
+ item.export_name,
413
+ item.description,
414
+ ]
415
+ end
416
+ unless output_rows.empty?
417
+ table = Terminal::Table.new headings: ['Key', 'Value', 'Export Name', 'Description'], rows: output_rows
418
+ CFnDK.logger.info table
419
+ end
233
420
  end
234
- table = Terminal::Table.new headings: %w(L-name P-name Type Timestamp Status Reason Desc), rows: rows
235
- CFnDK.logger.info table
236
421
  rescue Aws::CloudFormation::Errors::ValidationError => ex
237
- CFnDK.logger.warn ex.message
422
+ CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red)
423
+ end
424
+ if @option[:types].instance_of?(Array) && @option[:types].include?('resource')
425
+ begin
426
+ CFnDK.logger.info('Resources:'.color(:green))
427
+ rows = @client.describe_stack_resources(
428
+ stack_name: name
429
+ ).stack_resources.map do |item|
430
+ [
431
+ item.logical_resource_id,
432
+ item.physical_resource_id,
433
+ item.resource_type,
434
+ item.timestamp,
435
+ colored_status(item.resource_status),
436
+ item.resource_status_reason,
437
+ item.description,
438
+ ]
439
+ end
440
+ unless rows.empty?
441
+ table = Terminal::Table.new headings: %w(Logical Physical Type Timestamp Status Reason Desc), rows: rows
442
+ CFnDK.logger.info table
443
+ end
444
+ rescue Aws::CloudFormation::Errors::ValidationError => ex
445
+ CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red)
446
+ end
447
+ end
448
+ if @option[:types].instance_of?(Array) && @option[:types].include?('event')
449
+ CFnDK.logger.info('Events:'.color(:green))
450
+ begin
451
+ rows = @client.describe_stack_events(
452
+ stack_name: name
453
+ ).stack_events.map do |item|
454
+ [
455
+ item.resource_type,
456
+ item.timestamp,
457
+ colored_status(item.resource_status),
458
+ item.resource_status_reason,
459
+ ]
460
+ end
461
+ unless rows.empty?
462
+ table = Terminal::Table.new headings: %w(Type Time Status Reason), rows: rows
463
+ CFnDK.logger.info table
464
+ end
465
+ rescue Aws::CloudFormation::Errors::ValidationError => ex
466
+ CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red)
467
+ end
238
468
  end
239
469
  end
240
470
 
@@ -242,10 +472,18 @@ module CFnDK
242
472
  [@name, @option[:uuid]].compact.join('-')
243
473
  end
244
474
 
475
+ def change_set_name
476
+ [@name, @option[:change_set_uuid]].compact.join('-')
477
+ end
478
+
245
479
  def template_body
246
480
  File.open(@template_file, 'r').read
247
481
  end
248
482
 
483
+ def large_template?
484
+ File.size(@template_file) > 51200
485
+ end
486
+
249
487
  def parameters
250
488
  json = JSON.load(open(@parameter_input).read)
251
489
  json['Parameters'].map do |item|
@@ -259,6 +497,47 @@ module CFnDK
259
497
 
260
498
  private
261
499
 
500
+ def upload_template_file
501
+ begin
502
+ @s3_client.head_bucket(bucket: bucket_name)
503
+ rescue Aws::S3::Errors::NotFound, Aws::S3::Errors::Forbidden
504
+ @s3_client.create_bucket(bucket: bucket_name)
505
+ CFnDK.logger.info('Creatt S3 bucket: ' + bucket_name)
506
+ @s3_client.put_bucket_lifecycle_configuration(
507
+ bucket: bucket_name,
508
+ lifecycle_configuration: {
509
+ rules: [
510
+ {
511
+ expiration: {
512
+ days: 1,
513
+ },
514
+ status: 'Enabled',
515
+ id: 'Delete Old Files',
516
+ prefix: '',
517
+ abort_incomplete_multipart_upload: {
518
+ days_after_initiation: 1,
519
+ },
520
+ },
521
+ ],
522
+ }
523
+ )
524
+ end
525
+ key = [@global_config.s3_template_hash, @template_file].compact.join('/')
526
+ @s3_client.put_object(
527
+ body: template_body,
528
+ bucket: bucket_name,
529
+ key: key
530
+ )
531
+ url = "https://s3.amazonaws.com/#{bucket_name}/#{key}"
532
+ CFnDK.logger.info('Put S3 object: ' + url)
533
+ url
534
+ end
535
+
536
+ def bucket_name
537
+ resp = @sts_client.get_caller_identity({})
538
+ resp.account + '-' + @region + '-' + @global_config.s3_template_bucket
539
+ end
540
+
262
541
  def colored_status(str)
263
542
  case str
264
543
  when 'CREATE_FAILED' then