stack_master 0.0.1
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 +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +208 -0
- data/Rakefile +11 -0
- data/apply_demo.gif +0 -0
- data/bin/stack_master +16 -0
- data/example/simple/Gemfile +3 -0
- data/example/simple/parameters/myapp_vpc.yml +1 -0
- data/example/simple/parameters/myapp_web.yml +2 -0
- data/example/simple/stack_master.yml +13 -0
- data/example/simple/templates/myapp_vpc.rb +39 -0
- data/example/simple/templates/myapp_web.rb +16 -0
- data/features/apply.feature +241 -0
- data/features/delete.feature +43 -0
- data/features/diff.feature +191 -0
- data/features/events.feature +38 -0
- data/features/init.feature +6 -0
- data/features/outputs.feature +49 -0
- data/features/region_aliases.feature +66 -0
- data/features/resources.feature +45 -0
- data/features/stack_defaults.feature +88 -0
- data/features/status.feature +124 -0
- data/features/step_definitions/stack_steps.rb +50 -0
- data/features/support/env.rb +14 -0
- data/lib/stack_master.rb +81 -0
- data/lib/stack_master/aws_driver/cloud_formation.rb +56 -0
- data/lib/stack_master/cli.rb +164 -0
- data/lib/stack_master/command.rb +13 -0
- data/lib/stack_master/commands/apply.rb +104 -0
- data/lib/stack_master/commands/delete.rb +53 -0
- data/lib/stack_master/commands/diff.rb +31 -0
- data/lib/stack_master/commands/events.rb +39 -0
- data/lib/stack_master/commands/init.rb +109 -0
- data/lib/stack_master/commands/list_stacks.rb +16 -0
- data/lib/stack_master/commands/outputs.rb +27 -0
- data/lib/stack_master/commands/resources.rb +33 -0
- data/lib/stack_master/commands/status.rb +47 -0
- data/lib/stack_master/commands/validate.rb +17 -0
- data/lib/stack_master/config.rb +86 -0
- data/lib/stack_master/ctrl_c.rb +4 -0
- data/lib/stack_master/parameter_loader.rb +17 -0
- data/lib/stack_master/parameter_resolver.rb +45 -0
- data/lib/stack_master/parameter_resolvers/secret.rb +42 -0
- data/lib/stack_master/parameter_resolvers/security_group.rb +20 -0
- data/lib/stack_master/parameter_resolvers/sns_topic_name.rb +29 -0
- data/lib/stack_master/parameter_resolvers/stack_output.rb +53 -0
- data/lib/stack_master/prompter.rb +14 -0
- data/lib/stack_master/security_group_finder.rb +29 -0
- data/lib/stack_master/sns_topic_finder.rb +27 -0
- data/lib/stack_master/stack.rb +96 -0
- data/lib/stack_master/stack_definition.rb +49 -0
- data/lib/stack_master/stack_differ.rb +80 -0
- data/lib/stack_master/stack_events/fetcher.rb +45 -0
- data/lib/stack_master/stack_events/presenter.rb +27 -0
- data/lib/stack_master/stack_events/streamer.rb +55 -0
- data/lib/stack_master/stack_states.rb +34 -0
- data/lib/stack_master/template_compiler.rb +21 -0
- data/lib/stack_master/test_driver/cloud_formation.rb +139 -0
- data/lib/stack_master/testing.rb +7 -0
- data/lib/stack_master/utils.rb +31 -0
- data/lib/stack_master/validator.rb +25 -0
- data/lib/stack_master/version.rb +3 -0
- data/logo.png +0 -0
- data/script/buildkite/bundle.sh +5 -0
- data/script/buildkite/clean.sh +3 -0
- data/script/buildkite_rspec.sh +27 -0
- data/spec/fixtures/parameters/myapp_vpc.yml +1 -0
- data/spec/fixtures/stack_master.yml +35 -0
- data/spec/fixtures/templates/myapp_vpc.json +1 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/stack_master/commands/apply_spec.rb +92 -0
- data/spec/stack_master/commands/delete_spec.rb +40 -0
- data/spec/stack_master/commands/init_spec.rb +17 -0
- data/spec/stack_master/commands/status_spec.rb +38 -0
- data/spec/stack_master/commands/validate_spec.rb +26 -0
- data/spec/stack_master/config_spec.rb +81 -0
- data/spec/stack_master/parameter_loader_spec.rb +81 -0
- data/spec/stack_master/parameter_resolver_spec.rb +58 -0
- data/spec/stack_master/parameter_resolvers/secret_spec.rb +66 -0
- data/spec/stack_master/parameter_resolvers/security_group_spec.rb +17 -0
- data/spec/stack_master/parameter_resolvers/sns_topic_name_spec.rb +43 -0
- data/spec/stack_master/parameter_resolvers/stack_output_spec.rb +77 -0
- data/spec/stack_master/security_group_finder_spec.rb +49 -0
- data/spec/stack_master/sns_topic_finder_spec.rb +25 -0
- data/spec/stack_master/stack_definition_spec.rb +37 -0
- data/spec/stack_master/stack_differ_spec.rb +34 -0
- data/spec/stack_master/stack_events/fetcher_spec.rb +65 -0
- data/spec/stack_master/stack_events/presenter_spec.rb +18 -0
- data/spec/stack_master/stack_events/streamer_spec.rb +33 -0
- data/spec/stack_master/stack_spec.rb +157 -0
- data/spec/stack_master/template_compiler_spec.rb +48 -0
- data/spec/stack_master/test_driver/cloud_formation_spec.rb +24 -0
- data/spec/stack_master/utils_spec.rb +30 -0
- data/spec/stack_master/validator_spec.rb +38 -0
- data/stack_master.gemspec +38 -0
- data/stacktemplates/parameter_region.yml +3 -0
- data/stacktemplates/parameter_stack_name.yml +3 -0
- data/stacktemplates/stack.json.erb +20 -0
- data/stacktemplates/stack_master.yml.erb +6 -0
- metadata +427 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
class StackDefinition
|
|
3
|
+
include Virtus.value_object(strict: true, required: false)
|
|
4
|
+
|
|
5
|
+
values do
|
|
6
|
+
attribute :region, String
|
|
7
|
+
attribute :stack_name, String
|
|
8
|
+
attribute :template, String
|
|
9
|
+
attribute :tags, Hash
|
|
10
|
+
attribute :notification_arns, Array[String]
|
|
11
|
+
attribute :base_dir, String
|
|
12
|
+
attribute :secret_file, String
|
|
13
|
+
attribute :stack_policy_file, String
|
|
14
|
+
attribute :additional_parameter_lookup_dirs, Array[String]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def template_file_path
|
|
18
|
+
File.join(base_dir, 'templates', template)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parameter_files
|
|
22
|
+
[ default_parameter_file_path, region_parameter_file_path ] + additional_parameter_lookup_file_paths
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def stack_policy_file_path
|
|
26
|
+
File.join(base_dir, 'policies', stack_policy_file) if stack_policy_file
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def additional_parameter_lookup_file_paths
|
|
32
|
+
additional_parameter_lookup_dirs.map do |a|
|
|
33
|
+
File.join(base_dir, 'parameters', a, "#{underscored_stack_name}.yml")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def region_parameter_file_path
|
|
38
|
+
File.join(base_dir, 'parameters', "#{region}", "#{underscored_stack_name}.yml")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def default_parameter_file_path
|
|
42
|
+
File.join(base_dir, 'parameters', "#{underscored_stack_name}.yml")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def underscored_stack_name
|
|
46
|
+
stack_name.gsub('-', '_')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
class StackDiffer
|
|
3
|
+
def initialize(proposed_stack, current_stack)
|
|
4
|
+
@proposed_stack = proposed_stack
|
|
5
|
+
@current_stack = current_stack
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def proposed_template
|
|
9
|
+
JSON.pretty_generate(JSON.parse(@proposed_stack.template_body))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def current_template
|
|
13
|
+
JSON.pretty_generate(@current_stack.template_hash)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def current_parameters
|
|
17
|
+
YAML.dump(sort_params(@current_stack.parameters_with_defaults))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def proposed_parameters
|
|
21
|
+
YAML.dump(sort_params(@proposed_stack.parameters_with_defaults))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def body_different?
|
|
25
|
+
Diffy::Diff.new(current_template, proposed_template, {}).to_s != ''
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def params_different?
|
|
29
|
+
Diffy::Diff.new(current_parameters, proposed_parameters, {}).to_s != ''
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def output_diff
|
|
33
|
+
if @current_stack
|
|
34
|
+
text_diff('Stack', current_template, proposed_template, context: 7, include_diff_info: true)
|
|
35
|
+
text_diff('Parameters', current_parameters, proposed_parameters)
|
|
36
|
+
else
|
|
37
|
+
text_diff('Stack', '', proposed_template)
|
|
38
|
+
text_diff('Parameters', '', proposed_parameters)
|
|
39
|
+
StackMaster.stdout.puts "No stack found"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def text_diff(thing, current, proposed, diff_opts = {})
|
|
46
|
+
diff = Diffy::Diff.new(current, proposed, diff_opts).to_s
|
|
47
|
+
StackMaster.stdout.print "#{thing} diff: "
|
|
48
|
+
if diff == ''
|
|
49
|
+
StackMaster.stdout.puts "No changes"
|
|
50
|
+
else
|
|
51
|
+
StackMaster.stdout.puts
|
|
52
|
+
diff.each_line do |line|
|
|
53
|
+
if line.start_with?('+')
|
|
54
|
+
StackMaster.stdout.print colorize(line, :green)
|
|
55
|
+
elsif line.start_with?('-')
|
|
56
|
+
StackMaster.stdout.print colorize(line, :red)
|
|
57
|
+
else
|
|
58
|
+
StackMaster.stdout.print line
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def sort_params(hash)
|
|
65
|
+
hash.sort.to_h
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def colorize(text, color)
|
|
69
|
+
if colorize?
|
|
70
|
+
text.colorize(color)
|
|
71
|
+
else
|
|
72
|
+
text
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def colorize?
|
|
77
|
+
ENV.fetch('COLORIZE') { 'true' } == 'true'
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
class StackEvents
|
|
3
|
+
class Fetcher
|
|
4
|
+
def self.fetch(*args)
|
|
5
|
+
new(*args).fetch
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def initialize(stack_name, region, from: nil)
|
|
9
|
+
@stack_name = stack_name
|
|
10
|
+
@region = region
|
|
11
|
+
@from = from
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def fetch
|
|
15
|
+
events = fetch_events
|
|
16
|
+
if @from
|
|
17
|
+
filter_old_events(events)
|
|
18
|
+
else
|
|
19
|
+
events
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def cf
|
|
26
|
+
@cf ||= StackMaster.cloud_formation_driver
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def filter_old_events(events)
|
|
30
|
+
events.select { |event| event.timestamp > @from }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fetch_events
|
|
34
|
+
events = []
|
|
35
|
+
next_token = nil
|
|
36
|
+
begin
|
|
37
|
+
response = cf.describe_stack_events(stack_name: @stack_name, next_token: next_token)
|
|
38
|
+
next_token = response.next_token
|
|
39
|
+
events += response.stack_events
|
|
40
|
+
end while !next_token.nil?
|
|
41
|
+
events.reverse
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
class StackEvents
|
|
3
|
+
class Presenter
|
|
4
|
+
def self.print_event(io, event)
|
|
5
|
+
new(io).print_event(event)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def initialize(io)
|
|
9
|
+
@io = io
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def print_event(event)
|
|
13
|
+
@io.puts "#{event.timestamp.localtime} #{event.logical_resource_id} #{event.resource_type} #{event.resource_status} #{event.resource_status_reason}".colorize(event_colour(event))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def event_colour(event)
|
|
17
|
+
if StackStates.failure_state?(event.resource_status)
|
|
18
|
+
:red
|
|
19
|
+
elsif StackStates.success_state?(event.resource_status)
|
|
20
|
+
:green
|
|
21
|
+
else
|
|
22
|
+
:yellow
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
class StackEvents
|
|
3
|
+
class Streamer
|
|
4
|
+
def self.stream(*args, &block)
|
|
5
|
+
new(*args, &block).stream
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def initialize(stack_name, region, from: Time.now, break_on_finish_state: true, sleep_between_fetches: 1, io: nil, &block)
|
|
9
|
+
@stack_name = stack_name
|
|
10
|
+
@region = region
|
|
11
|
+
@block = block
|
|
12
|
+
@seen_events = Set.new
|
|
13
|
+
@from = from
|
|
14
|
+
@break_on_finish_state = break_on_finish_state
|
|
15
|
+
@sleep_between_fetches = sleep_between_fetches
|
|
16
|
+
@io = io
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def stream
|
|
20
|
+
catch(:halt) do
|
|
21
|
+
loop do
|
|
22
|
+
events = Fetcher.fetch(@stack_name, @region, from: @from)
|
|
23
|
+
unseen_events(events).each do |event|
|
|
24
|
+
@block.call(event) if @block
|
|
25
|
+
Presenter.print_event(@io, event) if @io
|
|
26
|
+
if @break_on_finish_state && finish_state?(event)
|
|
27
|
+
throw :halt
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
sleep @sleep_between_fetches
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
rescue Interrupt
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def unseen_events(events)
|
|
39
|
+
[].tap do |unseen_events|
|
|
40
|
+
events.each do |event|
|
|
41
|
+
next if @seen_events.include?(event.event_id)
|
|
42
|
+
@seen_events << event.event_id
|
|
43
|
+
unseen_events << event
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def finish_state?(event)
|
|
49
|
+
StackStates.finish_state?(event.resource_status) &&
|
|
50
|
+
event.resource_type == 'AWS::CloudFormation::Stack' &&
|
|
51
|
+
event.logical_resource_id == @stack_name
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
module StackStates
|
|
3
|
+
SUCCESS_STATES = %w[
|
|
4
|
+
CREATE_COMPLETE
|
|
5
|
+
UPDATE_COMPLETE
|
|
6
|
+
DELETE_COMPLETE
|
|
7
|
+
].freeze
|
|
8
|
+
FAILURE_STATES = %w[
|
|
9
|
+
CREATE_FAILED
|
|
10
|
+
DELETE_FAILED
|
|
11
|
+
UPDATE_ROLLBACK_FAILED
|
|
12
|
+
ROLLBACK_FAILED
|
|
13
|
+
ROLLBACK_COMPLETE
|
|
14
|
+
ROLLBACK_FAILED
|
|
15
|
+
UPDATE_ROLLBACK_COMPLETE
|
|
16
|
+
UPDATE_ROLLBACK_FAILED
|
|
17
|
+
].freeze
|
|
18
|
+
FINISH_STATES = (SUCCESS_STATES + FAILURE_STATES).freeze
|
|
19
|
+
|
|
20
|
+
extend self
|
|
21
|
+
|
|
22
|
+
def finish_state?(state)
|
|
23
|
+
FINISH_STATES.include?(state)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def failure_state?(state)
|
|
27
|
+
FAILURE_STATES.include?(state)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def success_state?(state)
|
|
31
|
+
SUCCESS_STATES.include?(state)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
class TemplateCompiler
|
|
3
|
+
|
|
4
|
+
MAX_TEMPLATE_SIZE = 51200
|
|
5
|
+
|
|
6
|
+
def self.compile(template_file_path)
|
|
7
|
+
if template_file_path.ends_with?('.rb')
|
|
8
|
+
SparkleFormation.sparkle_path = File.dirname(template_file_path)
|
|
9
|
+
JSON.pretty_generate(SparkleFormation.compile(template_file_path))
|
|
10
|
+
else
|
|
11
|
+
template_body = File.read(template_file_path)
|
|
12
|
+
if template_body.size > MAX_TEMPLATE_SIZE
|
|
13
|
+
# Parse the json and rewrite compressed
|
|
14
|
+
JSON.dump(JSON.parse(template_body))
|
|
15
|
+
else
|
|
16
|
+
template_body
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module StackMaster
|
|
2
|
+
module TestDriver
|
|
3
|
+
class Stack
|
|
4
|
+
include Virtus.model
|
|
5
|
+
attribute :stack_id, String
|
|
6
|
+
attribute :stack_name, String
|
|
7
|
+
attribute :description, String
|
|
8
|
+
attribute :parameters, Array[OpenStruct]
|
|
9
|
+
attribute :creation_time, String
|
|
10
|
+
attribute :last_update_time, String
|
|
11
|
+
attribute :stack_status, String
|
|
12
|
+
attribute :stack_status_reason, String
|
|
13
|
+
attribute :disable_rollback, String
|
|
14
|
+
attribute :notification_arns, Array
|
|
15
|
+
attribute :timeout_in_minutes, Integer
|
|
16
|
+
attribute :capabilities, Array
|
|
17
|
+
attribute :outputs, Array[OpenStruct]
|
|
18
|
+
attribute :tags, Array[OpenStruct]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class StackEvent
|
|
22
|
+
include Virtus.model
|
|
23
|
+
attribute :stack_id, String
|
|
24
|
+
attribute :event_id, String
|
|
25
|
+
attribute :stack_name, String
|
|
26
|
+
attribute :logical_resource_id, String
|
|
27
|
+
attribute :physical_resource_id, String
|
|
28
|
+
attribute :resource_type, String
|
|
29
|
+
attribute :timestamp, Time
|
|
30
|
+
attribute :resource_status, String
|
|
31
|
+
attribute :resource_status_reason, String
|
|
32
|
+
attribute :resource_properties, String
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class StackResource
|
|
36
|
+
include Virtus.model
|
|
37
|
+
attribute :stack_name, String
|
|
38
|
+
attribute :stack_id, String
|
|
39
|
+
attribute :logical_resource_id, String
|
|
40
|
+
attribute :physical_resource_id, String
|
|
41
|
+
attribute :resource_type, String
|
|
42
|
+
attribute :timestamp, Time
|
|
43
|
+
attribute :resource_status, String
|
|
44
|
+
attribute :resource_status_reason, String
|
|
45
|
+
attribute :description, String
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class CloudFormation
|
|
49
|
+
def initialize
|
|
50
|
+
reset
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def set_region(region)
|
|
54
|
+
@region = region
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def reset
|
|
58
|
+
@stacks = {}
|
|
59
|
+
@templates = {}
|
|
60
|
+
@stack_events = {}
|
|
61
|
+
@stack_resources = {}
|
|
62
|
+
@stack_policies = {}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def describe_stacks(options = {})
|
|
66
|
+
stack_name = options[:stack_name]
|
|
67
|
+
stacks = if stack_name
|
|
68
|
+
if @stacks[stack_name]
|
|
69
|
+
[@stacks[stack_name]]
|
|
70
|
+
else
|
|
71
|
+
raise Aws::CloudFormation::Errors::ValidationError.new('', 'Stack does not exist')
|
|
72
|
+
end
|
|
73
|
+
else
|
|
74
|
+
@stacks.values
|
|
75
|
+
end
|
|
76
|
+
OpenStruct.new(stacks: stacks, next_token: nil)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def describe_stack_resources(options = {})
|
|
80
|
+
@stacks.fetch(options.fetch(:stack_name)) { raise Aws::CloudFormation::Errors::ValidationError.new('', 'Stack does not exist') }
|
|
81
|
+
OpenStruct.new(stack_resources: @stack_resources[options.fetch(:stack_name)])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def get_template(options)
|
|
85
|
+
template_body = @templates[options[:stack_name]] || nil
|
|
86
|
+
OpenStruct.new(template_body: template_body)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def get_stack_policy(options)
|
|
90
|
+
OpenStruct.new(stack_policy_body: @stack_policies[options.fetch(:stack_name)])
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def describe_stack_events(options)
|
|
94
|
+
events = @stack_events[options.fetch(:stack_name)] || []
|
|
95
|
+
OpenStruct.new(stack_events: events, next_token: nil)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def update_stack(options)
|
|
99
|
+
stack_name = options.fetch(:stack_name)
|
|
100
|
+
@stacks[stack_name].attributes = options
|
|
101
|
+
@stack_policies[stack_name] = options[:stack_policy_body]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def create_stack(options)
|
|
105
|
+
stack_name = options.fetch(:stack_name)
|
|
106
|
+
add_stack(options)
|
|
107
|
+
@stack_policies[stack_name] = options[:stack_policy_body]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def delete_stack(options)
|
|
111
|
+
stack_name = options.fetch(:stack_name)
|
|
112
|
+
@stacks.delete(stack_name)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def validate_template(options)
|
|
116
|
+
true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def add_stack(stack)
|
|
120
|
+
@stacks[stack.fetch(:stack_name)] = Stack.new(stack)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def add_stack_resource(options)
|
|
124
|
+
@stack_resources[options.fetch(:stack_name)] ||= []
|
|
125
|
+
@stack_resources[options.fetch(:stack_name)] << StackResource.new(options)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def set_template(stack_name, template)
|
|
129
|
+
@templates[stack_name] = template
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def add_stack_event(event)
|
|
133
|
+
stack_name = event.fetch(:stack_name)
|
|
134
|
+
@stack_events[stack_name] ||= []
|
|
135
|
+
@stack_events[stack_name] << StackEvent.new(event)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|