cfndk 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +8 -5
  3. data/.gitignore +1 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +0 -11
  6. data/Gemfile.lock +1069 -587
  7. data/README.md +124 -10
  8. data/cfndk.gemspec +7 -2
  9. data/docker/Dockerfile +8 -0
  10. data/docker/build.sh +3 -0
  11. data/docker/cfndk.sh +14 -0
  12. data/lib/cfndk/change_set_command.rb +14 -8
  13. data/lib/cfndk/command.rb +14 -6
  14. data/lib/cfndk/credential_provider_chain.rb +12 -42
  15. data/lib/cfndk/credential_resolvable.rb +10 -0
  16. data/lib/cfndk/diff.rb +38 -0
  17. data/lib/cfndk/global_config.rb +33 -2
  18. data/lib/cfndk/key_pair.rb +33 -1
  19. data/lib/cfndk/key_pair_command.rb +10 -3
  20. data/lib/cfndk/key_pairs.rb +12 -0
  21. data/lib/cfndk/stack.rb +67 -60
  22. data/lib/cfndk/stack_command.rb +26 -8
  23. data/lib/cfndk/stacks.rb +16 -0
  24. data/lib/cfndk/template_packager.rb +210 -0
  25. data/lib/cfndk/uuid.rb +10 -0
  26. data/lib/cfndk/version.rb +1 -1
  27. data/lib/cfndk.rb +12 -1
  28. data/spec/cfndk_spec.rb +1 -1
  29. data/spec/cfndk_stack_create_spec.rb +365 -5
  30. data/spec/cfndk_stack_destroy_spec.rb +64 -0
  31. data/spec/cfndk_stack_update_spec.rb +86 -0
  32. data/spec/fixtures/big_vpc.yaml +533 -0
  33. data/spec/fixtures/lambda_function/index.js +4 -0
  34. data/spec/fixtures/lambda_function/lambda_function.json +4 -0
  35. data/spec/fixtures/lambda_function/lambda_function.yaml +28 -0
  36. data/spec/fixtures/nested_stack.json +35 -0
  37. data/spec/fixtures/nested_stack.yaml +20 -0
  38. data/spec/fixtures/serverless_function/index.js +4 -0
  39. data/spec/fixtures/serverless_function/serverless_function.json +4 -0
  40. data/spec/fixtures/serverless_function/serverless_function.yaml +21 -0
  41. data/spec/fixtures/stack.json +8 -0
  42. data/spec/fixtures/stack.template.json +39 -0
  43. data/spec/fixtures/stack.yaml +22 -0
  44. data/spec/fixtures/vpc.template.json +40 -0
  45. data/vagrant/Vagrantfile +89 -0
  46. metadata +117 -13
@@ -1,17 +1,22 @@
1
1
  module CFnDK
2
2
  class KeyPair
3
- attr_reader :key_file
3
+ attr_reader :key_file, :enabled, :pre_command, :post_command
4
4
  def initialize(name, data, option, global_config, credentials)
5
5
  @global_config = global_config
6
6
  @name = name
7
7
  data = {} unless data
8
8
  @key_file = data['key_file'] || nil
9
9
  @region = data['region'] || @global_config.region
10
+ @pre_command = data['pre_command'] || nil
11
+ @post_command = data['post_command'] || nil
12
+ @enabled = true
13
+ @enabled = false if data['enabled'] === false
10
14
  @option = option
11
15
  @client = Aws::EC2::Client.new(credentials: credentials, region: @region)
12
16
  end
13
17
 
14
18
  def create
19
+ return unless @enabled
15
20
  CFnDK.logger.info(('creating keypair: ' + name).color(:green))
