stack_master 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,17 @@
|
|
1
|
+
module StackMaster
|
2
|
+
module Commands
|
3
|
+
class Validate
|
4
|
+
include Command
|
5
|
+
include Commander::UI
|
6
|
+
|
7
|
+
def initialize(config, stack_definition)
|
8
|
+
@config = config
|
9
|
+
@stack_definition = stack_definition
|
10
|
+
end
|
11
|
+
|
12
|
+
def perform
|
13
|
+
Validator.perform(@stack_definition)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'deep_merge/rails_compat'
|
2
|
+
require 'active_support/core_ext/object/deep_dup'
|
3
|
+
|
4
|
+
module StackMaster
|
5
|
+
class Config
|
6
|
+
def self.load!(config_file = 'stack_master.yml')
|
7
|
+
config = YAML.load(File.read(config_file))
|
8
|
+
base_dir = File.dirname(File.expand_path(config_file))
|
9
|
+
new(config, base_dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :stacks,
|
13
|
+
:base_dir,
|
14
|
+
:stack_defaults,
|
15
|
+
:region_defaults,
|
16
|
+
:region_aliases
|
17
|
+
|
18
|
+
def initialize(config, base_dir)
|
19
|
+
@config = config
|
20
|
+
@base_dir = base_dir
|
21
|
+
@stack_defaults = config.fetch('stack_defaults', {})
|
22
|
+
@region_aliases = Utils.underscore_keys_to_hyphen(config.fetch('region_aliases', {}))
|
23
|
+
@region_to_aliases = @region_aliases.inject({}) do |hash, (key, value)|
|
24
|
+
hash[value] ||= []
|
25
|
+
hash[value] << key
|
26
|
+
hash
|
27
|
+
end
|
28
|
+
@region_defaults = normalise_region_defaults(config.fetch('region_defaults', {}))
|
29
|
+
@stacks = []
|
30
|
+
load_config
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_stack(region, stack_name)
|
34
|
+
@stacks.find do |s|
|
35
|
+
(s.region == region || s.region == region.gsub('_', '-')) &&
|
36
|
+
(s.stack_name == stack_name || s.stack_name == stack_name.gsub('_', '-'))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def unalias_region(region)
|
41
|
+
@region_aliases.fetch(region) { region }
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def load_config
|
47
|
+
unaliased_stacks = resolve_region_aliases(@config.fetch('stacks'))
|
48
|
+
load_stacks(unaliased_stacks)
|
49
|
+
end
|
50
|
+
|
51
|
+
def resolve_region_aliases(stacks)
|
52
|
+
stacks.inject({}) do |hash, (region, attributes)|
|
53
|
+
hash[unalias_region(region)] = attributes
|
54
|
+
hash
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_stacks(stacks)
|
59
|
+
stacks.each do |region, stacks_for_region|
|
60
|
+
region = Utils.underscore_to_hyphen(region)
|
61
|
+
stacks_for_region.each do |stack_name, attributes|
|
62
|
+
stack_name = Utils.underscore_to_hyphen(stack_name)
|
63
|
+
stack_attributes = build_stack_defaults(region).deeper_merge(attributes).merge(
|
64
|
+
'region' => region,
|
65
|
+
'stack_name' => stack_name,
|
66
|
+
'base_dir' => @base_dir,
|
67
|
+
'additional_parameter_lookup_dirs' => @region_to_aliases[region])
|
68
|
+
@stacks << StackDefinition.new(stack_attributes)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def build_stack_defaults(region)
|
74
|
+
region_defaults = @region_defaults.fetch(region, {}).deep_dup
|
75
|
+
@stack_defaults.deep_dup.deeper_merge(region_defaults)
|
76
|
+
end
|
77
|
+
|
78
|
+
def normalise_region_defaults(region_defaults)
|
79
|
+
region_defaults.inject({}) do |normalised_aliases, (region_or_alias, value)|
|
80
|
+
region = unalias_region(region_or_alias)
|
81
|
+
normalised_aliases[Utils.underscore_to_hyphen(region)] = value
|
82
|
+
normalised_aliases
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module StackMaster
|
2
|
+
class ParameterLoader
|
3
|
+
def self.load(parameter_files)
|
4
|
+
parameter_files.reduce({}) do |hash, file_name|
|
5
|
+
parameters = if File.exists?(file_name)
|
6
|
+
YAML.load(File.read(file_name)) || {}
|
7
|
+
else
|
8
|
+
{}
|
9
|
+
end
|
10
|
+
parameters.each do |key, value|
|
11
|
+
hash[key.camelize] = value
|
12
|
+
end
|
13
|
+
hash
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module StackMaster
|
2
|
+
class ParameterResolver
|
3
|
+
ResolverNotFound = Class.new(StandardError)
|
4
|
+
InvalidParameter = Class.new(StandardError)
|
5
|
+
|
6
|
+
def self.resolve(config, stack_definition, parameters)
|
7
|
+
new(config, stack_definition, parameters).resolve
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(config, stack_definition, parameters)
|
11
|
+
@config = config
|
12
|
+
@stack_definition = stack_definition
|
13
|
+
@parameters = parameters
|
14
|
+
@resolvers = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def resolve
|
18
|
+
@parameters.reduce({}) do |parameters, (key, value)|
|
19
|
+
parameters[key] = resolve_parameter_value(value)
|
20
|
+
parameters
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def resolve_parameter_value(parameter_value)
|
27
|
+
return parameter_value if String === parameter_value || parameter_value.nil?
|
28
|
+
raise InvalidParameter, parameter_value unless Hash === parameter_value
|
29
|
+
raise InvalidParameter, parameter_value unless parameter_value.keys.size == 1
|
30
|
+
resolver_class_name = parameter_value.keys.first.to_s.camelize
|
31
|
+
value = parameter_value.values.first
|
32
|
+
resolver_class(resolver_class_name).resolve(value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def resolver_class(class_name)
|
36
|
+
@resolvers.fetch(class_name) do
|
37
|
+
begin
|
38
|
+
@resolvers[class_name] = Kernel.const_get("StackMaster::ParameterResolvers::#{class_name}").new(@config, @stack_definition)
|
39
|
+
rescue NameError
|
40
|
+
raise ResolverNotFound, "Could not find parameter resolver called #{class_name}, please double check your configuration"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module StackMaster
|
2
|
+
module ParameterResolvers
|
3
|
+
class Secret
|
4
|
+
SecretNotFound = Class.new(StandardError)
|
5
|
+
|
6
|
+
def initialize(config, stack_definition)
|
7
|
+
@config = config
|
8
|
+
@stack_definition = stack_definition
|
9
|
+
end
|
10
|
+
|
11
|
+
def resolve(value)
|
12
|
+
secret_key = value
|
13
|
+
raise ArgumentError, "No secret_file defined for stack definition #{@stack_definition.stack_name} in #{@stack_definition.region}" unless !@stack_definition.secret_file.nil?
|
14
|
+
raise ArgumentError, "Could not find secret file at #{secret_file_path}" unless File.exist?(secret_file_path)
|
15
|
+
secrets_hash.fetch(secret_key) do
|
16
|
+
raise SecretNotFound, "Unable to find key #{secret_key} in file #{secret_file_path}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def secrets_hash
|
23
|
+
@secrets_hash ||= YAML.load(decrypt_with_dotgpg)
|
24
|
+
end
|
25
|
+
|
26
|
+
def decrypt_with_dotgpg
|
27
|
+
dir = Dotgpg::Dir.closest(secret_file_path)
|
28
|
+
stream = StringIO.new
|
29
|
+
dir.decrypt(secret_path_relative_to_base, stream)
|
30
|
+
stream.string
|
31
|
+
end
|
32
|
+
|
33
|
+
def secret_path_relative_to_base
|
34
|
+
@secret_path_relative_to_base ||= File.join('secrets', @stack_definition.secret_file)
|
35
|
+
end
|
36
|
+
|
37
|
+
def secret_file_path
|
38
|
+
@secret_file_path ||= File.join(@config.base_dir, secret_path_relative_to_base)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module StackMaster
|
2
|
+
module ParameterResolvers
|
3
|
+
class SecurityGroup
|
4
|
+
def initialize(config, stack_definition)
|
5
|
+
@config = config
|
6
|
+
@stack_definition = stack_definition
|
7
|
+
end
|
8
|
+
|
9
|
+
def resolve(value)
|
10
|
+
security_group_finder.find(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def security_group_finder
|
16
|
+
StackMaster::SecurityGroupFinder.new(@stack_definition.region)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module StackMaster
|
2
|
+
module ParameterResolvers
|
3
|
+
class SnsTopicName
|
4
|
+
TopicNotFound = Class.new(StandardError)
|
5
|
+
|
6
|
+
def initialize(config, stack_definition)
|
7
|
+
@config = config
|
8
|
+
@stack_definition = stack_definition
|
9
|
+
@stacks = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve(value)
|
13
|
+
sns_topic_finder.find(value)
|
14
|
+
rescue StackMaster::SnsTopicFinder::TopicNotFound => e
|
15
|
+
raise TopicNotFound.new(e.message)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def cf
|
21
|
+
@cf ||= StackMaster.cloud_formation_driver
|
22
|
+
end
|
23
|
+
|
24
|
+
def sns_topic_finder
|
25
|
+
StackMaster::SnsTopicFinder.new(@stack_definition.region)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module StackMaster
|
2
|
+
module ParameterResolvers
|
3
|
+
class StackOutput
|
4
|
+
StackNotFound = Class.new(StandardError)
|
5
|
+
StackOutputNotFound = Class.new(StandardError)
|
6
|
+
|
7
|
+
def initialize(config, stack_definition)
|
8
|
+
@config = config
|
9
|
+
@stack_definition = stack_definition
|
10
|
+
@stacks = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def resolve(value)
|
14
|
+
validate_value!(value)
|
15
|
+
stack_name, output_name = value.split('/')
|
16
|
+
stack = find_stack(stack_name)
|
17
|
+
if stack
|
18
|
+
output = stack.outputs.find { |output| output.output_key == output_name.camelize }
|
19
|
+
if output
|
20
|
+
output.output_value
|
21
|
+
else
|
22
|
+
raise StackOutputNotFound, "Stack exists (#{stack_name}), but output does not: #{output_name}"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
raise StackNotFound, "Stack in StackOutput not found: #{value}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def cf
|
32
|
+
@cf ||= StackMaster.cloud_formation_driver
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_value!(value)
|
36
|
+
if !value.is_a?(String) || !value.include?('/')
|
37
|
+
raise ArgumentError, 'Stack output values must be in the form of stack-name/output-name'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_stack(stack_name)
|
42
|
+
@stacks.fetch(stack_name) do
|
43
|
+
cf_stack = cf.describe_stacks(stack_name: stack_name).stacks.first
|
44
|
+
@stacks[stack_name] = cf_stack
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def cf
|
49
|
+
@cf ||= StackMaster.cloud_formation_driver
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module StackMaster
|
2
|
+
class SecurityGroupFinder
|
3
|
+
SecurityGroupNotFound = Class.new(StandardError)
|
4
|
+
MultipleSecurityGroupsFound = Class.new(StandardError)
|
5
|
+
|
6
|
+
def initialize(region)
|
7
|
+
@resource = Aws::EC2::Resource.new(region: region)
|
8
|
+
end
|
9
|
+
|
10
|
+
def find(reference)
|
11
|
+
STDERR.puts "Resolving security group reference '#{reference}'"
|
12
|
+
raise ArgumentError, 'Security group references must be non-empty strings' unless reference.is_a?(String) && !reference.empty?
|
13
|
+
|
14
|
+
groups = @resource.security_groups({
|
15
|
+
filters: [
|
16
|
+
{
|
17
|
+
name: "group-name",
|
18
|
+
values: [reference],
|
19
|
+
},
|
20
|
+
],
|
21
|
+
})
|
22
|
+
|
23
|
+
raise SecurityGroupNotFound, "No security group with name #{reference} found" unless groups.any?
|
24
|
+
raise MultipleSecurityGroupsFound, "More than one security group with name #{reference} found" if groups.count > 1
|
25
|
+
|
26
|
+
groups.first.id
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module StackMaster
|
2
|
+
class SnsTopicFinder
|
3
|
+
TopicNotFound = Class.new(StandardError)
|
4
|
+
|
5
|
+
def initialize(region)
|
6
|
+
@resource = Aws::SNS::Resource.new(region: region)
|
7
|
+
end
|
8
|
+
|
9
|
+
def find(reference)
|
10
|
+
$stderr.puts "Resolving SNS topic reference '#{reference}'"
|
11
|
+
raise ArgumentError, 'SNS topic references must be non-empty strings' unless reference.is_a?(String) && !reference.empty?
|
12
|
+
|
13
|
+
topic = @resource.topics.detect { |t| topic_name_from_arn(t.arn) == reference }
|
14
|
+
|
15
|
+
raise TopicNotFound, "No topic with name #{reference} found" unless topic
|
16
|
+
|
17
|
+
topic.arn
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def topic_name_from_arn(arn)
|
23
|
+
arn.split(":")[5]
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module StackMaster
|
2
|
+
class Stack
|
3
|
+
MAX_TEMPLATE_SIZE = 51200
|
4
|
+
|
5
|
+
include Virtus.model
|
6
|
+
|
7
|
+
attribute :stack_name, String
|
8
|
+
attribute :region, String
|
9
|
+
attribute :stack_id, String
|
10
|
+
attribute :stack_status, String
|
11
|
+
attribute :parameters, Hash
|
12
|
+
attribute :template_body, String
|
13
|
+
attribute :notification_arns, Array[String]
|
14
|
+
attribute :outputs, Array
|
15
|
+
attribute :stack_policy_body, String
|
16
|
+
attribute :tags, Hash
|
17
|
+
|
18
|
+
def template_hash
|
19
|
+
if template_body
|
20
|
+
@template_hash ||= JSON.parse(template_body)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def maybe_compressed_template_body
|
25
|
+
if template_body.size > MAX_TEMPLATE_SIZE
|
26
|
+
@compressed_template_body ||= JSON.dump(template_hash)
|
27
|
+
else
|
28
|
+
template_body
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def template_default_parameters
|
33
|
+
template_hash.fetch('Parameters', {}).inject({}) do |result, (parameter_name, description)|
|
34
|
+
result[parameter_name] = description['Default']
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def parameters_with_defaults
|
40
|
+
template_default_parameters.merge(parameters)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.find(region, stack_name)
|
44
|
+
cf = StackMaster.cloud_formation_driver
|
45
|
+
cf_stack = cf.describe_stacks(stack_name: stack_name).stacks.first
|
46
|
+
return unless cf_stack
|
47
|
+
parameters = cf_stack.parameters.inject({}) do |params_hash, param_struct|
|
48
|
+
params_hash[param_struct.parameter_key] = param_struct.parameter_value
|
49
|
+
params_hash
|
50
|
+
end
|
51
|
+
template_body ||= cf.get_template(stack_name: stack_name).template_body
|
52
|
+
stack_policy_body ||= cf.get_stack_policy(stack_name: stack_name).stack_policy_body
|
53
|
+
outputs = cf_stack.outputs
|
54
|
+
|
55
|
+
new(region: region,
|
56
|
+
stack_name: stack_name,
|
57
|
+
stack_id: cf_stack.stack_id,
|
58
|
+
parameters: parameters,
|
59
|
+
template_body: template_body,
|
60
|
+
outputs: outputs,
|
61
|
+
notification_arns: cf_stack.notification_arns,
|
62
|
+
stack_policy_body: stack_policy_body,
|
63
|
+
stack_status: cf_stack.stack_status)
|
64
|
+
rescue Aws::CloudFormation::Errors::ValidationError
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.generate(stack_definition, config)
|
69
|
+
parameter_hash = ParameterLoader.load(stack_definition.parameter_files)
|
70
|
+
template_body = TemplateCompiler.compile(stack_definition.template_file_path)
|
71
|
+
parameters = ParameterResolver.resolve(config, stack_definition, parameter_hash)
|
72
|
+
stack_policy_body = if stack_definition.stack_policy_file_path
|
73
|
+
File.read(stack_definition.stack_policy_file_path)
|
74
|
+
end
|
75
|
+
new(region: stack_definition.region,
|
76
|
+
stack_name: stack_definition.stack_name,
|
77
|
+
tags: stack_definition.tags,
|
78
|
+
parameters: parameters,
|
79
|
+
template_body: template_body,
|
80
|
+
notification_arns: stack_definition.notification_arns,
|
81
|
+
stack_policy_body: stack_policy_body)
|
82
|
+
end
|
83
|
+
|
84
|
+
def too_big?
|
85
|
+
maybe_compressed_template_body.size > MAX_TEMPLATE_SIZE
|
86
|
+
end
|
87
|
+
|
88
|
+
def aws_parameters
|
89
|
+
Utils.hash_to_aws_parameters(parameters)
|
90
|
+
end
|
91
|
+
|
92
|
+
def aws_tags
|
93
|
+
Utils.hash_to_aws_tags(tags)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|