bora 0.9.4 → 1.0.0.beta1
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 +144 -115
- data/bora.gemspec +1 -0
- data/exe/bora +5 -0
- data/lib/bora.rb +41 -4
- data/lib/bora/cfn/event.rb +35 -0
- data/lib/bora/cfn/output.rb +24 -0
- data/lib/bora/cfn/stack.rb +168 -0
- data/lib/bora/cfn/stack_status.rb +33 -0
- data/lib/bora/cfn/status.rb +43 -0
- data/lib/bora/cfn_param_resolver.rb +3 -3
- data/lib/bora/cli.rb +81 -0
- data/lib/bora/stack.rb +125 -118
- data/lib/bora/stack_tasks.rb +132 -0
- data/lib/bora/tasks.rb +8 -3
- data/lib/bora/template.rb +37 -0
- data/lib/bora/version.rb +2 -2
- metadata +29 -10
- data/lib/bora/event.rb +0 -31
- data/lib/bora/output.rb +0 -20
- data/lib/bora/rake_tasks.rb +0 -107
- data/lib/bora/stack_status.rb +0 -29
- data/lib/bora/status.rb +0 -41
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'aws-sdk'
|
4
|
+
require 'diffy'
|
5
|
+
require 'bora/cfn/stack_status'
|
6
|
+
require 'bora/cfn/event'
|
7
|
+
require 'bora/cfn/output'
|
8
|
+
|
9
|
+
class Bora
|
10
|
+
module Cfn
|
11
|
+
|
12
|
+
class Stack
|
13
|
+
NO_UPDATE_MESSAGE = "No updates are to be performed"
|
14
|
+
|
15
|
+
def initialize(stack_name)
|
16
|
+
@stack_name = stack_name
|
17
|
+
@processed_events = Set.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def create(options, &block)
|
21
|
+
call_cfn_action(:create, options, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def update(options, &block)
|
25
|
+
call_cfn_action(:update, options, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_or_update(options, &block)
|
29
|
+
exists? ? update(options, &block) : create(options, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def recreate(options, &block)
|
33
|
+
delete(&block) if exists?
|
34
|
+
create(options, &block) if !exists?
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(&block)
|
38
|
+
call_cfn_action(:delete, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def events
|
42
|
+
return if !exists?
|
43
|
+
events = cloudformation.describe_stack_events({stack_name: underlying_stack.stack_id}).stack_events
|
44
|
+
events.reverse.map { |e| Event.new(e) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def outputs
|
48
|
+
return if !exists?
|
49
|
+
underlying_stack.outputs.map { |output| Output.new(output) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def template(pretty = true)
|
53
|
+
return if !exists?
|
54
|
+
template = cloudformation.get_template({stack_name: @stack_name}).template_body
|
55
|
+
template = JSON.pretty_generate(JSON.parse(template)) if pretty
|
56
|
+
template
|
57
|
+
end
|
58
|
+
|
59
|
+
def new_template(options, pretty = true)
|
60
|
+
options = resolve_options(options, true)
|
61
|
+
template = options[:template_body]
|
62
|
+
if template
|
63
|
+
template = JSON.pretty_generate(JSON.parse(template)) if pretty
|
64
|
+
template
|
65
|
+
else
|
66
|
+
raise "new_template not yet implemented for URL #{options[:template_url]}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def diff(options)
|
71
|
+
Diffy::Diff.new(template, new_template(options))
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate(options)
|
75
|
+
cloudformation.validate_template(resolve_options(options).select { |k| [:template_body, :template_url].include?(k) })
|
76
|
+
end
|
77
|
+
|
78
|
+
def status
|
79
|
+
StackStatus.new(underlying_stack)
|
80
|
+
end
|
81
|
+
|
82
|
+
def exists?
|
83
|
+
status.exists?
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# =============================================================================================
|
88
|
+
private
|
89
|
+
|
90
|
+
def cloudformation
|
91
|
+
@cfn ||= Aws::CloudFormation::Client.new
|
92
|
+
end
|
93
|
+
|
94
|
+
def method_missing(sym, *args, &block)
|
95
|
+
underlying_stack ? underlying_stack.send(sym, *args, &block) : nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def call_cfn_action(action, options = {}, &block)
|
99
|
+
underlying_stack(refresh: true)
|
100
|
+
return true if action == :delete && !exists?
|
101
|
+
@previous_event_time = last_event_time
|
102
|
+
begin
|
103
|
+
action_options = {stack_name: @stack_name}.merge(resolve_options(options))
|
104
|
+
cloudformation.method("#{action.to_s.downcase}_stack").call(action_options)
|
105
|
+
wait_for_completion(&block)
|
106
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
107
|
+
raise e unless e.message.include?(NO_UPDATE_MESSAGE)
|
108
|
+
return nil
|
109
|
+
end
|
110
|
+
(action == :delete && !underlying_stack) || status.success?
|
111
|
+
end
|
112
|
+
|
113
|
+
def resolve_options(options, load_all = false)
|
114
|
+
return options if options[:template_body] || !options[:template_url]
|
115
|
+
uri = URI(options[:template_url])
|
116
|
+
if uri.scheme != "s3" || load_all
|
117
|
+
resolved_options = options.clone
|
118
|
+
resolved_options[:template_body] = open(options[:template_url]).read
|
119
|
+
resolved_options.delete(:template_url)
|
120
|
+
resolved_options
|
121
|
+
else
|
122
|
+
options
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def wait_for_completion
|
127
|
+
begin
|
128
|
+
events = unprocessed_events
|
129
|
+
events.each { |e| yield e } if block_given?
|
130
|
+
finished = events.find do |e|
|
131
|
+
e.resource_type == 'AWS::CloudFormation::Stack' && e.logical_resource_id == @stack_name && e.status_complete?
|
132
|
+
end
|
133
|
+
sleep 10 unless finished
|
134
|
+
end until finished
|
135
|
+
underlying_stack(refresh: true)
|
136
|
+
end
|
137
|
+
|
138
|
+
def underlying_stack(refresh: false)
|
139
|
+
if !@_stack || refresh
|
140
|
+
begin
|
141
|
+
response = cloudformation.describe_stacks({stack_name: @stack_name})
|
142
|
+
@_stack = response.stacks[0]
|
143
|
+
rescue Aws::CloudFormation::Errors::ValidationError
|
144
|
+
@_stack = nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
@_stack
|
148
|
+
end
|
149
|
+
|
150
|
+
def unprocessed_events
|
151
|
+
return [] if !underlying_stack
|
152
|
+
events = cloudformation.describe_stack_events({stack_name: underlying_stack.stack_id}).stack_events
|
153
|
+
unprocessed_events = events.select do |event|
|
154
|
+
!@processed_events.include?(event.event_id) && @previous_event_time < event.timestamp
|
155
|
+
end
|
156
|
+
@processed_events.merge(unprocessed_events.map(&:event_id))
|
157
|
+
unprocessed_events.reverse.map { |e| Event.new(e) }
|
158
|
+
end
|
159
|
+
|
160
|
+
def last_event_time
|
161
|
+
return Time.at(0) if !underlying_stack
|
162
|
+
events = cloudformation.describe_stack_events({stack_name: @stack_name}).stack_events
|
163
|
+
events.length > 0 ? events[0].timestamp : Time.at(0)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'bora/cfn/status'
|
2
|
+
|
3
|
+
class Bora
|
4
|
+
module Cfn
|
5
|
+
|
6
|
+
class StackStatus
|
7
|
+
def initialize(underlying_stack)
|
8
|
+
@stack = underlying_stack
|
9
|
+
if @stack
|
10
|
+
@status = Status.new(@stack.stack_status)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def exists?
|
15
|
+
@status && !@status.deleted?
|
16
|
+
end
|
17
|
+
|
18
|
+
def success?
|
19
|
+
@status && @status.success?
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
if @stack
|
24
|
+
status_reason = @stack.stack_status_reason ? " - #{@stack.stack_status_reason}" : ""
|
25
|
+
"#{@stack.stack_name} - #{@status}#{status_reason}"
|
26
|
+
else
|
27
|
+
"Stack does not exist"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
class Bora
|
4
|
+
module Cfn
|
5
|
+
class Status
|
6
|
+
def initialize(status)
|
7
|
+
@status = status
|
8
|
+
end
|
9
|
+
|
10
|
+
def success?
|
11
|
+
@status.end_with?("_COMPLETE") && !@status.include?("ROLLBACK")
|
12
|
+
end
|
13
|
+
|
14
|
+
def failure?
|
15
|
+
@status.end_with?("_FAILED") || @status.include?("ROLLBACK")
|
16
|
+
end
|
17
|
+
|
18
|
+
def deleted?
|
19
|
+
@status == "DELETE_COMPLETE"
|
20
|
+
end
|
21
|
+
|
22
|
+
def complete?
|
23
|
+
success? || failure?
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
@status.colorize(color)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def color
|
34
|
+
case
|
35
|
+
when success?; :green
|
36
|
+
when failure?; :red
|
37
|
+
else; :yellow;
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require 'bora/stack'
|
1
|
+
require 'bora/cfn/stack'
|
2
2
|
|
3
|
-
|
3
|
+
class Bora
|
4
4
|
class CfnParamResolver
|
5
5
|
def initialize(param)
|
6
6
|
@param = param
|
@@ -12,7 +12,7 @@ module Bora
|
|
12
12
|
raise "Invalid parameter substitution: #{@param}"
|
13
13
|
end
|
14
14
|
|
15
|
-
stack = Stack.new(stack_name)
|
15
|
+
stack = Cfn::Stack.new(stack_name)
|
16
16
|
if !stack.exists?
|
17
17
|
raise "Output #{name} not found in stack #{stack_name} as the stack does not exist"
|
18
18
|
end
|
data/lib/bora/cli.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "bora"
|
3
|
+
|
4
|
+
class Bora
|
5
|
+
class Cli < Thor
|
6
|
+
class_option :file, type: :string, aliases: :f, default: Bora::DEFAULT_CONFIG_FILE, desc: "The Bora config file to use"
|
7
|
+
|
8
|
+
desc "apply STACK_NAME", "Creates or updates the stack"
|
9
|
+
option :params, type: :array, aliases: :p, desc: "Parameters to be passed to the template, eg: --params 'instance_type=t2.micro'"
|
10
|
+
def apply(stack_name)
|
11
|
+
stack(options.file, stack_name).apply(params)
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "delete STACK_NAME", "Deletes the stack"
|
15
|
+
def delete(stack_name)
|
16
|
+
stack(options.file, stack_name).delete
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "diff STACK_NAME", "Diffs the new template with the stack's current template"
|
20
|
+
option :params, type: :array, aliases: :p, desc: "Parameters to be passed to the template, eg: --params 'instance_type=t2.micro'"
|
21
|
+
def diff(stack_name)
|
22
|
+
stack(options.file, stack_name).diff(params)
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "events STACK_NAME", "Outputs the latest events from the stack"
|
26
|
+
def events(stack_name)
|
27
|
+
stack(options.file, stack_name).events
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "outputs STACK_NAME", "Shows the outputs from the stack"
|
31
|
+
def outputs(stack_name)
|
32
|
+
stack(options.file, stack_name).outputs
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "recreate STACK_NAME", "Recreates (deletes then creates) the stack"
|
36
|
+
option :params, type: :array, aliases: :p, desc: "Parameters to be passed to the template, eg: --params 'instance_type=t2.micro'"
|
37
|
+
def recreate(stack_name)
|
38
|
+
stack(options.file, stack_name).recreate(params)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "show STACK_NAME", "Shows the new template for stack"
|
42
|
+
option :params, type: :array, aliases: :p, desc: "Parameters to be passed to the template, eg: --params 'instance_type=t2.micro'"
|
43
|
+
def show(stack_name)
|
44
|
+
stack(options.file, stack_name).show(params)
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "show_current STACK_NAME", "Shows the current template for the stack"
|
48
|
+
def show_current(stack_name)
|
49
|
+
stack(options.file, stack_name).show_current
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "status STACK_NAME", "Displays the current status of the stack"
|
53
|
+
def status(stack_name)
|
54
|
+
stack(options.file, stack_name).status
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "validate STACK_NAME", "Checks the stack's template for validity"
|
58
|
+
option :params, type: :array, aliases: :p, desc: "Parameters to be passed to the template, eg: --params 'instance_type=t2.micro'"
|
59
|
+
def validate(stack_name)
|
60
|
+
stack(options.file, stack_name).validate(params)
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def stack(config_file, stack_name)
|
67
|
+
bora = Bora.new(config_file_or_hash: config_file)
|
68
|
+
stack = bora.stack(stack_name)
|
69
|
+
if !stack
|
70
|
+
STDERR.puts "Could not find stack #{stack_name}"
|
71
|
+
exit(1)
|
72
|
+
end
|
73
|
+
stack
|
74
|
+
end
|
75
|
+
|
76
|
+
def params
|
77
|
+
options.params ? Hash[options.params.map { |param| param.split("=", 2) }] : {}
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/lib/bora/stack.rb
CHANGED
@@ -1,164 +1,171 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require 'bora/
|
6
|
-
require 'bora/
|
7
|
-
|
8
|
-
|
9
|
-
module Bora
|
1
|
+
require 'tempfile'
|
2
|
+
require 'colorize'
|
3
|
+
require 'cfndsl'
|
4
|
+
require 'bora/cfn/stack'
|
5
|
+
require 'bora/cfn_param_resolver'
|
6
|
+
require 'bora/stack_tasks'
|
7
|
+
|
8
|
+
class Bora
|
10
9
|
class Stack
|
11
|
-
|
12
|
-
|
13
|
-
def initialize(stack_name)
|
10
|
+
def initialize(stack_name, template_file, stack_config)
|
14
11
|
@stack_name = stack_name
|
15
|
-
@
|
12
|
+
@cfn_stack_name = stack_config['stack_name'] || @stack_name
|
13
|
+
@template_file = template_file
|
14
|
+
@stack_config = stack_config
|
15
|
+
@cfn_options = extract_cfn_options(stack_config)
|
16
|
+
@cfn_stack = Cfn::Stack.new(@cfn_stack_name)
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
call_cfn_action(:create, options, &block)
|
20
|
-
end
|
19
|
+
attr_reader :stack_name
|
21
20
|
|
22
|
-
def
|
23
|
-
|
21
|
+
def rake_tasks
|
22
|
+
StackTasks.new(self)
|
24
23
|
end
|
25
24
|
|
26
|
-
def
|
27
|
-
|
25
|
+
def apply(override_params = {})
|
26
|
+
generate(override_params)
|
27
|
+
success = invoke_action(@cfn_stack.exists? ? "update" : "create", @cfn_options)
|
28
|
+
if success
|
29
|
+
outputs = @cfn_stack.outputs
|
30
|
+
if outputs && outputs.length > 0
|
31
|
+
puts "Stack outputs"
|
32
|
+
outputs.each { |output| puts output }
|
33
|
+
end
|
34
|
+
end
|
28
35
|
end
|
29
36
|
|
30
|
-
def
|
31
|
-
delete
|
32
|
-
create(options, &block) if !exists?
|
37
|
+
def delete
|
38
|
+
invoke_action("delete")
|
33
39
|
end
|
34
40
|
|
35
|
-
def
|
36
|
-
|
41
|
+
def diff(override_params = {})
|
42
|
+
generate(override_params)
|
43
|
+
puts @cfn_stack.diff(@cfn_options).to_s(String.disable_colorization ? :text : :color)
|
37
44
|
end
|
38
45
|
|
39
46
|
def events
|
40
|
-
|
41
|
-
events
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
47
|
+
events = @cfn_stack.events
|
48
|
+
if events
|
49
|
+
if events.length > 0
|
50
|
+
puts "Events for stack '#{@cfn_stack_name}'"
|
51
|
+
@cfn_stack.events.each { |e| puts e }
|
52
|
+
else
|
53
|
+
puts "Stack '#{@cfn_stack_name}' has no events"
|
54
|
+
end
|
55
|
+
else
|
56
|
+
puts "Stack '#{@cfn_stack_name}' does not exist"
|
57
|
+
end
|
48
58
|
end
|
49
59
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
60
|
+
def generate(override_params = {})
|
61
|
+
params = process_params(override_params)
|
62
|
+
if File.extname(@template_file) == ".rb"
|
63
|
+
template_body = run_cfndsl(@template_file, params)
|
64
|
+
template_json = JSON.parse(template_body)
|
65
|
+
if template_json["Parameters"]
|
66
|
+
cfn_param_keys = template_json["Parameters"].keys
|
67
|
+
cfn_params = params.select { |k, v| cfn_param_keys.include?(k) }.map do |k, v|
|
68
|
+
{ parameter_key: k, parameter_value: v }
|
69
|
+
end
|
70
|
+
@cfn_options[:parameters] = cfn_params if !cfn_params.empty?
|
71
|
+
end
|
72
|
+
@cfn_options[:template_body] = template_body
|
73
|
+
else
|
74
|
+
@cfn_options[:template_url] = @template_file
|
75
|
+
if !params.empty?
|
76
|
+
@cfn_options[:parameters] = params.map do |k, v|
|
77
|
+
{ parameter_key: k, parameter_value: v }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
55
81
|
end
|
56
82
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
83
|
+
def outputs
|
84
|
+
outputs = @cfn_stack.outputs
|
85
|
+
if outputs
|
86
|
+
if outputs.length > 0
|
87
|
+
puts "Outputs for stack '#{@cfn_stack_name}'"
|
88
|
+
outputs.each { |output| puts output }
|
89
|
+
else
|
90
|
+
puts "Stack '#{@cfn_stack_name}' has no outputs"
|
91
|
+
end
|
63
92
|
else
|
64
|
-
|
93
|
+
puts "Stack '#{@cfn_stack_name}' does not exist"
|
65
94
|
end
|
66
95
|
end
|
67
96
|
|
68
|
-
def
|
69
|
-
|
97
|
+
def recreate(override_params = {})
|
98
|
+
generate(override_params)
|
99
|
+
invoke_action("recreate", @cfn_options)
|
70
100
|
end
|
71
101
|
|
72
|
-
def
|
73
|
-
|
102
|
+
def show(override_params = {})
|
103
|
+
generate(override_params)
|
104
|
+
puts @cfn_stack.new_template(@cfn_options)
|
74
105
|
end
|
75
106
|
|
76
|
-
def
|
77
|
-
|
107
|
+
def show_current
|
108
|
+
template = @cfn_stack.template
|
109
|
+
puts template ? template : "Stack '#{@cfn_stack_name}' does not exist"
|
78
110
|
end
|
79
111
|
|
80
|
-
def
|
81
|
-
status
|
112
|
+
def status
|
113
|
+
puts @cfn_stack.status
|
82
114
|
end
|
83
115
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
def cloudformation
|
89
|
-
@cfn ||= Aws::CloudFormation::Client.new
|
116
|
+
def validate(override_params = {})
|
117
|
+
generate(override_params)
|
118
|
+
puts "Template for stack '#{@cfn_stack_name}' is valid" if @cfn_stack.validate(@cfn_options)
|
90
119
|
end
|
91
120
|
|
92
|
-
def method_missing(sym, *args, &block)
|
93
|
-
underlying_stack ? underlying_stack.send(sym, *args, &block) : nil
|
94
|
-
end
|
95
121
|
|
96
|
-
|
97
|
-
underlying_stack(refresh: true)
|
98
|
-
return true if action == :delete && !exists?
|
99
|
-
@previous_event_time = last_event_time
|
100
|
-
begin
|
101
|
-
action_options = {stack_name: @stack_name}.merge(resolve_options(options))
|
102
|
-
cloudformation.method("#{action.to_s.downcase}_stack").call(action_options)
|
103
|
-
wait_for_completion(&block)
|
104
|
-
rescue Aws::CloudFormation::Errors::ValidationError => e
|
105
|
-
raise e unless e.message.include?(NO_UPDATE_MESSAGE)
|
106
|
-
return nil
|
107
|
-
end
|
108
|
-
(action == :delete && !underlying_stack) || status.success?
|
109
|
-
end
|
122
|
+
protected
|
110
123
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
if
|
115
|
-
|
116
|
-
resolved_options[:template_body] = open(options[:template_url]).read
|
117
|
-
resolved_options.delete(:template_url)
|
118
|
-
resolved_options
|
124
|
+
def invoke_action(action, *args)
|
125
|
+
puts "#{action.capitalize} stack '#{@cfn_stack_name}'"
|
126
|
+
success = @cfn_stack.send(action, *args) { |event| puts event }
|
127
|
+
if success
|
128
|
+
puts "#{action.capitalize} stack '#{@cfn_stack_name}' completed successfully"
|
119
129
|
else
|
120
|
-
|
130
|
+
if success == nil
|
131
|
+
puts "#{action.capitalize} stack '#{@cfn_stack_name}' skipped as template has not changed"
|
132
|
+
else
|
133
|
+
raise("#{action.capitalize} stack '#{@cfn_stack_name}' failed")
|
134
|
+
end
|
121
135
|
end
|
136
|
+
success
|
122
137
|
end
|
123
138
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
sleep 10 unless finished
|
132
|
-
end until finished
|
133
|
-
underlying_stack(refresh: true)
|
134
|
-
end
|
135
|
-
|
136
|
-
def underlying_stack(refresh: false)
|
137
|
-
if !@_stack || refresh
|
138
|
-
begin
|
139
|
-
response = cloudformation.describe_stacks({stack_name: @stack_name})
|
140
|
-
@_stack = response.stacks[0]
|
141
|
-
rescue Aws::CloudFormation::Errors::ValidationError
|
142
|
-
@_stack = nil
|
143
|
-
end
|
144
|
-
end
|
145
|
-
@_stack
|
139
|
+
def run_cfndsl(template_file, params)
|
140
|
+
temp_extras = Tempfile.new("bora")
|
141
|
+
temp_extras.write(params.to_yaml)
|
142
|
+
temp_extras.close
|
143
|
+
template_body = CfnDsl.eval_file_with_extras(template_file, [[:yaml, temp_extras.path]]).to_json
|
144
|
+
temp_extras.unlink
|
145
|
+
template_body
|
146
146
|
end
|
147
147
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
148
|
+
def process_params(override_params)
|
149
|
+
params = @stack_config['params'] || {}
|
150
|
+
params.merge!(override_params) if override_params
|
151
|
+
params.map { |k, v| [k, process_param_substitutions(v)] }.to_h
|
152
|
+
end
|
153
|
+
|
154
|
+
def process_param_substitutions(val)
|
155
|
+
old_val = nil
|
156
|
+
while old_val != val
|
157
|
+
old_val = val
|
158
|
+
val = val.sub(/\${[^}]+}/) do |m|
|
159
|
+
token = m[2..-2]
|
160
|
+
CfnParamResolver.new(token).resolve
|
161
|
+
end
|
153
162
|
end
|
154
|
-
|
155
|
-
unprocessed_events.reverse.map { |e| Event.new(e) }
|
163
|
+
val
|
156
164
|
end
|
157
165
|
|
158
|
-
def
|
159
|
-
|
160
|
-
|
161
|
-
events.length > 0 ? events[0].timestamp : Time.at(0)
|
166
|
+
def extract_cfn_options(config)
|
167
|
+
valid_options = ["capabilities"]
|
168
|
+
config.select { |k| valid_options.include?(k) }
|
162
169
|
end
|
163
170
|
|
164
171
|
end
|