bora 1.4.1 → 1.5.0

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
  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