bora 1.4.1 → 1.5.0

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
  SHA1:
3
- metadata.gz: a0e4a8a4f46e58395bd724d7b5b43e47682aa4ad
4
- data.tar.gz: 9f457db9cd67e8b64d6c23d2424491f69cd8977f
3
+ metadata.gz: ff4f553135f201ba7386c5b97464b2cf23931c85
4
+ data.tar.gz: 78c79cc5fd9755d085b6f28ea6c8233a967fc0c2
5
5
  SHA512:
6
- metadata.gz: 000fe250f567e4e7e9ad734fdab2f3c8d0783e0d688492163690849cb60c1111e9932267393edce64c7bf856ec7bf1b4b03e925eb4cd9559f6280f0ff6b0dea2
7
- data.tar.gz: db0d0bde5699eff9255cf38e98419ca46805b0d57f84505e6568ef42568f7294f82238fd66a28f52109559a8248a946de025ad33b84928ccb08aa454268ea5e6
6
+ metadata.gz: f95882cd11d3ee76306203b5a38c765c7ff525ff6e7633325593d5114afee6dee4ebf00f26dae6088628fb8a891ed1bfd488c8cb43d3d0175aa2b85e6837c874
7
+ data.tar.gz: ae7563ed6725c60cd623051a3f3cbd625aa1eff8bacce0a5e7c3931a92a7fac07b86a15efc26557b824c20927dbed2b5ee660f9d20b97ae42e29ae63a142cf86
data/README.md CHANGED
@@ -151,8 +151,10 @@ templates:
151
151
  The following commands are available through the command line and rake tasks.
152
152
 
153
153
  * **apply** - Creates the stack if it doesn't exist, or updates it otherwise
154
+ * **changeset** - Manage CloudFormation change sets for the stack
154
155
  * **delete** - Deletes the stack
155
- * **diff** - Provides a visual diff between the local template and the currently applied template in AWS
156
+ * **diff** - Provides a visual diff between the local template and the currently applied template in AWS.
157
+ The diff also shows the changes that CloudFormation will apply as reported by the CloudFormation [Change Set API](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html).
156
158
  * **events** - Outputs the latest events from the stack
157
159
  * **list** - Outputs a list of all stacks defined in the config file
158
160
  * **outputs** - Shows the outputs from the stack
@@ -163,6 +165,20 @@ The following commands are available through the command line and rake tasks.
163
165
  * **status** - Displays the current status of the stack
164
166
  * **validate** - Validates the template using the AWS CloudFormation "validate" API call
165
167
 
168
+ ### Change Sets
169
+ Bora provides full support for working with [CloudFormation Change Sets](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html)
170
+ via the "changeset" command. The following subcommands are available (eg: `bora changeset <subcommand> ...`). Run `bora changeset help` for more information.
171
+
172
+ * **changeset create** - Creates a change set for the stack
173
+ * **changeset list** - Lists all available change sets for the stack
174
+ * **changeset show** - Shows the details of a particular change set
175
+ * **changeset apply** - Applies a change set. Any other available change sets will be automatically deleted by AWS after this action.
176
+ * **changeset delete** - Deletes a particular change set for the stack
177
+
178
+ Note that `bora diff` will also show you a summary of the change set that will be applied.
179
+ It does this by creating (and automaticlly deleting) a temporary change set in order to get the change actions to display.
180
+
181
+ Note that change set funtionality is not available via the Rake tasks at this time.
166
182
 
167
183
  ### Command Line
168
184
 
