stack_master 2.17.0 → 2.18.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 +1 -6
- data/bin/stack_master +3 -5
- data/lib/stack_master/aws_driver/cloud_formation.rb +25 -23
- data/lib/stack_master/aws_driver/s3.rb +24 -17
- data/lib/stack_master/change_set.rb +11 -15
- data/lib/stack_master/cli.rb +53 -32
- data/lib/stack_master/command.rb +1 -5
- data/lib/stack_master/commands/apply.rb +39 -20
- data/lib/stack_master/commands/delete.rb +6 -7
- data/lib/stack_master/commands/drift.rb +11 -12
- data/lib/stack_master/commands/events.rb +4 -6
- data/lib/stack_master/commands/init.rb +21 -20
- data/lib/stack_master/commands/lint.rb +1 -1
- data/lib/stack_master/commands/list_stacks.rb +1 -1
- data/lib/stack_master/commands/nag.rb +0 -1
- data/lib/stack_master/commands/outputs.rb +1 -1
- data/lib/stack_master/commands/resources.rb +9 -1
- data/lib/stack_master/commands/status.rb +4 -4
- data/lib/stack_master/commands/terminal_helper.rb +3 -3
- data/lib/stack_master/commands/tidy.rb +14 -13
- data/lib/stack_master/config.rb +23 -21
- data/lib/stack_master/diff.rb +2 -2
- data/lib/stack_master/identity.rb +2 -1
- data/lib/stack_master/parameter_loader.rb +3 -5
- data/lib/stack_master/parameter_resolver.rb +18 -18
- data/lib/stack_master/parameter_resolvers/acm_certificate.rb +4 -1
- data/lib/stack_master/parameter_resolvers/ami_finder.rb +2 -3
- data/lib/stack_master/parameter_resolvers/ejson.rb +9 -6
- data/lib/stack_master/parameter_resolvers/env.rb +1 -2
- data/lib/stack_master/parameter_resolvers/latest_ami_by_tags.rb +1 -1
- data/lib/stack_master/parameter_resolvers/latest_container.rb +9 -7
- data/lib/stack_master/parameter_resolvers/one_password.rb +11 -7
- data/lib/stack_master/parameter_resolvers/parameter_store.rb +1 -5
- data/lib/stack_master/parameter_resolvers/security_group.rb +1 -1
- data/lib/stack_master/parameter_resolvers/sso_group_id.rb +3 -2
- data/lib/stack_master/parameter_resolvers/stack_output.rb +7 -9
- data/lib/stack_master/parameter_validator.rb +2 -5
- data/lib/stack_master/prompter.rb +11 -10
- data/lib/stack_master/resolver_array.rb +2 -3
- data/lib/stack_master/role_assumer.rb +7 -5
- data/lib/stack_master/security_group_finder.rb +7 -5
- data/lib/stack_master/sns_topic_finder.rb +4 -3
- data/lib/stack_master/sparkle_formation/compile_time/allowed_pattern_validator.rb +2 -3
- data/lib/stack_master/sparkle_formation/compile_time/allowed_values_validator.rb +2 -3
- data/lib/stack_master/sparkle_formation/compile_time/definitions_validator.rb +6 -6
- data/lib/stack_master/sparkle_formation/compile_time/empty_validator.rb +0 -2
- data/lib/stack_master/sparkle_formation/compile_time/max_length_validator.rb +1 -2
- data/lib/stack_master/sparkle_formation/compile_time/max_size_validator.rb +1 -2
- data/lib/stack_master/sparkle_formation/compile_time/min_length_validator.rb +2 -3
- data/lib/stack_master/sparkle_formation/compile_time/min_size_validator.rb +2 -3
- data/lib/stack_master/sparkle_formation/compile_time/number_validator.rb +1 -2
- data/lib/stack_master/sparkle_formation/compile_time/parameters_validator.rb +0 -1
- data/lib/stack_master/sparkle_formation/compile_time/state_builder.rb +0 -2
- data/lib/stack_master/sparkle_formation/compile_time/string_validator.rb +2 -3
- data/lib/stack_master/sparkle_formation/compile_time/value_builder.rb +7 -9
- data/lib/stack_master/sparkle_formation/compile_time/value_validator.rb +3 -6
- data/lib/stack_master/sparkle_formation/compile_time/value_validator_factory.rb +11 -13
- data/lib/stack_master/sparkle_formation/template_file.rb +2 -4
- data/lib/stack_master/sso_group_id_finder.rb +15 -12
- data/lib/stack_master/stack.rb +51 -18
- data/lib/stack_master/stack_definition.rb +6 -5
- data/lib/stack_master/stack_differ.rb +36 -9
- data/lib/stack_master/stack_events/fetcher.rb +3 -1
- data/lib/stack_master/stack_events/presenter.rb +6 -1
- data/lib/stack_master/stack_events/streamer.rb +3 -1
- data/lib/stack_master/stack_status.rb +1 -0
- data/lib/stack_master/template_compilers/json.rb +1 -1
- data/lib/stack_master/template_compilers/sparkle_formation.rb +12 -9
- data/lib/stack_master/template_utils.rb +8 -4
- data/lib/stack_master/test_driver/cloud_formation.rb +34 -9
- data/lib/stack_master/test_driver/s3.rb +2 -3
- data/lib/stack_master/utils.rb +4 -6
- data/lib/stack_master/validator.rb +7 -6
- data/lib/stack_master/version.rb +1 -1
- data/lib/stack_master.rb +3 -1
- metadata +21 -10
- data/lib/stack_master/parameter_resolvers/accounts_by_tags.rb +0 -60
|
@@ -6,16 +6,17 @@ module StackMaster
|
|
|
6
6
|
include Command
|
|
7
7
|
include Commander::UI
|
|
8
8
|
|
|
9
|
-
DETECTION_COMPLETE_STATES = [
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
DETECTION_COMPLETE_STATES = %w[
|
|
10
|
+
DETECTION_COMPLETE
|
|
11
|
+
DETECTION_FAILED
|
|
12
12
|
]
|
|
13
13
|
|
|
14
14
|
def perform
|
|
15
15
|
detect_stack_drift_result = cf.detect_stack_drift(stack_name: stack_name)
|
|
16
16
|
drift_results = wait_for_drift_results(detect_stack_drift_result.stack_drift_detection_id)
|
|
17
17
|
|
|
18
|
-
puts colorize("Drift Status: #{drift_results.stack_drift_status}",
|
|
18
|
+
puts colorize("Drift Status: #{drift_results.stack_drift_status}",
|
|
19
|
+
stack_drift_status_color(drift_results.stack_drift_status))
|
|
19
20
|
return if drift_results.stack_drift_status == 'IN_SYNC'
|
|
20
21
|
|
|
21
22
|
failed
|
|
@@ -40,9 +41,7 @@ module StackMaster
|
|
|
40
41
|
drift.physical_resource_id].join(' '), color)
|
|
41
42
|
return unless drift.stack_resource_drift_status == 'MODIFIED'
|
|
42
43
|
|
|
43
|
-
unless drift.property_differences.empty?
|
|
44
|
-
puts colorize(' Property differences:', color)
|
|
45
|
-
end
|
|
44
|
+
puts colorize(' Property differences:', color) unless drift.property_differences.empty?
|
|
46
45
|
drift.property_differences.each do |property_difference|
|
|
47
46
|
puts colorize(" - #{property_difference.difference_type} #{property_difference.property_path}", color)
|
|
48
47
|
end
|
|
@@ -51,8 +50,10 @@ module StackMaster
|
|
|
51
50
|
end
|
|
52
51
|
|
|
53
52
|
def display_resource_drift(drift)
|
|
54
|
-
diff = ::StackMaster::Diff.new(
|
|
55
|
-
|
|
53
|
+
diff = ::StackMaster::Diff.new(
|
|
54
|
+
before: prettify_json(drift.expected_properties),
|
|
55
|
+
after: prettify_json(drift.actual_properties)
|
|
56
|
+
)
|
|
56
57
|
diff.display_colorized_diff
|
|
57
58
|
end
|
|
58
59
|
|
|
@@ -95,9 +96,7 @@ module StackMaster
|
|
|
95
96
|
break if DETECTION_COMPLETE_STATES.include?(resp.detection_status)
|
|
96
97
|
|
|
97
98
|
elapsed_time = Time.now - start_time
|
|
98
|
-
if elapsed_time > @options.timeout
|
|
99
|
-
raise "Timeout waiting for stack drift detection"
|
|
100
|
-
end
|
|
99
|
+
raise 'Timeout waiting for stack drift detection' if elapsed_time > @options.timeout
|
|
101
100
|
|
|
102
101
|
sleep SLEEP_SECONDS
|
|
103
102
|
end
|
|
@@ -9,9 +9,9 @@ module StackMaster
|
|
|
9
9
|
filter_events(events).each do |event|
|
|
10
10
|
StackEvents::Presenter.print_event(StackMaster.stdout, event)
|
|
11
11
|
end
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
return unless @options.tail
|
|
13
|
+
|
|
14
|
+
StackEvents::Streamer.stream(@stack_definition.stack_name, @stack_definition.region, io: StackMaster.stdout)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
private
|
|
@@ -22,9 +22,7 @@ module StackMaster
|
|
|
22
22
|
else
|
|
23
23
|
n = @options.number || 25
|
|
24
24
|
from = events.count - n
|
|
25
|
-
if from < 0
|
|
26
|
-
from = 0
|
|
27
|
-
end
|
|
25
|
+
from = 0 if from < 0
|
|
28
26
|
events[from..-1]
|
|
29
27
|
end
|
|
30
28
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'erb'
|
|
2
2
|
|
|
3
3
|
module StackMaster
|
|
4
4
|
module Commands
|
|
@@ -12,27 +12,28 @@ module StackMaster
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def perform
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
return unless check_files
|
|
16
|
+
|
|
17
|
+
create_stack_master_yml
|
|
18
|
+
create_stack_json_yml
|
|
19
|
+
create_parameters_yml
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
24
|
def check_files
|
|
25
|
-
@stack_master_filename =
|
|
25
|
+
@stack_master_filename = 'stack_master.yml'
|
|
26
26
|
@stack_json_filename = "templates/#{@stack_name}.json"
|
|
27
|
-
@parameters_filename = File.join(
|
|
28
|
-
@region_parameters_filename = File.join(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
[@stack_master_filename, @stack_json_filename, @parameters_filename,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
@parameters_filename = File.join('parameters', "#{@stack_name}.yml")
|
|
28
|
+
@region_parameters_filename = File.join('parameters', @region, "#{@stack_name}.yml")
|
|
29
|
+
|
|
30
|
+
unless @options.overwrite
|
|
31
|
+
[@stack_master_filename, @stack_json_filename, @parameters_filename,
|
|
32
|
+
@region_parameters_filename].each do |filename|
|
|
33
|
+
next unless File.exist?(filename)
|
|
34
|
+
|
|
35
|
+
StackMaster.stderr.puts("Aborting: #{filename} already exists. Use --overwrite to force overwriting file.")
|
|
36
|
+
return false
|
|
36
37
|
end
|
|
37
38
|
end
|
|
38
39
|
true
|
|
@@ -49,7 +50,7 @@ module StackMaster
|
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def stack_json_template
|
|
52
|
-
File.join(StackMaster.base_dir,
|
|
53
|
+
File.join(StackMaster.base_dir, 'stacktemplates', 'stack.json.erb')
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def create_stack_master_yml
|
|
@@ -62,7 +63,7 @@ module StackMaster
|
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def stack_master_template
|
|
65
|
-
File.join(StackMaster.base_dir,
|
|
66
|
+
File.join(StackMaster.base_dir, 'stacktemplates', 'stack_master.yml.erb')
|
|
66
67
|
end
|
|
67
68
|
|
|
68
69
|
def create_parameters_yml
|
|
@@ -82,11 +83,11 @@ module StackMaster
|
|
|
82
83
|
end
|
|
83
84
|
|
|
84
85
|
def parameter_stack_name_template
|
|
85
|
-
File.join(StackMaster.base_dir,
|
|
86
|
+
File.join(StackMaster.base_dir, 'stacktemplates', 'parameter_stack_name.yml')
|
|
86
87
|
end
|
|
87
88
|
|
|
88
89
|
def parameter_region_template
|
|
89
|
-
File.join(StackMaster.base_dir,
|
|
90
|
+
File.join(StackMaster.base_dir, 'stacktemplates', 'parameter_region.yml')
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
def render(renderer)
|
|
@@ -8,7 +8,15 @@ module StackMaster
|
|
|
8
8
|
|
|
9
9
|
def perform
|
|
10
10
|
if stack_resources
|
|
11
|
-
tp
|
|
11
|
+
tp(
|
|
12
|
+
stack_resources,
|
|
13
|
+
:logical_resource_id,
|
|
14
|
+
:resource_type,
|
|
15
|
+
:timestamp,
|
|
16
|
+
:resource_status,
|
|
17
|
+
:resource_status_reason,
|
|
18
|
+
:description
|
|
19
|
+
)
|
|
12
20
|
else
|
|
13
21
|
failed("Stack doesn't exist")
|
|
14
22
|
end
|
|
@@ -21,11 +21,11 @@ module StackMaster
|
|
|
21
21
|
{
|
|
22
22
|
region: stack_definition.region,
|
|
23
23
|
stack_name: stack_definition.stack_name,
|
|
24
|
-
stack_status: running_in_allowed_account?(allowed_accounts) ? stack_status.status :
|
|
25
|
-
different: running_in_allowed_account?(allowed_accounts) ? stack_status.changed_message :
|
|
24
|
+
stack_status: running_in_allowed_account?(allowed_accounts) ? stack_status.status : 'Disallowed account',
|
|
25
|
+
different: running_in_allowed_account?(allowed_accounts) ? stack_status.changed_message : 'N/A'
|
|
26
26
|
}
|
|
27
27
|
end
|
|
28
|
-
tp.set :max_width,
|
|
28
|
+
tp.set :max_width, window_size
|
|
29
29
|
tp.set :io, StackMaster.stdout
|
|
30
30
|
tp status
|
|
31
31
|
StackMaster.stdout.puts " * No echo parameters can't be diffed"
|
|
@@ -34,7 +34,7 @@ module StackMaster
|
|
|
34
34
|
private
|
|
35
35
|
|
|
36
36
|
def progress
|
|
37
|
-
@progress ||= ProgressBar.create(title:
|
|
37
|
+
@progress ||= ProgressBar.create(title: 'Fetching stack information',
|
|
38
38
|
total: @config.stacks.size,
|
|
39
39
|
output: StackMaster.stdout)
|
|
40
40
|
end
|
|
@@ -4,9 +4,9 @@ module StackMaster
|
|
|
4
4
|
module Commands
|
|
5
5
|
module TerminalHelper
|
|
6
6
|
def window_size
|
|
7
|
-
size = ENV.fetch(
|
|
7
|
+
size = ENV.fetch('COLUMNS') { OS.windows? ? windows_window_size : unix_window_size }
|
|
8
8
|
|
|
9
|
-
if size.nil? || size ==
|
|
9
|
+
if size.nil? || size == ''
|
|
10
10
|
80
|
|
11
11
|
else
|
|
12
12
|
size.to_i
|
|
@@ -18,7 +18,7 @@ module StackMaster
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def windows_window_size
|
|
21
|
-
columns_regex =
|
|
21
|
+
columns_regex = /^\s+Columns:\s+([0-9]+)$/
|
|
22
22
|
output = `mode con`
|
|
23
23
|
columns_line = output.split("\n").select { |line| line.match(columns_regex) }.last
|
|
24
24
|
columns_line.match(columns_regex)[1]
|
|
@@ -8,20 +8,23 @@ module StackMaster
|
|
|
8
8
|
used_templates = []
|
|
9
9
|
used_parameter_files = []
|
|
10
10
|
|
|
11
|
-
templates = Set.new(find_templates
|
|
12
|
-
parameter_files = Set.new(find_parameter_files
|
|
11
|
+
templates = Set.new(find_templates)
|
|
12
|
+
parameter_files = Set.new(find_parameter_files)
|
|
13
13
|
|
|
14
14
|
status = @config.stacks.each do |stack_definition|
|
|
15
15
|
parameter_files.subtract(stack_definition.parameter_files_from_globs)
|
|
16
16
|
template = File.absolute_path(stack_definition.template_file_path)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
templates.delete(template)
|
|
18
|
+
next unless template
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
templates.delete(template)
|
|
21
|
+
|
|
22
|
+
next if File.exist?(template)
|
|
23
|
+
|
|
24
|
+
StackMaster.stdout.puts(
|
|
25
|
+
"Stack \"#{stack_definition.stack_name}\" in \"#{stack_definition.region}\" " \
|
|
26
|
+
"missing template \"#{rel_path(template)}\""
|
|
27
|
+
)
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
templates.each do |path|
|
|
@@ -43,21 +46,19 @@ module StackMaster
|
|
|
43
46
|
# template directories) this won't find the right directory.
|
|
44
47
|
template_dir = @config.template_dir || File.join(@config.base_dir, 'templates')
|
|
45
48
|
|
|
46
|
-
templates = Dir.glob(File.absolute_path(File.join(template_dir, '**',
|
|
49
|
+
templates = Dir.glob(File.absolute_path(File.join(template_dir, '**', '*.{rb,yaml,yml,json}')))
|
|
47
50
|
dynamics_dir = File.join(template_dir, 'dynamics')
|
|
48
51
|
|
|
49
52
|
# Exclude sparkleformation dynamics
|
|
50
53
|
# TODO: Should this filter out anything with 'dynamics', not just the first
|
|
51
54
|
# subdirectory?
|
|
52
|
-
templates
|
|
55
|
+
templates.select do |path|
|
|
53
56
|
!path.start_with?(dynamics_dir)
|
|
54
57
|
end
|
|
55
|
-
|
|
56
|
-
templates
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
def find_parameter_files
|
|
60
|
-
Dir.glob(File.absolute_path(File.join(@config.base_dir,
|
|
61
|
+
Dir.glob(File.absolute_path(File.join(@config.base_dir, 'parameters', '*.{yml,yaml}')))
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
end
|
data/lib/stack_master/config.rb
CHANGED
|
@@ -14,23 +14,25 @@ module StackMaster
|
|
|
14
14
|
raise ConfigParseError, "Unable to parse #{resolved_config_file}: #{error}"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
attr_accessor
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
attr_accessor(
|
|
18
|
+
:stacks,
|
|
19
|
+
:base_dir,
|
|
20
|
+
:template_dir,
|
|
21
|
+
:parameters_dir,
|
|
22
|
+
:stack_defaults,
|
|
23
|
+
:region_defaults,
|
|
24
|
+
:region_aliases,
|
|
25
|
+
:template_compilers
|
|
26
|
+
)
|
|
25
27
|
|
|
26
28
|
def self.search_up_and_chdir(config_file)
|
|
27
|
-
return config_file unless File.dirname(config_file) ==
|
|
29
|
+
return config_file unless File.dirname(config_file) == '.'
|
|
28
30
|
|
|
29
31
|
dir = Dir.pwd
|
|
30
|
-
parent_dir = File.expand_path(
|
|
32
|
+
parent_dir = File.expand_path('..', Dir.pwd)
|
|
31
33
|
while parent_dir != dir && !File.exist?(File.join(dir, config_file))
|
|
32
34
|
dir = parent_dir
|
|
33
|
-
parent_dir = File.expand_path(
|
|
35
|
+
parent_dir = File.expand_path('..', dir)
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
File.join(dir, config_file)
|
|
@@ -43,15 +45,15 @@ module StackMaster
|
|
|
43
45
|
@parameters_dir = config.fetch('parameters_dir', nil)
|
|
44
46
|
@stack_defaults = config.fetch('stack_defaults', {})
|
|
45
47
|
@region_aliases = Utils.underscore_keys_to_hyphen(config.fetch('region_aliases', {}))
|
|
46
|
-
@region_to_aliases = @region_aliases.
|
|
48
|
+
@region_to_aliases = @region_aliases.each_with_object({}) do |(key, value), hash|
|
|
47
49
|
hash[value] ||= []
|
|
48
50
|
hash[value] << key
|
|
49
|
-
hash
|
|
50
51
|
end
|
|
51
52
|
@region_defaults = normalise_region_defaults(config.fetch('region_defaults', {}))
|
|
52
53
|
@stacks = []
|
|
53
54
|
|
|
54
55
|
raise ConfigParseError.new("Stack defaults can't be undefined") if @stack_defaults.nil?
|
|
56
|
+
|
|
55
57
|
load_template_compilers(config)
|
|
56
58
|
load_config
|
|
57
59
|
end
|
|
@@ -72,6 +74,7 @@ module StackMaster
|
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
private
|
|
77
|
+
|
|
75
78
|
def load_template_compilers(config)
|
|
76
79
|
@template_compilers = {}
|
|
77
80
|
populate_template_compilers(config.fetch('template_compilers', {}))
|
|
@@ -82,7 +85,7 @@ module StackMaster
|
|
|
82
85
|
@template_compilers = default_template_compilers.merge(@template_compilers)
|
|
83
86
|
end
|
|
84
87
|
|
|
85
|
-
def populate_template_compilers
|
|
88
|
+
def populate_template_compilers(user_defined_compilers)
|
|
86
89
|
user_defined_compilers.each do |key, val|
|
|
87
90
|
@template_compilers[key.to_sym] = val.to_sym
|
|
88
91
|
end
|
|
@@ -92,9 +95,9 @@ module StackMaster
|
|
|
92
95
|
{
|
|
93
96
|
rb: :sparkle_formation,
|
|
94
97
|
json: :json,
|
|
95
|
-
yml:
|
|
98
|
+
yml: :yaml,
|
|
96
99
|
yaml: :yaml,
|
|
97
|
-
erb:
|
|
100
|
+
erb: :yaml_erb
|
|
98
101
|
}
|
|
99
102
|
end
|
|
100
103
|
|
|
@@ -104,9 +107,8 @@ module StackMaster
|
|
|
104
107
|
end
|
|
105
108
|
|
|
106
109
|
def resolve_region_aliases(stacks)
|
|
107
|
-
stacks.
|
|
110
|
+
stacks.each_with_object({}) do |(region, attributes), hash|
|
|
108
111
|
hash[unalias_region(region)] = attributes
|
|
109
|
-
hash
|
|
110
112
|
end
|
|
111
113
|
end
|
|
112
114
|
|
|
@@ -123,7 +125,8 @@ module StackMaster
|
|
|
123
125
|
'base_dir' => @base_dir,
|
|
124
126
|
'template_dir' => @template_dir,
|
|
125
127
|
'parameters_dir' => @parameters_dir,
|
|
126
|
-
'additional_parameter_lookup_dirs' => @region_to_aliases[region]
|
|
128
|
+
'additional_parameter_lookup_dirs' => @region_to_aliases[region]
|
|
129
|
+
)
|
|
127
130
|
stack_attributes['allowed_accounts'] = attributes['allowed_accounts'] if attributes['allowed_accounts']
|
|
128
131
|
@stacks << StackDefinition.new(stack_attributes)
|
|
129
132
|
end
|
|
@@ -136,10 +139,9 @@ module StackMaster
|
|
|
136
139
|
end
|
|
137
140
|
|
|
138
141
|
def normalise_region_defaults(region_defaults)
|
|
139
|
-
region_defaults.
|
|
142
|
+
region_defaults.each_with_object({}) do |(region_or_alias, value), normalised_aliases|
|
|
140
143
|
region = unalias_region(region_or_alias)
|
|
141
144
|
normalised_aliases[Utils.underscore_to_hyphen(region)] = value
|
|
142
|
-
normalised_aliases
|
|
143
145
|
end
|
|
144
146
|
end
|
|
145
147
|
end
|
data/lib/stack_master/diff.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module StackMaster
|
|
2
2
|
class Diff
|
|
3
|
-
def initialize(name: nil,
|
|
3
|
+
def initialize(before:, after:, name: nil, context: 10_000)
|
|
4
4
|
@name = name
|
|
5
5
|
@before = before
|
|
6
6
|
@after = after
|
|
@@ -10,7 +10,7 @@ module StackMaster
|
|
|
10
10
|
def display
|
|
11
11
|
stdout.print "#{@name} diff: "
|
|
12
12
|
if diff == ''
|
|
13
|
-
stdout.puts
|
|
13
|
+
stdout.puts 'No changes'
|
|
14
14
|
else
|
|
15
15
|
stdout.puts
|
|
16
16
|
display_colorized_diff
|
|
@@ -21,7 +21,8 @@ module StackMaster
|
|
|
21
21
|
def account_aliases
|
|
22
22
|
@aliases ||= iam.list_account_aliases.account_aliases
|
|
23
23
|
rescue Aws::IAM::Errors::AccessDenied
|
|
24
|
-
raise MissingIamPermissionsError,
|
|
24
|
+
raise MissingIamPermissionsError,
|
|
25
|
+
'Failed to retrieve account aliases. Missing required IAM permission: iam:ListAccountAliases'
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
private
|
|
@@ -2,19 +2,17 @@ require 'active_support/core_ext/object/deep_dup'
|
|
|
2
2
|
|
|
3
3
|
module StackMaster
|
|
4
4
|
class ParameterLoader
|
|
5
|
-
|
|
6
5
|
COMPILE_TIME_PARAMETERS_KEY = 'compile_time_parameters'
|
|
7
6
|
|
|
8
7
|
def self.load(parameter_files: [], parameters: {})
|
|
9
8
|
StackMaster.debug 'Searching for parameter files...'
|
|
10
9
|
all_parameters = parameter_files.map { |file_name| load_parameters(file_name) } + [parameters]
|
|
11
|
-
all_parameters.
|
|
10
|
+
all_parameters.each_with_object({ template_parameters: {}, compile_time_parameters: {} }) do |parameters, hash|
|
|
12
11
|
template_parameters = create_template_parameters(parameters)
|
|
13
12
|
compile_time_parameters = create_compile_time_parameters(parameters)
|
|
14
13
|
|
|
15
14
|
merge_and_camelize(hash[:template_parameters], template_parameters)
|
|
16
15
|
merge_and_camelize(hash[:compile_time_parameters], compile_time_parameters)
|
|
17
|
-
hash
|
|
18
16
|
end
|
|
19
17
|
end
|
|
20
18
|
|
|
@@ -32,7 +30,8 @@ module StackMaster
|
|
|
32
30
|
|
|
33
31
|
def self.create_template_parameters(parameters)
|
|
34
32
|
parameters.deep_dup.tap do |parameters_clone|
|
|
35
|
-
parameters_clone.delete(COMPILE_TIME_PARAMETERS_KEY) ||
|
|
33
|
+
parameters_clone.delete(COMPILE_TIME_PARAMETERS_KEY) ||
|
|
34
|
+
parameters_clone.delete(COMPILE_TIME_PARAMETERS_KEY.camelize)
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
|
|
@@ -43,6 +42,5 @@ module StackMaster
|
|
|
43
42
|
def self.merge_and_camelize(hash, parameters)
|
|
44
43
|
parameters.each { |key, value| hash[key.camelize] = value }
|
|
45
44
|
end
|
|
46
|
-
|
|
47
45
|
end
|
|
48
46
|
end
|
|
@@ -15,13 +15,12 @@ module StackMaster
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def resolve
|
|
18
|
-
@parameters.
|
|
18
|
+
@parameters.each_with_object({}) do |(key, value), parameters|
|
|
19
19
|
begin
|
|
20
20
|
parameters[key] = resolve_parameter_value(key, value)
|
|
21
21
|
rescue InvalidParameter
|
|
22
22
|
raise InvalidParameter, "Unable to resolve parameter #{key.inspect} value causing error: #{$!.message}"
|
|
23
23
|
end
|
|
24
|
-
parameters
|
|
25
24
|
end
|
|
26
25
|
end
|
|
27
26
|
|
|
@@ -30,11 +29,9 @@ module StackMaster
|
|
|
30
29
|
def require_parameter_resolver(file_name)
|
|
31
30
|
require "stack_master/parameter_resolvers/#{file_name}"
|
|
32
31
|
rescue LoadError
|
|
33
|
-
if file_name == file_name.singularize
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
require_parameter_resolver(file_name.singularize)
|
|
37
|
-
end
|
|
32
|
+
raise ResolverNotFound.new(file_name) if file_name == file_name.singularize
|
|
33
|
+
|
|
34
|
+
require_parameter_resolver(file_name.singularize)
|
|
38
35
|
end
|
|
39
36
|
|
|
40
37
|
def load_parameter_resolver(class_name)
|
|
@@ -46,9 +43,12 @@ module StackMaster
|
|
|
46
43
|
end
|
|
47
44
|
|
|
48
45
|
def resolve_parameter_value(key, parameter_value)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
if parameter_value.is_a?(Numeric) || parameter_value == true || parameter_value == false
|
|
47
|
+
return parameter_value.to_s
|
|
48
|
+
end
|
|
49
|
+
return resolve_array_parameter_values(key, parameter_value).join(',') if parameter_value.is_a?(Array)
|
|
50
|
+
return parameter_value unless parameter_value.is_a?(Hash)
|
|
51
|
+
|
|
52
52
|
resolve_parameter_resolver_hash(key, parameter_value)
|
|
53
53
|
rescue Aws::CloudFormation::Errors::ValidationError
|
|
54
54
|
raise InvalidParameter, $!.message
|
|
@@ -72,14 +72,13 @@ module StackMaster
|
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
def assume_role_if_present(account, role, key)
|
|
75
|
+
def assume_role_if_present(account, role, key, &block)
|
|
76
76
|
return yield if account.nil? && role.nil?
|
|
77
77
|
if account.nil? || role.nil?
|
|
78
78
|
raise InvalidParameter, "Both 'account' and 'role' are required to assume role for parameter '#{key}'"
|
|
79
79
|
end
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
end
|
|
80
|
+
|
|
81
|
+
role_assumer.assume_role(account, role, &block)
|
|
83
82
|
end
|
|
84
83
|
|
|
85
84
|
def resolve_array_parameter_values(key, parameter_values)
|
|
@@ -105,15 +104,16 @@ module StackMaster
|
|
|
105
104
|
begin
|
|
106
105
|
@resolvers[class_name] = resolver_class_const(class_name).new(@config, @stack_definition)
|
|
107
106
|
rescue NameError
|
|
108
|
-
raise ResolverNotFound,
|
|
107
|
+
raise ResolverNotFound,
|
|
108
|
+
"Could not find parameter resolver called #{class_name}, please double check your configuration"
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def validate_parameter_value!(key, parameter_value)
|
|
114
|
-
if parameter_value.keys.size
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
return if parameter_value.keys.size == 1
|
|
115
|
+
|
|
116
|
+
raise InvalidParameter, "#{key} hash contained more than one key: #{parameter_value.inspect}"
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def role_assumer
|
|
@@ -10,7 +10,10 @@ module StackMaster
|
|
|
10
10
|
|
|
11
11
|
def resolve(domain_name)
|
|
12
12
|
cert_arn = find_cert_arn_by_domain_name(domain_name)
|
|
13
|
-
|
|
13
|
+
unless cert_arn
|
|
14
|
+
raise CertificateNotFound, "Could not find certificate #{domain_name} in #{@stack_definition.region}"
|
|
15
|
+
end
|
|
16
|
+
|
|
14
17
|
cert_arn
|
|
15
18
|
end
|
|
16
19
|
|
|
@@ -6,16 +6,15 @@ module StackMaster
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def build_filters_from_string(value, prefix = nil)
|
|
9
|
-
|
|
9
|
+
value.split(',').map do |name_with_value|
|
|
10
10
|
name, value = name_with_value.strip.split('=')
|
|
11
11
|
name = prefix ? "#{prefix}:#{name}" : name
|
|
12
12
|
{ name: name, values: [value] }
|
|
13
13
|
end
|
|
14
|
-
filters
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def build_filters_from_hash(hash)
|
|
18
|
-
hash.map { |key, value| {name: key, values: Array(value.to_s)}}
|
|
17
|
+
hash.map { |key, value| { name: key, values: Array(value.to_s) } }
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def find_latest_ami(filters, owners = ['self'])
|
|
@@ -22,17 +22,20 @@ module StackMaster
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
24
|
def validate_ejson_file_specified
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
return unless @stack_definition.ejson_file.nil?
|
|
26
|
+
|
|
27
|
+
raise ArgumentError, 'No ejson_file defined for stack definition ' \
|
|
28
|
+
"#{@stack_definition.stack_name} in #{@stack_definition.region}"
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
def decrypt_ejson_file
|
|
31
32
|
ejson_file_key = credentials_key
|
|
32
33
|
@decrypted_ejson_files.fetch(ejson_file_key) do
|
|
33
|
-
@decrypted_ejson_files[ejson_file_key] = EJSONWrapper.decrypt(
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
@decrypted_ejson_files[ejson_file_key] = EJSONWrapper.decrypt(
|
|
35
|
+
ejson_file_path,
|
|
36
|
+
use_kms: @stack_definition.ejson_file_kms,
|
|
37
|
+
region: ejson_file_region
|
|
38
|
+
)
|
|
36
39
|
end
|
|
37
40
|
end
|
|
38
41
|
|