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 +4 -4
- data/README.md +22 -2
- data/bora.gemspec +1 -0
- data/lib/bora/cfn/change.rb +17 -0
- data/lib/bora/cfn/change_set.rb +45 -0
- data/lib/bora/cfn/change_set_action.rb +36 -0
- data/lib/bora/cfn/stack.rb +39 -10
- data/lib/bora/cfn/status.rb +1 -1
- data/lib/bora/cli.rb +6 -28
- data/lib/bora/cli_base.rb +43 -0
- data/lib/bora/cli_change_set.rb +40 -0
- data/lib/bora/resolver/ami.rb +23 -16
- data/lib/bora/stack.rb +58 -10
- data/lib/bora/version.rb +1 -1
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff4f553135f201ba7386c5b97464b2cf23931c85
|
4
|
+
data.tar.gz: 78c79cc5fd9755d085b6f28ea6c8233a967fc0c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/bora.gemspec
CHANGED
@@ -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
|
data/lib/bora/cfn/stack.rb
CHANGED
@@ -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(:
|
23
|
+
call_cfn_action(:create_stack, options, &block)
|
23
24
|
end
|
24
25
|
|
25
26
|
def update(options, &block)
|
26
|
-
call_cfn_action(:
|
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(:
|
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 == :
|
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(
|
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 == :
|
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
|
data/lib/bora/cfn/status.rb
CHANGED
data/lib/bora/cli.rb
CHANGED
@@ -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 <
|
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
|
-
|
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
|
data/lib/bora/resolver/ami.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
data/lib/bora/stack.rb
CHANGED
@@ -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
|
-
|
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(
|
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(
|
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 "#{
|
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 % [
|
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 % [
|
300
|
+
puts STACK_ACTION_NOT_CHANGED_MESSAGE % [action_desc, @cfn_stack_name]
|
253
301
|
else
|
254
|
-
raise(STACK_ACTION_FAILURE_MESSAGE % [
|
302
|
+
raise(STACK_ACTION_FAILURE_MESSAGE % [action_desc, @cfn_stack_name])
|
255
303
|
end
|
256
304
|
end
|
257
305
|
success
|
data/lib/bora/version.rb
CHANGED
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
|
+
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-
|
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
|