@@ -288,9 +304,12 @@ ${hostedzone://example.com/private}
288
304
 
289
305
  Looks up an AMI given a name prefix which may contain wildcards. If query returns multiple images the latest is used.
290
306
 
307
+ Owners takes a query string list of AWS account ID, self (owner is the sender of the request), or an AWS owner alias (valid values are amazon | aws-marketplace | microsoft). Omitting this option defaults to self
308
+
291
309
  ```bash
292
310
  ${ami://amzn-ami-hv*x86_64-gp2?owner=amazon}
293
311
  ${ami://my-windows-soe}
312
+ ${ami://my-windows-soe?owner=1234567890,self}
294
313
  ```
295
314
 
296
315
  ## Overriding Stack Parameters from the Command Line
@@ -334,7 +353,8 @@ If this is of interest to you, please have a look at the [GitHub issue](https://
334
353
 
335
354
 
336
355
  ## Related Projects
337
- The following projects provided inspiration for Bora:
356
+ The following projects provided inspiration for or are similar to Bora.
357
+ If Bora doesn't meet your needs, one of these might.
338
358
  * [CfnDsl](https://github.com/stevenjack/cfndsl) - A Ruby DSL for CloudFormation templates
339
359
  * [StackMaster](https://github.com/envato/stack_master) - Very similar in goals to Bora
340
360
  * [CloudFormer](https://github.com/kunday/cloudformer) - Rake tasks for CloudFormation
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency "thor", "~> 0.19"
27
27
 
28
28
  spec.add_development_dependency "bundler", "~> 1.11"
29
+ spec.add_development_dependency "hashie", "~> 3.4.6"
29
30
  spec.add_development_dependency "rspec", "~> 3.0"
30
31
  spec.add_development_dependency "simplecov", "~> 0.12"
31
32
  end
@@ -0,0 +1,17 @@
1
+ require "bora/cfn/change_set_action"
2
+
3
+ class Bora
4
+ module Cfn
5
+ class Change
6
+ def initialize(change)
7
+ @change = change
8
+ @resource_change = @change.resource_change
9
+ @action = ChangeSetAction.new(@resource_change.action, @resource_change.replacement)
10
+ end
11
+
12
+ def to_s
13
+ "#{@action} - #{@resource_change.resource_type} - #{@resource_change.logical_resource_id}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ require "bora/cfn/change"
2
+ require "bora/cfn/status"
3
+
4
+ class Bora
5
+ module Cfn
6
+ class ChangeSet
7
+ def initialize(change_set, is_summary = false)
8
+ @change_set = change_set
9
+ @is_summary = is_summary
10
+ @status = Status.new(@change_set.status)
11
+ @execution_status = Status.new(@change_set.execution_status)
12
+ @changes = @is_summary ? [] : change_set.changes.map { |c| Change.new(c) }
13
+ end
14
+
15
+ def status_success?
16
+ @status.success?
17
+ end
18
+
19
+ def status_failure?
20
+ @status.failure?
21
+ end
22
+
23
+ def status_complete?
24
+ status_success? || status_failure?
25
+ end
26
+
27
+ def has_changes?
28
+ @status.success? && @changes.size > 0
29
+ end
30
+
31
+ def to_s(changes_only: false)
32
+ reason = @change_set.status_reason ? " (#{@change_set.status_reason})" : ""
33
+ description = @change_set.description ? " - #{@change_set.description}" : ""
34
+ changes_str = !@is_summary ? @changes.map(&:to_s).join("\n") : ""
35
+ if changes_only
36
+ s = changes_str
37
+ else
38
+ s = "#{@change_set.change_set_name.bold} - #{@change_set.creation_time.getlocal} - #{@status}#{reason} - #{@execution_status}#{description}"
39
+ s += "\n#{changes_str}" if !changes_str.empty?
40
+ end
41
+ s
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,36 @@
1
+ require 'colorize'
2
+
3
+ class Bora
4
+ module Cfn
5
+ class ChangeSetAction
6
+ def initialize(action, replacement)
7
+ @action = action
8
+ @replacement = replacement
9
+ end
10
+
11
+ def to_s
12
+ action_str = @action
13
+ if @action == "Modify"
14
+ action_str = case @replacement
15
+ when "True" then "Replace"
16
+ when "Conditional" then "Replace (conditional)"
17
+ else action_str
18
+ end
19
+ end
20
+ action_str.colorize(color)
21
+ end
22
+
23
+
24
+ private
25
+
26
+ def color
27
+ case @action
28
+ when "Add" then :green
29
+ when "Remove" then :red
30
+ else :yellow
31
+ end
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -2,6 +2,7 @@ require 'set'
2
2
  require 'open-uri'
3
3
  require 'aws-sdk'
4
4
  require 'bora/cfn/stack_status'
5
+ require 'bora/cfn/change_set'
5
6
  require 'bora/cfn/event'
6
7
  require 'bora/cfn/output'
7
8
  require 'bora/cfn/parameter'
@@ -19,11 +20,11 @@ class Bora
19
20
  end
20
21
 
21
22
  def create(options, &block)
22
- call_cfn_action(:create, options, &block)
23
+ call_cfn_action(:create_stack, options, &block)
23
24
  end
24
25
 
25
26
  def update(options, &block)
26
- call_cfn_action(:update, options, &block)
27
+ call_cfn_action(:update_stack, options, &block)
27
28
  end
28
29
 
29
30
  def create_or_update(options, &block)
@@ -36,7 +37,7 @@ class Bora
36
37
  end
37
38
 
38
39
  def delete(&block)
39
- call_cfn_action(:delete, &block)
40
+ call_cfn_action(:delete_stack, &block)
40
41
  end
41
42
 
42
43
  def events
@@ -72,6 +73,36 @@ class Bora
72
73
  status.exists?
73
74
  end
74
75
 
76
+ def create_change_set(change_set_name, options)
77
+ change_set_options = {
78
+ stack_name: @stack_name,
79
+ change_set_name: change_set_name
80
+ }
81
+ cloudformation.create_change_set(change_set_options.merge(options))
82
+ begin
83
+ change_set = ChangeSet.new(cloudformation.describe_change_set(change_set_options))
84
+ sleep 5 unless change_set.status_complete?
85
+ end until change_set.status_complete?
86
+ change_set
87
+ end
88
+
89
+ def list_change_sets
90
+ cfn_change_sets = cloudformation.list_change_sets(stack_name: @stack_name)
91
+ cfn_change_sets.summaries.map { |cs| ChangeSet.new(cs, true) }
92
+ end
93
+
94
+ def describe_change_set(change_set_name)
95
+ ChangeSet.new(cloudformation.describe_change_set(stack_name: @stack_name, change_set_name: change_set_name))
96
+ end
97
+
98
+ def delete_change_set(change_set_name)
99
+ cloudformation.delete_change_set(stack_name: @stack_name, change_set_name: change_set_name)
100
+ end
101
+
102
+ def execute_change_set(change_set_name, &block)
103
+ call_cfn_action(:execute_change_set, {change_set_name: change_set_name}, &block)
104
+ end
105
+
75
106
 
76
107
  # =============================================================================================
77
108
  private
@@ -82,23 +113,19 @@ class Bora
82
113
  end
83
114
  end
84
115
 
85
- def method_missing(sym, *args, &block)
86
- underlying_stack ? underlying_stack.send(sym, *args, &block) : nil
87
- end
88
-
89
116
  def call_cfn_action(action, options = {}, &block)
90
117
  underlying_stack(refresh: true)
91
- return true if action == :delete && !exists?
118
+ return true if action == :delete_stack && !exists?
92
119
  @previous_event_time = last_event_time
93
120
  begin
94
121
  action_options = {stack_name: @stack_name}.merge(options)
95
- cloudformation.method("#{action.to_s.downcase}_stack").call(action_options)
122
+ cloudformation.method(action.to_s.downcase).call(action_options)
96
123
  wait_for_completion(&block)
97
124
  rescue Aws::CloudFormation::Errors::ValidationError => e
98
125
  raise e unless e.message.include?(NO_UPDATE_MESSAGE)
99
126
  return nil
100
127
  end
101
- (action == :delete && !underlying_stack) || status.success?
128
+ (action == :delete_stack && !underlying_stack) || status.success?
102
129
  end
103
130
 
104
131
  def wait_for_completion
@@ -140,7 +167,9 @@ class Bora
140
167
  events = cloudformation.describe_stack_events({stack_name: @stack_name}).stack_events
141
168
  events.length > 0 ? events[0].timestamp : Time.at(0)
142
169
  end
170
+
143
171
  end
144
172
 
173
+
145
174
  end
146
175
  end
@@ -12,7 +12,7 @@ class Bora
12
12
  end
13
13
 
14
14
  def failure?
15
- @status.end_with?("_FAILED") || @status.include?("ROLLBACK")
15
+ @status.end_with?("FAILED") || @status.include?("ROLLBACK")
16
16
  end
17
17
 
18
18
  def deleted?
@@ -1,8 +1,11 @@
1
1
  require "thor"
2
2
  require "bora"
3
+ require "bora/cli_base"
4
+ require "bora/cli_change_set"
3
5
 
4
6
  class Bora
5
- class Cli < Thor
7
+ class Cli < CliBase
8
+
6
9
  class_option :file,
7
10
  type: :string,
8
11
  aliases: :f,
@@ -91,33 +94,8 @@ class Bora
91
94
  stack(options.file, stack_name).validate(params)
92
95
  end
93
96
 
94
-
95
- private
96
-
97
- def stack(config_file, stack_name)
98
- region = options.region
99
- cfn_stack_name = options["cfn-stack-name"]
100
-
101
- override_config = {}
102
- override_config["default_region"] = region if region
103
- override_config["cfn_stack_name"] = cfn_stack_name if cfn_stack_name
104
-
105
- bora = bora(config_file, override_config)
106
- stack = bora.stack(stack_name)
107
- if !stack
108
- STDERR.puts "Could not find stack #{stack_name}"
109
- exit(1)
110
- end
111
- stack
112
- end
113
-
114
- def bora(config_file, override_config = {})
115
- Bora.new(config_file_or_hash: config_file, override_config: override_config)
116
- end
117
-
118
- def params
119
- options.params ? Hash[options.params.map { |param| param.split("=", 2) }] : {}
120
- end
97
+ desc "changeset SUBCOMMAND ...ARGS", "Manage CloudFormation change sets"
98
+ subcommand "changeset", CliChangeSet
121
99
 
122
100
  end
123
101
  end
@@ -0,0 +1,43 @@
1
+ require "thor"
2
+
3
+ class Bora
4
+ class CliBase < Thor
5
+ # Fix for incorrect subcommand help. See https://github.com/erikhuda/thor/issues/261
6
+ def self.banner(command, namespace = nil, subcommand = false)
7
+ subcommand = subcommand_prefix
8
+ subcommand_str = subcommand ? " #{subcommand}" : ""
9
+ "#{basename}#{subcommand_str} #{command.usage}"
10
+ end
11
+
12
+ def self.subcommand_prefix
13
+ nil
14
+ end
15
+
16
+ no_commands do
17
+ def stack(config_file, stack_name)
18
+ region = options.region
19
+ cfn_stack_name = options["cfn-stack-name"]
20
+
21
+ override_config = {}
22
+ override_config["default_region"] = region if region
23
+ override_config["cfn_stack_name"] = cfn_stack_name if cfn_stack_name
24
+
25
+ bora = bora(config_file, override_config)
26
+ stack = bora.stack(stack_name)
27
+ if !stack
28
+ STDERR.puts "Could not find stack #{stack_name}"
29
+ exit(1)
30
+ end
31
+ stack
32
+ end
33
+
34
+ def bora(config_file, override_config = {})
35
+ Bora.new(config_file_or_hash: config_file, override_config: override_config)
36
+ end
37
+
38
+ def params
39
+ options.params ? Hash[options.params.map { |param| param.split("=", 2) }] : {}
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ require "thor"
2
+ require 'thor/group'
3
+
4
+ class Bora
5
+ class CliChangeSet < CliBase
6
+ # Fix for incorrect subcommand help. See https://github.com/erikhuda/thor/issues/261
7
+ def self.subcommand_prefix
8
+ "changeset"
9
+ end
10
+
11
+ desc "create STACK_NAME CHANGE_SET_NAME", "Creates a change set"
12
+ option :params, type: :array, aliases: :p, desc: "Parameters to be passed to the template, eg: --params 'instance_type=t2.micro'"
13
+ option :description, type: :string, aliases: :d, desc: "A description for this change set"
14
+ option :pretty, type: :boolean, default: false, desc: "Send pretty (formatted) JSON to AWS (only works for cfndsl templates)"
15
+ def create(stack_name, change_set_name)
16
+ stack(options.file, stack_name).create_change_set(change_set_name, options.description, params, options.pretty)
17
+ end
18
+
19
+ desc "list STACK_NAME", "Lists all change sets for stack STACK_NAME"
20
+ def list(stack_name)
21
+ stack(options.file, stack_name).list_change_sets
22
+ end
23
+
24
+ desc "show STACK_NAME CHANGE_SET_NAME", "Shows the details of the named change set"
25
+ def show(stack_name, change_set_name)
26
+ stack(options.file, stack_name).describe_change_set(change_set_name)
27
+ end
28
+
29
+ desc "delete STACK_NAME CHANGE_SET_NAME", "Deletes the named change set"
30
+ def delete(stack_name, change_set_name)
31
+ stack(options.file, stack_name).delete_change_set(change_set_name)
32
+ end
33
+
34
+ desc "apply STACK_NAME CHANGE_SET_NAME", "Executes the named change set"
35
+ def apply(stack_name, change_set_name)
36
+ stack(options.file, stack_name).execute_change_set(change_set_name)
37
+ end
38
+
39
+ end
40
+ end
@@ -1,38 +1,45 @@
1
1
  require 'bora/cfn/stack'
2
-
3
2
  class Bora
4
3
  module Resolver
5
4
  class Ami
6
5
  NoAMI = Class.new(StandardError)
7
6
  InvalidParameter = Class.new(StandardError)
7
+ InvalidUserId = Class.new(StandardError)
8
8
 
9
9
  def initialize(stack)
10
10
  @stack = stack
11
11
  end
12
12
 
13
13
  def resolve(uri)
14
- owner = 'self' # Default to account owner
14
+ owners = []
15
15
  ami_prefix = uri.host
16
16
  raise InvalidParameter, "Invalid ami parameter #{uri}" unless ami_prefix
17
17
  if !uri.query.nil? && uri.query.include?('owner')
18
18
  query = URI.decode_www_form(uri.query).to_h
19
- owner = query['owner']
19
+ owners = query['owner'].split(',')
20
+ else
21
+ owners << 'self'
20
22
  end
21
23
 
24
+
22
25
  ec2 = Aws::EC2::Client.new(region: @stack.region)
23
- images = ec2.describe_images(
24
- owners: [owner],
25
- filters: [
26
- {
27
- name: 'name',
28
- values: [ami_prefix]
29
- },
30
- {
31
- name: 'state',
32
- values: ['available']
33
- }
34
- ]
35
- ).images
26
+ begin
27
+ images = ec2.describe_images(
28
+ owners: owners,
29
+ filters: [
30
+ {
31
+ name: 'name',
32
+ values: [ami_prefix]
33
+ },
34
+ {
35
+ name: 'state',
36
+ values: ['available']
37
+ }
38
+ ]
39
+ ).images
40
+ rescue Aws::EC2::Errors::InvalidUserIDMalformed
41
+ raise InvalidUserId, "Invalid owner argument in #{uri}"
42
+ end
36
43
 
37
44
  raise NoAMI, "No Matching AMI's for prefix #{ami_prefix}" if images.empty?
38
45
  images.sort! { |a, b| DateTime.parse(a.creation_date) <=> DateTime.parse(b.creation_date) }.last.image_id
@@ -1,3 +1,4 @@
1
+ require 'securerandom'
1
2
  require 'tempfile'
2
3
  require 'colorize'
3
4
  require 'cfndsl'
@@ -19,6 +20,7 @@ class Bora
19
20
  STACK_VALIDATE_SUCCESS_MESSAGE = "Template for stack '%s' is valid"
20
21
  STACK_DIFF_TEMPLATE_UNCHANGED_MESSAGE = "Template has not changed"
21
22
  STACK_DIFF_PARAMETERS_UNCHANGED_MESSAGE = "Parameters have not changed"
23
+ STACK_DIFF_NO_CHANGES_MESSAGE = "No changes will be applied"
22
24
 
23
25
  def initialize(stack_name, template_file, stack_config)
24
26
  @stack_name = stack_name
@@ -41,7 +43,8 @@ class Bora
41
43
 
42
44
  def apply(override_params = {}, pretty_json = false)
43
45
  cfn_options = generate(override_params, pretty_json)
44
- success = invoke_action(@cfn_stack.exists? ? "update" : "create", cfn_options)
46
+ action = @cfn_stack.exists? ? "update" : "create"
47
+ success = invoke_action(action.capitalize, action, cfn_options)
45
48
  if success
46
49
  outputs = @cfn_stack.outputs
47
50
  if outputs && outputs.length > 0
@@ -53,13 +56,14 @@ class Bora
53
56
  end
54
57
 
55
58
  def delete
56
- invoke_action("delete")
59
+ invoke_action("Delete", "delete")
57
60
  end
58
61
 
59
62
  def diff(override_params = {}, context_lines = 3)
60
63
  cfn_options = generate(override_params)
61
64
  diff_parameters(cfn_options)
62
- diff_template(override_params, context_lines, cfn_options)
65
+ diff_template(context_lines, cfn_options)
66
+ diff_change_set(cfn_options)
63
67
  end
64
68
 
65
69
  def events
@@ -109,7 +113,7 @@ class Bora
109
113
 
110
114
  def recreate(override_params = {})
111
115
  cfn_options = generate(override_params)
112
- invoke_action("recreate", cfn_options)
116
+ invoke_action("Recreate", "recreate", cfn_options)
113
117
  end
114
118
 
115
119
  def show(override_params = {})
@@ -133,6 +137,32 @@ class Bora
133
137
  is_valid
134
138
  end
135
139
 
140
+ def create_change_set(change_set_name, description = nil, override_params = {}, pretty_json = false)
141
+ puts "Creating change set '#{change_set_name}' for stack '#{@cfn_stack_name}' in region #{@region}"
142
+ cfn_options = generate(override_params, pretty_json)
143
+ cfn_options[:description] = description if description
144
+ change_set = @cfn_stack.create_change_set(change_set_name, cfn_options)
145
+ puts change_set
146
+ change_set
147
+ end
148
+
149
+ def list_change_sets
150
+ puts @cfn_stack.list_change_sets.map(&:to_s).join("\n")
151
+ end
152
+
153
+ def describe_change_set(change_set_name)
154
+ puts @cfn_stack.describe_change_set(change_set_name)
155
+ end
156
+
157
+ def delete_change_set(change_set_name)
158
+ @cfn_stack.delete_change_set(change_set_name)
159
+ puts "Deleted change set '#{change_set_name}' for stack '#{@cfn_stack_name}' in region #{@region}"
160
+ end
161
+
162
+ def execute_change_set(change_set_name)
163
+ invoke_action("Execute change set '#{change_set_name}'", "execute_change_set", change_set_name)
164
+ end
165
+
136
166
  def resolved_params(override_params = {})
137
167
  params = @stack_config['params'] || {}
138
168
  params.merge!(override_params)
@@ -192,7 +222,7 @@ class Bora
192
222
  params
193
223
  end
194
224
 
195
- def diff_template(override_params, context_lines, cfn_options)
225
+ def diff_template(context_lines, cfn_options)
196
226
  diff = Diffy::Diff.new(get_current_template, get_new_template(cfn_options),
197
227
  context: context_lines,
198
228
  include_diff_info: true)
@@ -217,6 +247,24 @@ class Bora
217
247
  puts
218
248
  end
219
249
 
250
+ def diff_change_set(cfn_options)
251
+ change_set_name = "cs-#{SecureRandom.uuid}"
252
+ if @cfn_stack.exists?
253
+ change_set = @cfn_stack.create_change_set(change_set_name, cfn_options)
254
+ @cfn_stack.delete_change_set(change_set_name)
255
+ if change_set.has_changes?
256
+ puts "Changes".colorize(mode: :bold)
257
+ puts "-------"
258
+ puts change_set.to_s(changes_only: true)
259
+ puts
260
+ else
261
+ puts "Changes".colorize(mode: :bold)
262
+ puts "-------"
263
+ puts STACK_DIFF_NO_CHANGES_MESSAGE
264
+ end
265
+ end
266
+ end
267
+
220
268
  def generate(override_params = {}, pretty_json = false)
221
269
  cfn_options = cfn_options_from_stack_config
222
270
  params = resolved_params(override_params)
@@ -242,16 +290,16 @@ class Bora
242
290
  cfn_options
243
291
  end
244
292
 
245
- def invoke_action(action, *args)
246
- puts "#{action.capitalize} stack '#{@cfn_stack_name}' in region #{@region}"
293
+ def invoke_action(action_desc, action, *args)
294
+ puts "#{action_desc} stack '#{@cfn_stack_name}' in region #{@region}"
247
295
  success = @cfn_stack.send(action, *args) { |event| puts event }
248
296
  if success
249
- puts STACK_ACTION_SUCCESS_MESSAGE % [action.capitalize, @cfn_stack_name]
297
+ puts STACK_ACTION_SUCCESS_MESSAGE % [action_desc, @cfn_stack_name]
250
298
  else
251
299
  if success == nil
252
- puts STACK_ACTION_NOT_CHANGED_MESSAGE % [action.capitalize, @cfn_stack_name]
300
+ puts STACK_ACTION_NOT_CHANGED_MESSAGE % [action_desc, @cfn_stack_name]
253
301
  else
254
- raise(STACK_ACTION_FAILURE_MESSAGE % [action.capitalize, @cfn_stack_name])
302
+ raise(STACK_ACTION_FAILURE_MESSAGE % [action_desc, @cfn_stack_name])
255
303
  end
256
304
  end
257
305
  success
@@ -1,3 +1,3 @@
1
1
  class Bora
2
- VERSION = "1.4.1"
2
+ VERSION = "1.5.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bora
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Blaxland
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-02 00:00:00.000000000 Z
11
+ date: 2016-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '1.11'
111
+ - !ruby/object:Gem::Dependency
112
+ name: hashie
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 3.4.6
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 3.4.6
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rspec
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -156,6 +170,9 @@ files:
156
170
  - bora.gemspec
157
171
  - exe/bora
158
172
  - lib/bora.rb
173
+ - lib/bora/cfn/change.rb
174
+ - lib/bora/cfn/change_set.rb
175
+ - lib/bora/cfn/change_set_action.rb
159
176
  - lib/bora/cfn/event.rb
160
177
  - lib/bora/cfn/output.rb
161
178
  - lib/bora/cfn/parameter.rb
@@ -163,6 +180,8 @@ files:
163
180
  - lib/bora/cfn/stack_status.rb
164
181
  - lib/bora/cfn/status.rb
165
182
  - lib/bora/cli.rb
183
+ - lib/bora/cli_base.rb
184
+ - lib/bora/cli_change_set.rb
166
185
  - lib/bora/parameter_resolver.rb
167
186
  - lib/bora/parameter_resolver_loader.rb
168
187
  - lib/bora/resolver/ami.rb