16
21
  key_pair = @client.create_key_pair(
17
22
  key_name: name
@@ -22,6 +27,7 @@ module CFnDK
22
27
  end
23
28
 
24
29
  def destroy
30
+ return unless @enabled
25
31
  if exists?
26
32
  CFnDK.logger.info(('deleting keypair: ' + name).color(:green))
27
33
  @client.delete_key_pair(
@@ -51,6 +57,32 @@ module CFnDK
51
57
  @name
52
58
  end
53
59
 
60
+ def pre_command_execute
61
+ return unless @enabled
62
+ if @pre_command
63
+ CFnDK.logger.info(('execute pre command: ' + @pre_command).color(:green))
64
+ IO.popen(@pre_command, :err => [:child, :out]) do |io|
65
+ io.each_line do |line|
66
+ CFnDK.logger.info((line).color(:green))
67
+ end
68
+ end
69
+ raise 'pre command is error. status: ' + $?.exitstatus.to_s + ' command: ' + @pre_command if $?.exitstatus != 0
70
+ end
71
+ end
72
+
73
+ def post_command_execute
74
+ return unless @enabled
75
+ if @post_command
76
+ CFnDK.logger.info(('execute post command: ' + @post_command).color(:green))
77
+ IO.popen(@post_command, :err => [:child, :out]) do |io|
78
+ io.each_line do |line|
79
+ CFnDK.logger.info((line).color(:green))
80
+ end
81
+ end
82
+ raise 'post command is error. status: ' + $?.exitstatus.to_s + ' command: ' + @post_command if $?.exitstatus != 0
83
+ end
84
+ end
85
+
54
86
  private
55
87
 
56
88
  def create_key_file(key_pair)
@@ -2,6 +2,8 @@ module CFnDK
2
2
  class KeyPairCommand < Thor
3
3
  include SubcommandHelpReturnable
4
4
  include ConfigFileLoadable
5
+ include CredentialResolvable
6
+
5
7
  class_option :verbose, type: :boolean, aliases: 'v', desc: 'More verbose output.'
6
8
  class_option :color, type: :boolean, default: true, desc: 'Use colored output'
7
9
  class_option :config_path, type: :string, aliases: 'c', default: "#{Dir.getwd}/cfndk.yml", desc: 'The configuration file to use'
@@ -13,10 +15,15 @@ module CFnDK
13
15
  def create
14
16
  CFnDK.logger.info 'create...'.color(:green)
15
17
  data = load_config_data(options)
16
-
17
- credentials = CFnDK::CredentialProviderChain.new.resolve
18
+ credentials = resolve_credential(data, options)
19
+ global_config = CFnDK::GlobalConfig.new(data, options)
18
20
  keypairs = CFnDK::KeyPairs.new(data, options, credentials)
21
+
22
+ global_config.pre_command_execute
23
+ keypairs.pre_command_execute
19
24
  keypairs.create
25
+ keypairs.post_command_execute
26
+ global_config.post_command_execute
20
27
  return 0
21
28
  rescue => e
22
29
  CFnDK.logger.error "#{e.class}: #{e.message}".color(:red)
@@ -31,8 +38,8 @@ module CFnDK
31
38
  def destroy
32
39
  CFnDK.logger.info 'destroy...'.color(:green)
33
40
  data = load_config_data(options)
41
+ credentials = resolve_credential(data, options)
34
42
 
35
- credentials = CFnDK::CredentialProviderChain.new.resolve
36
43
  keypairs = CFnDK::KeyPairs.new(data, options, credentials)
37
44
 
38
45
  if options[:force] || yes?('Are you sure you want to destroy? (y/n)', :yellow)
@@ -21,6 +21,18 @@ module CFnDK
21
21
  end
22
22
  end
23
23
 
24
+ def pre_command_execute
25
+ @keypairs.each_value do |keypair|
26
+ keypair.pre_command_execute
27
+ end
28
+ end
29
+
30
+ def post_command_execute
31
+ @keypairs.each_value do |keypair|
32
+ keypair.post_command_execute
33
+ end
34
+ end
35
+
24
36
  private
25
37
 
26
38
  def prepare_keypairs(data)
data/lib/cfndk/stack.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module CFnDK
2
2
  class Stack
3
- attr_reader :template_file, :parameter_input, :capabilities, :depends, :timeout_in_minutes, :region
3
+ attr_reader :template_file, :parameter_input, :capabilities, :depends, :timeout_in_minutes, :region, :role_arn, :package, :enabled, :pre_command, :post_command
4
4
  def initialize(name, data, option, global_config, credentials)
5
5
  @global_config = global_config
6
6
  @name = name
@@ -9,16 +9,24 @@ module CFnDK
9
9
  @capabilities = data['capabilities'] || []
10
10
  @depends = data['depends'] || []
11
11
  @region = data['region'] || @global_config.region
12
+ @role_arn = @global_config.role_arn
13
+ @package = data['package'] || @global_config.package
14
+ @pre_command = data['pre_command'] || nil
15
+ @post_command = data['post_command'] || nil
16
+ @enabled = true
17
+ @enabled = false if data['enabled'] === false
12
18
  @timeout_in_minutes = data['timeout_in_minutes'] || @global_config.timeout_in_minutes
13
19
  @override_parameters = data['parameters'] || {}
14
20
  @option = option
15
21
  @client = Aws::CloudFormation::Client.new(credentials: credentials, region: @region)
16
22
  @s3_client = Aws::S3::Client.new(credentials: credentials, region: @region)
17
23
  @sts_client = Aws::STS::Client.new(credentials: credentials, region: @region)
24
+ @tp = CFnDK::TemplatePackager.new(@template_file, @region, @package, @global_config, @s3_client, @sts_client)
18
25
  end
19
26
 
20
27
  def create
21
28
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
29
+ return unless @enabled
22
30
  CFnDK.logger.info(('creating stack: ' + name).color(:green))
23
31
  CFnDK.logger.debug('Name :' + name)
24
32
  CFnDK.logger.debug('Parametres :' + parameters.inspect)
@@ -42,10 +50,12 @@ module CFnDK
42
50
  timeout_in_minutes: timeout_in_minutes,
43
51
  tags: tags,
44
52
  }
45
- if large_template?
46
- hash[:template_url] = upload_template_file()
53
+ hash[:role_arn] = @role_arn if @role_arn
54
+
55
+ if @tp.large_template?
56
+ hash[:template_url] = @tp.upload_template_file()
47
57
  else
48
- hash[:template_body] = template_body()
58
+ hash[:template_body] = @tp.template_body()
49
59
  end
50
60
  @client.create_stack(
51
61
  hash
@@ -54,6 +64,7 @@ module CFnDK
54
64
 
55
65
  def wait_until_create
56
66
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
67
+ return unless @enabled
57
68
  CFnDK.logger.info(('waiting create stack: ' + name).color(:green))
58
69
  begin
59
70
  @client.wait_until(
@@ -74,6 +85,7 @@ module CFnDK
74
85
 
75
86
  def update
76
87
  return false if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
88
+ return unless @enabled
77
89
  CFnDK.logger.info(('updating stack: ' + name).color(:green))
78
90
  CFnDK.logger.debug('Name :' + name)
79
91
  CFnDK.logger.debug('Parametres :' + parameters.inspect)
@@ -86,10 +98,11 @@ module CFnDK
86
98
  parameters: parameters,
87
99
  capabilities: capabilities,
88
100
  }
89
- if large_template?
90
- hash[:template_url] = upload_template_file()
101
+ hash[:role_arn] = @role_arn if @role_arn
102
+ if @tp.large_template?
103
+ hash[:template_url] = @tp.upload_template_file()
91
104
  else
92
- hash[:template_body] = template_body()
105
+ hash[:template_body] = @tp.template_body()
93
106
  end
94
107
  @client.update_stack(
95
108
  hash
@@ -108,6 +121,7 @@ module CFnDK
108
121
 
109
122
  def wait_until_update
110
123
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
124
+ return unless @enabled
111
125
  CFnDK.logger.info(('waiting update stack: ' + name).color(:green))
112
126
  @client.wait_until(
113
127
  :stack_update_complete,
@@ -121,12 +135,17 @@ module CFnDK
121
135
 
122
136
  def destroy
123
137
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
138
+ return unless @enabled
124
139
  if exits?
125
140
  CFnDK.logger.info(('deleting stack: ' + name).color(:green))
126
141
  CFnDK.logger.debug('Name :' + name)
127
142
  CFnDK.logger.debug('Region :' + region)
143
+ hash = {
144
+ stack_name: name,
145
+ }
146
+ hash[:role_arn] = @role_arn if @role_arn
128
147
  @client.delete_stack(
129
- stack_name: name
148
+ hash
130
149
  )
131
150
  else
132
151
  CFnDK.logger.info(('do not delete stack: ' + name).color(:red))
@@ -135,6 +154,7 @@ module CFnDK
135
154
 
136
155
  def wait_until_destroy
137
156
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
157
+ return unless @enabled
138
158
  return unless exits?
139
159
  CFnDK.logger.info(('waiting delete stack: ' + name).color(:green))
140
160
  @client.wait_until(
@@ -149,6 +169,7 @@ module CFnDK
149
169
 
150
170
  def create_change_set
151
171
  return nil if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
172
+ return unless @enabled
152
173
  CFnDK.logger.info(('creating change set: ' + change_set_name).color(:green))
153
174
  CFnDK.logger.debug('Parametres :' + parameters.inspect)
154
175
  CFnDK.logger.debug('Capabilities:' + capabilities.inspect)
@@ -175,10 +196,11 @@ module CFnDK
175
196
  change_set_type: exits? ? 'UPDATE' : 'CREATE',
176
197
  tags: tags,
177
198
  }
178
- if large_template?
179
- hash[:template_url] = upload_template_file()
199
+ hash[:role_arn] = @role_arn if @role_arn
200
+ if @tp.large_template?
201
+ hash[:template_url] = @tp.upload_template_file()
180
202
  else
181
- hash[:template_body] = template_body()
203
+ hash[:template_body] = @tp.template_body()
182
204
  end
183
205
  @client.create_change_set(
184
206
  hash
@@ -196,6 +218,7 @@ module CFnDK
196
218
 
197
219
  def wait_until_create_change_set
198
220
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
221
+ return unless @enabled
199
222
  return unless exits?
200
223
  CFnDK.logger.info(('waiting create change set: ' + change_set_name).color(:green))
201
224
  @client.wait_until(
@@ -221,6 +244,7 @@ module CFnDK
221
244
 
222
245
  def execute_change_set
223
246
  return nil if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
247
+ return unless @enabled
224
248
  if available_change_set?
225
249
  CFnDK.logger.info(('executing change set: ' + change_set_name).color(:green))
226
250
  @client.execute_change_set(
@@ -237,6 +261,7 @@ module CFnDK
237
261
 
238
262
  def delete_change_set
239
263
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
264
+ return unless @enabled
240
265
  CFnDK.logger.info(('deleting change set: ' + change_set_name).color(:green))
241
266
  @client.delete_change_set(
242
267
  stack_name: name,
@@ -247,6 +272,7 @@ module CFnDK
247
272
 
248
273
  def report_change_set
249
274
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
275
+ return unless @enabled
250
276
  CFnDK.logger.info('*****************************************************'.color(:green))
251
277
  CFnDK.logger.info(('change set: ' + change_set_name).color(:green))
252
278
  CFnDK.logger.info('*****************************************************'.color(:green))
@@ -310,13 +336,14 @@ module CFnDK
310
336
 
311
337
  def validate
312
338
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
339
+ return unless @enabled
313
340
  CFnDK.logger.info(('validate stack: ' + name).color(:green))
314
341
  CFnDK.logger.debug('Name :' + @name)
315
342
  hash = {}
316
- if large_template?
317
- hash[:template_url] = upload_template_file()
343
+ if @tp.large_template?
344
+ hash[:template_url] = @tp.upload_template_file()
318
345
  else
319
- hash[:template_body] = template_body()
346
+ hash[:template_body] = @tp.template_body()
320
347
  end
321
348
  @client.validate_template(
322
349
  hash
@@ -365,6 +392,7 @@ module CFnDK
365
392
 
366
393
  def report
367
394
  return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
395
+ return unless @enabled
368
396
  CFnDK.logger.info('*****************************************************'.color(:green))
369
397
  CFnDK.logger.info(('stack: ' + name).color(:green))
370
398
  CFnDK.logger.info('*****************************************************'.color(:green))
@@ -476,14 +504,6 @@ module CFnDK
476
504
  [@name, @option[:change_set_uuid]].compact.join('-')
477
505
  end
478
506
 
479
- def template_body
480
- File.open(@template_file, 'r').read
481
- end
482
-
483
- def large_template?
484
- File.size(@template_file) > 51200
485
- end
486
-
487
507
  def parameters
488
508
  json = JSON.load(open(@parameter_input).read)
489
509
  json['Parameters'].map do |item|
@@ -495,49 +515,36 @@ module CFnDK
495
515
  end.compact
496
516
  end
497
517
 
498
- private
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
- )
518
+ def pre_command_execute
519
+ return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
520
+ return unless @enabled
521
+ if @pre_command
522
+ CFnDK.logger.info(('execute pre command: ' + @pre_command).color(:green))
523
+ IO.popen(@pre_command, :err => [:child, :out]) do |io|
524
+ io.each_line do |line|
525
+ CFnDK.logger.info((line).color(:green))
526
+ end
527
+ end
528
+ raise 'pre command is error. status: ' + $?.exitstatus.to_s + ' command: ' + @pre_command if $?.exitstatus != 0
524
529
  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
530
  end
535
531
 
536
- def bucket_name
537
- resp = @sts_client.get_caller_identity({})
538
- resp.account + '-' + @region + '-' + @global_config.s3_template_bucket
532
+ def post_command_execute
533
+ return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name)
534
+ return unless @enabled
535
+ if @post_command
536
+ CFnDK.logger.info(('execute post command: ' + @post_command).color(:green))
537
+ IO.popen(@post_command, :err => [:child, :out]) do |io|
538
+ io.each_line do |line|
539
+ CFnDK.logger.info((line).color(:green))
540
+ end
541
+ end
542
+ raise 'post command is error. status: ' + $?.exitstatus.to_s + ' command: ' + @post_command if $?.exitstatus != 0
543
+ end
539
544
  end
540
545
 
546
+ private
547
+
541
548
  def colored_status(str)
542
549
  case str
543
550
  when 'CREATE_FAILED' then
@@ -2,11 +2,12 @@ module CFnDK
2
2
  class StackCommand < Thor
3
3
  include SubcommandHelpReturnable
4
4
  include ConfigFileLoadable
5
+ include CredentialResolvable
5
6
 
6
7
  class_option :verbose, type: :boolean, aliases: 'v', desc: 'More verbose output.'
7
8
  class_option :color, type: :boolean, default: true, desc: 'Use colored output'
8
9
  class_option :config_path, type: :string, aliases: 'c', default: "#{Dir.getwd}/cfndk.yml", desc: 'The configuration file to use'
9
- class_option :stack_names, type: :array, desc: 'Target stack names'
10
+ class_option :stack_names, type: :array, aliases: 's', desc: 'Target stack names'
10
11
 
11
12
  desc 'create', 'Create stack'
12
13
  option :uuid, type: :string, aliases: 'u', default: ENV['CFNDK_UUID'] || nil, desc: 'Use UUID'
@@ -14,11 +15,16 @@ module CFnDK
14
15
  def create
15
16
  CFnDK.logger.info 'create...'.color(:green)
16
17
  data = load_config_data(options)
17
-
18
- credentials = CFnDK::CredentialProviderChain.new.resolve
18
+ credentials = resolve_credential(data, options)
19
+ global_config = CFnDK::GlobalConfig.new(data, options)
19
20
  stacks = CFnDK::Stacks.new(data, options, credentials)
21
+
22
+ global_config.pre_command_execute
23
+ stacks.pre_command_execute
20
24
  stacks.validate
21
25
  stacks.create
26
+ stacks.post_command_execute
27
+ global_config.post_command_execute
22
28
  return 0
23
29
  rescue => e
24
30
  CFnDK.logger.error "#{e.class}: #{e.message}".color(:red)
@@ -34,11 +40,16 @@ module CFnDK
34
40
  def update
35
41
  CFnDK.logger.info 'update...'.color(:green)
36
42
  data = load_config_data(options)
37
-
38
- credentials = CFnDK::CredentialProviderChain.new.resolve
43
+ credentials = resolve_credential(data, options)
44
+ global_config = CFnDK::GlobalConfig.new(data, options)
39
45
  stacks = CFnDK::Stacks.new(data, options, credentials)
46
+
47
+ global_config.pre_command_execute
48
+ stacks.pre_command_execute
40
49
  stacks.validate
41
50
  stacks.update
51
+ stacks.post_command_execute
52
+ global_config.post_command_execute
42
53
  return 0
43
54
  rescue => e
44
55
  CFnDK.logger.error "#{e.class}: #{e.message}".color(:red)
@@ -54,8 +65,8 @@ module CFnDK
54
65
  def destroy
55
66
  CFnDK.logger.info 'destroy...'.color(:green)
56
67
  data = load_config_data(options)
68
+ credentials = resolve_credential(data, options)
57
69
 
58
- credentials = CFnDK::CredentialProviderChain.new.resolve
59
70
  stacks = CFnDK::Stacks.new(data, options, credentials)
60
71
 
61
72
  if options[:force] || yes?('Are you sure you want to destroy? (y/n)', :yellow)
@@ -77,9 +88,15 @@ module CFnDK
77
88
  def validate
78
89
  CFnDK.logger.info 'validate...'.color(:green)
79
90
  data = load_config_data(options)
80
- credentials = CFnDK::CredentialProviderChain.new.resolve
91
+ credentials = resolve_credential(data, options)
92
+ global_config = CFnDK::GlobalConfig.new(data, options)
81
93
  stacks = CFnDK::Stacks.new(data, options, credentials)
94
+
95
+ global_config.pre_command_execute
96
+ stacks.pre_command_execute
82
97
  stacks.validate
98
+ stacks.post_command_execute
99
+ global_config.post_command_execute
83
100
  return 0
84
101
  rescue => e
85
102
  CFnDK.logger.error "#{e.class}: #{e.message}".color(:red)
@@ -95,7 +112,8 @@ module CFnDK
95
112
  def report
96
113
  CFnDK.logger.info 'report...'.color(:green)
97
114
  data = load_config_data(options)
98
- credentials = CFnDK::CredentialProviderChain.new.resolve
115
+ credentials = resolve_credential(data, options)
116
+
99
117
  stacks = CFnDK::Stacks.new(data, options, credentials)
100
118
  stacks.report
101
119
  return 0
data/lib/cfndk/stacks.rb CHANGED
@@ -107,6 +107,22 @@ module CFnDK
107
107
  end
108
108
  end
109
109
 
110
+ def pre_command_execute
111
+ @sequence.each do |stacks|
112
+ stacks.each do |name|
113
+ @stacks[name].pre_command_execute
114
+ end
115
+ end
116
+ end
117
+
118
+ def post_command_execute
119
+ @sequence.each do |stacks|
120
+ stacks.each do |name|
121
+ @stacks[name].post_command_execute
122
+ end
123
+ end
124
+ end
125
+
110
126
  private
111
127
 
112
128
  def prepare_stack(data)