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
|
@@ -2,7 +2,6 @@ module StackMaster
|
|
|
2
2
|
module SparkleFormation
|
|
3
3
|
module CompileTime
|
|
4
4
|
class ValueValidator
|
|
5
|
-
|
|
6
5
|
attr_reader :is_valid, :error
|
|
7
6
|
|
|
8
7
|
def validate
|
|
@@ -28,13 +27,11 @@ module StackMaster
|
|
|
28
27
|
private
|
|
29
28
|
|
|
30
29
|
def convert_to_array(definition, parameter)
|
|
31
|
-
if definition[:multiple] && parameter.is_a?(String)
|
|
32
|
-
|
|
33
|
-
end
|
|
30
|
+
return parameter.split(',').map(&:strip) if definition[:multiple] && parameter.is_a?(String)
|
|
31
|
+
|
|
34
32
|
parameter.is_a?(Array) ? parameter : [parameter]
|
|
35
33
|
end
|
|
36
|
-
|
|
37
34
|
end
|
|
38
35
|
end
|
|
39
36
|
end
|
|
40
|
-
end
|
|
37
|
+
end
|
|
@@ -12,17 +12,16 @@ module StackMaster
|
|
|
12
12
|
module SparkleFormation
|
|
13
13
|
module CompileTime
|
|
14
14
|
class ValueValidatorFactory
|
|
15
|
-
|
|
16
15
|
VALIDATORS_TYPES = [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
EmptyValidator,
|
|
17
|
+
StringValidator,
|
|
18
|
+
NumberValidator,
|
|
19
|
+
AllowedValuesValidator,
|
|
20
|
+
AllowedPatternValidator,
|
|
21
|
+
MaxLengthValidator,
|
|
22
|
+
MinLengthValidator,
|
|
23
|
+
MaxSizeValidator,
|
|
24
|
+
MinSizeValidator
|
|
26
25
|
]
|
|
27
26
|
|
|
28
27
|
def initialize(name, definition, parameter)
|
|
@@ -32,10 +31,9 @@ module StackMaster
|
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
def build
|
|
35
|
-
VALIDATORS_TYPES.map { |validator| validator.new(@name, @definition, @parameter)}
|
|
34
|
+
VALIDATORS_TYPES.map { |validator| validator.new(@name, @definition, @parameter) }
|
|
36
35
|
end
|
|
37
|
-
|
|
38
36
|
end
|
|
39
37
|
end
|
|
40
38
|
end
|
|
41
|
-
end
|
|
39
|
+
end
|
|
@@ -17,7 +17,6 @@ module StackMaster
|
|
|
17
17
|
value
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
|
-
|
|
21
20
|
end.new(vars, prefix)
|
|
22
21
|
end
|
|
23
22
|
|
|
@@ -50,18 +49,17 @@ module StackMaster
|
|
|
50
49
|
def _joined_file(file_name, vars = {})
|
|
51
50
|
join!(Template.render('joined_file', file_name, vars))
|
|
52
51
|
end
|
|
53
|
-
|
|
52
|
+
alias joined_file! _joined_file
|
|
54
53
|
end
|
|
55
54
|
|
|
56
55
|
module UserDataFile
|
|
57
56
|
def _user_data_file(file_name, vars = {})
|
|
58
57
|
base64!(join!(Template.render('user_data', file_name, vars)))
|
|
59
58
|
end
|
|
60
|
-
|
|
59
|
+
alias user_data_file! _user_data_file
|
|
61
60
|
end
|
|
62
61
|
end
|
|
63
62
|
end
|
|
64
63
|
|
|
65
64
|
SparkleFormation::SparkleAttribute::Aws.send(:include, StackMaster::SparkleFormation::UserDataFile)
|
|
66
65
|
SparkleFormation::SparkleAttribute::Aws.send(:include, StackMaster::SparkleFormation::JoinedFile)
|
|
67
|
-
|
|
@@ -6,28 +6,31 @@ module StackMaster
|
|
|
6
6
|
output_regex = %r{(?:(?<region>[^:]+):)?(?<identity_store_id>[^:/]+)/(?<group_name>.+)}
|
|
7
7
|
|
|
8
8
|
if !reference.is_a?(String) || !(match = output_regex.match(reference))
|
|
9
|
-
|
|
9
|
+
raise ArgumentError, 'Sso group lookup parameter must be in the form of [region:]identity-store-id/group_name'
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
region = match[:region] || StackMaster.cloud_formation_driver.region
|
|
13
13
|
client = Aws::IdentityStore::Client.new({ region: region })
|
|
14
14
|
|
|
15
15
|
begin
|
|
16
|
-
response = client.get_group_id(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
response = client.get_group_id(
|
|
17
|
+
{
|
|
18
|
+
identity_store_id: match[:identity_store_id],
|
|
19
|
+
alternate_identifier: {
|
|
20
|
+
unique_attribute: {
|
|
21
|
+
attribute_path: 'displayName',
|
|
22
|
+
attribute_value: match[:group_name]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
)
|
|
25
27
|
return response.group_id
|
|
26
28
|
rescue Aws::IdentityStore::Errors::ServiceError => e
|
|
27
|
-
|
|
29
|
+
puts "Error calling GetGroupId: #{e.message}"
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
raise SsoGroupNotFound,
|
|
32
|
+
raise SsoGroupNotFound,
|
|
33
|
+
"No group with name #{match[:group_name]} found in identity store #{match[:identity_store_id]} in #{region}"
|
|
31
34
|
end
|
|
32
35
|
end
|
|
33
36
|
end
|
data/lib/stack_master/stack.rb
CHANGED
|
@@ -17,10 +17,12 @@ module StackMaster
|
|
|
17
17
|
include Utils::Initializable
|
|
18
18
|
|
|
19
19
|
def template_default_parameters
|
|
20
|
-
TemplateUtils
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
TemplateUtils
|
|
21
|
+
.template_hash(template)
|
|
22
|
+
.fetch('Parameters', {})
|
|
23
|
+
.each_with_object({}) do |(parameter_name, description), result|
|
|
24
|
+
result[parameter_name] = description['Default']&.to_s
|
|
25
|
+
end
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def parameters_with_defaults
|
|
@@ -31,19 +33,23 @@ module StackMaster
|
|
|
31
33
|
cf = StackMaster.cloud_formation_driver
|
|
32
34
|
cf_stack = cf.describe_stacks({ stack_name: stack_name }).stacks.first
|
|
33
35
|
return unless cf_stack
|
|
34
|
-
|
|
36
|
+
|
|
37
|
+
parameters = cf_stack.parameters.each_with_object({}) do |param_struct, params_hash|
|
|
35
38
|
params_hash[param_struct.parameter_key] = param_struct.parameter_value
|
|
36
|
-
params_hash
|
|
37
39
|
end
|
|
38
40
|
template_body ||= cf.get_template({ stack_name: stack_name, template_stage: 'Original' }).template_body
|
|
39
41
|
template_format = TemplateUtils.identify_template_format(template_body)
|
|
40
42
|
stack_policy_body ||= cf.get_stack_policy({ stack_name: stack_name }).stack_policy_body
|
|
41
43
|
outputs = cf_stack.outputs
|
|
44
|
+
tags = cf_stack.tags&.each_with_object({}) do |tag_struct, tags_hash|
|
|
45
|
+
tags_hash[tag_struct.key] = tag_struct.value
|
|
46
|
+
end || {}
|
|
42
47
|
|
|
43
48
|
new(region: region,
|
|
44
49
|
stack_name: stack_name,
|
|
45
50
|
stack_id: cf_stack.stack_id,
|
|
46
51
|
parameters: parameters,
|
|
52
|
+
tags: tags,
|
|
47
53
|
template_body: template_body,
|
|
48
54
|
template_format: template_format,
|
|
49
55
|
outputs: outputs,
|
|
@@ -56,14 +62,27 @@ module StackMaster
|
|
|
56
62
|
end
|
|
57
63
|
|
|
58
64
|
def self.generate(stack_definition, config)
|
|
59
|
-
parameter_hash = ParameterLoader.load(
|
|
65
|
+
parameter_hash = ParameterLoader.load(
|
|
66
|
+
parameter_files: stack_definition.all_parameter_files,
|
|
67
|
+
parameters: stack_definition.parameters
|
|
68
|
+
)
|
|
60
69
|
template_parameters = ParameterResolver.resolve(config, stack_definition, parameter_hash[:template_parameters])
|
|
61
|
-
compile_time_parameters = ParameterResolver.resolve(
|
|
62
|
-
|
|
70
|
+
compile_time_parameters = ParameterResolver.resolve(
|
|
71
|
+
config,
|
|
72
|
+
stack_definition,
|
|
73
|
+
parameter_hash[:compile_time_parameters]
|
|
74
|
+
)
|
|
75
|
+
template_body = TemplateCompiler.compile(
|
|
76
|
+
config,
|
|
77
|
+
stack_definition.compiler,
|
|
78
|
+
stack_definition.template_dir,
|
|
79
|
+
stack_definition.template,
|
|
80
|
+
compile_time_parameters,
|
|
81
|
+
stack_definition.compiler_options
|
|
82
|
+
)
|
|
63
83
|
template_format = TemplateUtils.identify_template_format(template_body)
|
|
64
|
-
stack_policy_body =
|
|
65
|
-
|
|
66
|
-
end
|
|
84
|
+
stack_policy_body =
|
|
85
|
+
(File.read(stack_definition.stack_policy_file_path) if stack_definition.stack_policy_file_path)
|
|
67
86
|
new(region: stack_definition.region,
|
|
68
87
|
stack_name: stack_definition.stack_name,
|
|
69
88
|
tags: stack_definition.tags,
|
|
@@ -76,13 +95,26 @@ module StackMaster
|
|
|
76
95
|
end
|
|
77
96
|
|
|
78
97
|
def self.generate_without_parameters(stack_definition, config)
|
|
79
|
-
parameter_hash = ParameterLoader.load(
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
parameter_hash = ParameterLoader.load(
|
|
99
|
+
parameter_files: stack_definition.all_parameter_files,
|
|
100
|
+
parameters: stack_definition.parameters
|
|
101
|
+
)
|
|
102
|
+
compile_time_parameters = ParameterResolver.resolve(
|
|
103
|
+
config,
|
|
104
|
+
stack_definition,
|
|
105
|
+
parameter_hash[:compile_time_parameters]
|
|
106
|
+
)
|
|
107
|
+
template_body = TemplateCompiler.compile(
|
|
108
|
+
config,
|
|
109
|
+
stack_definition.compiler,
|
|
110
|
+
stack_definition.template_dir,
|
|
111
|
+
stack_definition.template,
|
|
112
|
+
compile_time_parameters,
|
|
113
|
+
stack_definition.compiler_options
|
|
114
|
+
)
|
|
82
115
|
template_format = TemplateUtils.identify_template_format(template_body)
|
|
83
|
-
stack_policy_body =
|
|
84
|
-
|
|
85
|
-
end
|
|
116
|
+
stack_policy_body =
|
|
117
|
+
(File.read(stack_definition.stack_policy_file_path) if stack_definition.stack_policy_file_path)
|
|
86
118
|
new(region: stack_definition.region,
|
|
87
119
|
stack_name: stack_definition.stack_name,
|
|
88
120
|
tags: stack_definition.tags,
|
|
@@ -96,6 +128,7 @@ module StackMaster
|
|
|
96
128
|
|
|
97
129
|
def max_template_size(use_s3)
|
|
98
130
|
return TemplateUtils::MAX_S3_TEMPLATE_SIZE if use_s3
|
|
131
|
+
|
|
99
132
|
TemplateUtils::MAX_TEMPLATE_SIZE
|
|
100
133
|
end
|
|
101
134
|
|
|
@@ -35,7 +35,7 @@ module StackMaster
|
|
|
35
35
|
@compiler = nil
|
|
36
36
|
super
|
|
37
37
|
@additional_parameter_lookup_dirs ||= []
|
|
38
|
-
@base_dir ||=
|
|
38
|
+
@base_dir ||= ''
|
|
39
39
|
@template_dir ||= File.join(@base_dir, 'templates')
|
|
40
40
|
@parameters_dir ||= File.join(@base_dir, 'parameters')
|
|
41
41
|
@allowed_accounts = Array(@allowed_accounts)
|
|
@@ -44,7 +44,7 @@ module StackMaster
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def ==(other)
|
|
47
|
-
self.class
|
|
47
|
+
other.is_a?(self.class) &&
|
|
48
48
|
@region == other.region &&
|
|
49
49
|
@stack_name == other.stack_name &&
|
|
50
50
|
@template == other.template &&
|
|
@@ -65,6 +65,7 @@ module StackMaster
|
|
|
65
65
|
|
|
66
66
|
def template_file_path
|
|
67
67
|
return unless template
|
|
68
|
+
|
|
68
69
|
File.expand_path(template, template_dir)
|
|
69
70
|
end
|
|
70
71
|
|
|
@@ -73,18 +74,18 @@ module StackMaster
|
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
def s3_files
|
|
76
|
-
files.
|
|
77
|
+
files.each_with_object({}) do |file, hash|
|
|
77
78
|
path = File.join(files_dir, file)
|
|
78
79
|
hash[file] = {
|
|
79
80
|
path: path,
|
|
80
81
|
body: File.read(path)
|
|
81
82
|
}
|
|
82
|
-
hash
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def s3_template_file_name
|
|
87
87
|
return template if ['.json', '.yaml', '.yml'].include?(File.extname(template))
|
|
88
|
+
|
|
88
89
|
Utils.change_extension(template, 'json')
|
|
89
90
|
end
|
|
90
91
|
|
|
@@ -101,7 +102,7 @@ module StackMaster
|
|
|
101
102
|
end
|
|
102
103
|
|
|
103
104
|
def parameter_file_globs
|
|
104
|
-
[
|
|
105
|
+
[default_parameter_glob, region_parameter_glob] + additional_parameter_lookup_globs
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
def stack_policy_file_path
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require 'diffy'
|
|
2
|
+
require 'hashdiff'
|
|
3
3
|
|
|
4
4
|
module StackMaster
|
|
5
5
|
class StackDiffer
|
|
@@ -10,12 +10,14 @@ module StackMaster
|
|
|
10
10
|
|
|
11
11
|
def proposed_template
|
|
12
12
|
return @proposed_stack.template_body unless @proposed_stack.template_format == :json
|
|
13
|
+
|
|
13
14
|
JSON.pretty_generate(JSON.parse(@proposed_stack.template_body)) + "\n"
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def current_template
|
|
17
18
|
return '' unless @current_stack
|
|
18
19
|
return @current_stack.template_body unless @current_stack.template_format == :json
|
|
20
|
+
|
|
19
21
|
JSON.pretty_generate(TemplateUtils.template_hash(@current_stack.template_body)) + "\n"
|
|
20
22
|
end
|
|
21
23
|
|
|
@@ -32,7 +34,7 @@ module StackMaster
|
|
|
32
34
|
params = @proposed_stack.parameters_with_defaults
|
|
33
35
|
if @current_stack
|
|
34
36
|
noecho_keys.each do |key|
|
|
35
|
-
params[key] =
|
|
37
|
+
params[key] = '****'
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
40
|
YAML.dump(sort_params(params))
|
|
@@ -59,20 +61,43 @@ module StackMaster
|
|
|
59
61
|
after: proposed_parameters)
|
|
60
62
|
end
|
|
61
63
|
|
|
64
|
+
def tags_different?
|
|
65
|
+
tags_diff.different?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def tags_diff
|
|
69
|
+
@tags_diff ||= Diff.new(name: 'Tags',
|
|
70
|
+
before: current_tags,
|
|
71
|
+
after: proposed_tags)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def current_tags
|
|
75
|
+
tags_hash = @current_stack&.tags
|
|
76
|
+
return '' if tags_hash.nil? || tags_hash.empty?
|
|
77
|
+
|
|
78
|
+
YAML.dump(sort_params(tags_hash))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def proposed_tags
|
|
82
|
+
tags_hash = @proposed_stack.tags
|
|
83
|
+
return '' if tags_hash.nil? || tags_hash.empty?
|
|
84
|
+
|
|
85
|
+
YAML.dump(sort_params(tags_hash))
|
|
86
|
+
end
|
|
87
|
+
|
|
62
88
|
def output_diff
|
|
63
89
|
body_diff.display
|
|
64
90
|
parameters_diff.display
|
|
91
|
+
tags_diff.display
|
|
65
92
|
|
|
66
|
-
unless noecho_keys.empty?
|
|
67
|
-
|
|
68
|
-
end
|
|
69
|
-
StackMaster.stdout.puts "No stack found" if @current_stack.nil?
|
|
93
|
+
StackMaster.stdout.puts ' * can not tell if NoEcho parameters are different.' unless noecho_keys.empty?
|
|
94
|
+
StackMaster.stdout.puts 'No stack found' if @current_stack.nil?
|
|
70
95
|
end
|
|
71
96
|
|
|
72
97
|
def noecho_keys
|
|
73
98
|
if @current_stack
|
|
74
99
|
@current_stack.parameters_with_defaults.select do |key, value|
|
|
75
|
-
value ==
|
|
100
|
+
value == '****'
|
|
76
101
|
end.keys
|
|
77
102
|
else
|
|
78
103
|
[]
|
|
@@ -81,10 +106,12 @@ module StackMaster
|
|
|
81
106
|
|
|
82
107
|
def single_param_update?(param_name)
|
|
83
108
|
return false if param_name.blank? || @current_stack.blank? || body_different?
|
|
109
|
+
|
|
84
110
|
differences = Hashdiff.diff(@current_stack.parameters_with_defaults, @proposed_stack.parameters_with_defaults)
|
|
85
111
|
return false if differences.count != 1
|
|
112
|
+
|
|
86
113
|
diff = differences[0]
|
|
87
|
-
diff[0] ==
|
|
114
|
+
diff[0] == '~' && diff[1] == param_name
|
|
88
115
|
end
|
|
89
116
|
|
|
90
117
|
private
|
|
@@ -31,7 +31,9 @@ module StackMaster
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def fetch_events
|
|
34
|
-
PagedResponseAccumulator
|
|
34
|
+
PagedResponseAccumulator
|
|
35
|
+
.call(cf, :describe_stack_events, { stack_name: @stack_name }, :stack_events)
|
|
36
|
+
.stack_events
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
end
|
|
@@ -10,7 +10,12 @@ module StackMaster
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def print_event(event)
|
|
13
|
-
@io.puts
|
|
13
|
+
@io.puts(
|
|
14
|
+
Rainbow(
|
|
15
|
+
"#{event.timestamp.localtime} #{event.logical_resource_id} #{event.resource_type} " \
|
|
16
|
+
"#{event.resource_status} #{event.resource_status_reason}"
|
|
17
|
+
).color(event_colour(event))
|
|
18
|
+
)
|
|
14
19
|
end
|
|
15
20
|
|
|
16
21
|
def event_colour(event)
|
|
@@ -7,7 +7,8 @@ module StackMaster
|
|
|
7
7
|
new(stack_name, region, **args, &block).stream
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def initialize(stack_name, region, from: Time.now, break_on_finish_state: true, sleep_between_fetches: 1,
|
|
10
|
+
def initialize(stack_name, region, from: Time.now, break_on_finish_state: true, sleep_between_fetches: 1,
|
|
11
|
+
io: nil, &block)
|
|
11
12
|
@stack_name = stack_name
|
|
12
13
|
@region = region
|
|
13
14
|
@block = block
|
|
@@ -42,6 +43,7 @@ module StackMaster
|
|
|
42
43
|
[].tap do |unseen_events|
|
|
43
44
|
events.each do |event|
|
|
44
45
|
next if @seen_events.include?(event.event_id)
|
|
46
|
+
|
|
45
47
|
@seen_events << event.event_id
|
|
46
48
|
unseen_events << event
|
|
47
49
|
end
|
|
@@ -4,7 +4,6 @@ require 'stack_master/sparkle_formation/compile_time/state_builder'
|
|
|
4
4
|
|
|
5
5
|
module StackMaster::TemplateCompilers
|
|
6
6
|
class SparkleFormation
|
|
7
|
-
|
|
8
7
|
CompileTime = StackMaster::SparkleFormation::CompileTime
|
|
9
8
|
|
|
10
9
|
def self.require_dependencies
|
|
@@ -28,27 +27,31 @@ module StackMaster::TemplateCompilers
|
|
|
28
27
|
private
|
|
29
28
|
|
|
30
29
|
def self.compile_sparkle_template(template_dir, template, compiler_options)
|
|
31
|
-
sparkle_path =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
sparkle_path =
|
|
31
|
+
if compiler_options['sparkle_path']
|
|
32
|
+
File.expand_path(compiler_options['sparkle_path'])
|
|
33
|
+
else
|
|
34
|
+
template_dir
|
|
35
|
+
end
|
|
36
36
|
|
|
37
37
|
collection = ::SparkleFormation::SparkleCollection.new
|
|
38
38
|
root_pack = ::SparkleFormation::Sparkle.new(
|
|
39
|
-
:
|
|
39
|
+
root: sparkle_path
|
|
40
40
|
)
|
|
41
41
|
collection.set_root(root_pack)
|
|
42
42
|
if compiler_options['sparkle_packs']
|
|
43
43
|
compiler_options['sparkle_packs'].each do |pack_name|
|
|
44
44
|
require pack_name
|
|
45
|
-
pack = ::SparkleFormation::SparklePack.new(:
|
|
45
|
+
pack = ::SparkleFormation::SparklePack.new(name: pack_name)
|
|
46
46
|
collection.add_sparkle(pack)
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
if compiler_options['sparkle_pack_template']
|
|
51
|
-
|
|
51
|
+
unless collection.templates['aws'].include? template
|
|
52
|
+
raise ArgumentError.new("Template #{template.inspect} not found in any sparkle pack")
|
|
53
|
+
end
|
|
54
|
+
|
|
52
55
|
template_file_path = collection.templates['aws'][template].top['path']
|
|
53
56
|
else
|
|
54
57
|
template_file_path = File.join(template_dir, template)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module StackMaster
|
|
2
2
|
module TemplateUtils
|
|
3
|
-
MAX_TEMPLATE_SIZE =
|
|
4
|
-
MAX_S3_TEMPLATE_SIZE =
|
|
3
|
+
MAX_TEMPLATE_SIZE = 51_200
|
|
4
|
+
MAX_S3_TEMPLATE_SIZE = 460_800
|
|
5
5
|
# Matches if the first non-whitespace character is a '{', handling cases
|
|
6
6
|
# with leading whitespace and extra (whitespace-only) lines.
|
|
7
7
|
JSON_IDENTIFICATION_PATTERN = Regexp.new('\A\s*{', Regexp::MULTILINE)
|
|
@@ -16,8 +16,9 @@ module StackMaster
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def template_hash(template_body=nil)
|
|
19
|
+
def template_hash(template_body = nil)
|
|
20
20
|
return unless template_body
|
|
21
|
+
|
|
21
22
|
template_format = identify_template_format(template_body)
|
|
22
23
|
case template_format
|
|
23
24
|
when :json
|
|
@@ -30,7 +31,10 @@ module StackMaster
|
|
|
30
31
|
def maybe_compressed_template_body(template_body)
|
|
31
32
|
# Do not compress the template if it's not JSON because parsing YAML as a hash ignores
|
|
32
33
|
# CloudFormation-specific tags such as !Ref
|
|
33
|
-
|
|
34
|
+
if template_body.size <= MAX_TEMPLATE_SIZE || identify_template_format(template_body) != :json
|
|
35
|
+
return template_body
|
|
36
|
+
end
|
|
37
|
+
|
|
34
38
|
JSON.dump(template_hash(template_body))
|
|
35
39
|
end
|
|
36
40
|
end
|
|
@@ -28,6 +28,14 @@ module StackMaster
|
|
|
28
28
|
parameter_value: hash[:parameter_value])
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
|
+
|
|
32
|
+
def tags
|
|
33
|
+
return [] if @tags.nil?
|
|
34
|
+
|
|
35
|
+
@tags.map do |hash|
|
|
36
|
+
OpenStruct.new(key: hash[:key], value: hash[:value])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
31
39
|
end
|
|
32
40
|
|
|
33
41
|
class StackEvent
|
|
@@ -99,11 +107,27 @@ module StackMaster
|
|
|
99
107
|
change_set_id = options.fetch(:change_set_name)
|
|
100
108
|
change_set = @change_sets.fetch(change_set_id)
|
|
101
109
|
change_details = [
|
|
102
|
-
OpenStruct.new(
|
|
110
|
+
OpenStruct.new(
|
|
111
|
+
evaluation: 'Static',
|
|
112
|
+
change_source: 'ResourceReference',
|
|
113
|
+
target: OpenStruct.new(
|
|
114
|
+
attribute: 'Properties',
|
|
115
|
+
requires_recreation: 'Always',
|
|
116
|
+
name: 'blah'
|
|
117
|
+
)
|
|
118
|
+
)
|
|
103
119
|
]
|
|
104
|
-
change = OpenStruct.new(
|
|
120
|
+
change = OpenStruct.new(
|
|
121
|
+
action: 'Modify',
|
|
122
|
+
replacement: 'True',
|
|
123
|
+
scope: ['Properties'],
|
|
124
|
+
details: change_details
|
|
125
|
+
)
|
|
105
126
|
changes = [
|
|
106
|
-
OpenStruct.new(
|
|
127
|
+
OpenStruct.new(
|
|
128
|
+
type: 'AWS::Resource',
|
|
129
|
+
resource_change: change
|
|
130
|
+
)
|
|
107
131
|
]
|
|
108
132
|
OpenStruct.new(change_set.merge(changes: changes, status: 'CREATE_COMPLETE'))
|
|
109
133
|
end
|
|
@@ -121,15 +145,16 @@ module StackMaster
|
|
|
121
145
|
|
|
122
146
|
def describe_stacks(options = {})
|
|
123
147
|
stack_name = options[:stack_name]
|
|
124
|
-
stacks =
|
|
125
|
-
if
|
|
148
|
+
stacks =
|
|
149
|
+
if stack_name
|
|
150
|
+
unless @stacks[stack_name]
|
|
151
|
+
raise Aws::CloudFormation::Errors::ValidationError.new('', 'Stack does not exist')
|
|
152
|
+
end
|
|
153
|
+
|
|
126
154
|
[@stacks[stack_name]]
|
|
127
155
|
else
|
|
128
|
-
|
|
156
|
+
@stacks.values
|
|
129
157
|
end
|
|
130
|
-
else
|
|
131
|
-
@stacks.values
|
|
132
|
-
end
|
|
133
158
|
OpenStruct.new(stacks: stacks, next_token: nil)
|
|
134
159
|
end
|
|
135
160
|
|
|
@@ -5,11 +5,10 @@ module StackMaster
|
|
|
5
5
|
reset
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def set_region(_)
|
|
9
|
-
end
|
|
8
|
+
def set_region(_); end
|
|
10
9
|
|
|
11
10
|
def reset
|
|
12
|
-
@files = Hash.new { |hash, key| hash[key] =
|
|
11
|
+
@files = Hash.new { |hash, key| hash[key] = {} }
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
def upload_files(bucket: nil, prefix: nil, region: nil, files: {})
|
data/lib/stack_master/utils.rb
CHANGED
|
@@ -22,17 +22,16 @@ module StackMaster
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def hash_to_aws_parameters(params)
|
|
25
|
-
params.
|
|
25
|
+
params.each_with_object([]) do |(key, value), params|
|
|
26
26
|
params << { parameter_key: key, parameter_value: value }
|
|
27
|
-
params
|
|
28
27
|
end
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
def hash_to_aws_tags(tags)
|
|
32
31
|
return [] if tags.nil?
|
|
33
|
-
|
|
32
|
+
|
|
33
|
+
tags.each_with_object([]) do |(key, value), aws_tags|
|
|
34
34
|
aws_tags << { key: key, value: value }
|
|
35
|
-
aws_tags
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
|
|
@@ -41,9 +40,8 @@ module StackMaster
|
|
|
41
40
|
end
|
|
42
41
|
|
|
43
42
|
def underscore_keys_to_hyphen(hash)
|
|
44
|
-
hash.
|
|
43
|
+
hash.each_with_object({}) do |(key, value), hash|
|
|
45
44
|
hash[underscore_to_hyphen(key)] = value
|
|
46
|
-
hash
|
|
47
45
|
end
|
|
48
46
|
end
|
|
49
47
|
end
|