stack_master 2.17.1 → 2.19.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/bin/stack_master +3 -5
  4. data/lib/stack_master/aws_driver/cloud_formation.rb +4 -4
  5. data/lib/stack_master/aws_driver/s3.rb +7 -7
  6. data/lib/stack_master/change_set.rb +7 -11
  7. data/lib/stack_master/cli.rb +33 -27
  8. data/lib/stack_master/command.rb +1 -5
  9. data/lib/stack_master/commands/apply.rb +34 -19
  10. data/lib/stack_master/commands/delete.rb +4 -4
  11. data/lib/stack_master/commands/drift.rb +5 -9
  12. data/lib/stack_master/commands/events.rb +4 -6
  13. data/lib/stack_master/commands/init.rb +14 -14
  14. data/lib/stack_master/commands/lint.rb +1 -1
  15. data/lib/stack_master/commands/list_stacks.rb +1 -1
  16. data/lib/stack_master/commands/outputs.rb +1 -1
  17. data/lib/stack_master/commands/status.rb +4 -4
  18. data/lib/stack_master/commands/terminal_helper.rb +3 -3
  19. data/lib/stack_master/commands/tidy.rb +15 -20
  20. data/lib/stack_master/config.rb +8 -11
  21. data/lib/stack_master/diff.rb +2 -2
  22. data/lib/stack_master/parameter_loader.rb +32 -31
  23. data/lib/stack_master/parameter_resolver.rb +14 -17
  24. data/lib/stack_master/parameter_resolvers/ami_finder.rb +1 -2
  25. data/lib/stack_master/parameter_resolvers/ejson.rb +4 -4
  26. data/lib/stack_master/parameter_resolvers/latest_ami_by_tags.rb +1 -1
  27. data/lib/stack_master/parameter_resolvers/latest_container.rb +5 -5
  28. data/lib/stack_master/parameter_resolvers/one_password.rb +5 -5
  29. data/lib/stack_master/parameter_resolvers/sso_group_id.rb +1 -1
  30. data/lib/stack_master/parameter_resolvers/stack_output.rb +7 -11
  31. data/lib/stack_master/parameter_validator.rb +1 -5
  32. data/lib/stack_master/prompter.rb +1 -1
  33. data/lib/stack_master/resolver_array.rb +2 -2
  34. data/lib/stack_master/role_assumer.rb +1 -1
  35. data/lib/stack_master/security_group_finder.rb +4 -4
  36. data/lib/stack_master/sns_topic_finder.rb +1 -1
  37. data/lib/stack_master/sparkle_formation/compile_time/allowed_pattern_validator.rb +1 -1
  38. data/lib/stack_master/sparkle_formation/compile_time/definitions_validator.rb +1 -1
  39. data/lib/stack_master/sparkle_formation/compile_time/value_builder.rb +7 -7
  40. data/lib/stack_master/sparkle_formation/compile_time/value_validator.rb +1 -3
  41. data/lib/stack_master/sparkle_formation/template_file.rb +4 -4
  42. data/lib/stack_master/sso_group_id_finder.rb +3 -3
  43. data/lib/stack_master/stack.rb +6 -4
  44. data/lib/stack_master/stack_definition.rb +3 -4
  45. data/lib/stack_master/stack_differ.rb +33 -10
  46. data/lib/stack_master/template_compiler.rb +1 -1
  47. data/lib/stack_master/template_compilers/json.rb +1 -1
  48. data/lib/stack_master/template_compilers/sparkle_formation.rb +59 -57
  49. data/lib/stack_master/template_utils.rb +2 -2
  50. data/lib/stack_master/test_driver/cloud_formation.rb +12 -4
  51. data/lib/stack_master/test_driver/s3.rb +2 -3
  52. data/lib/stack_master/utils.rb +3 -6
  53. data/lib/stack_master/validator.rb +1 -1
  54. data/lib/stack_master/version.rb +1 -1
  55. data/lib/stack_master.rb +1 -1
  56. metadata +8 -7
@@ -49,17 +49,17 @@ module StackMaster
49
49
  def _joined_file(file_name, vars = {})
50
50
  join!(Template.render('joined_file', file_name, vars))
51
51
  end
52
- alias_method :joined_file!, :_joined_file
52
+ alias joined_file! _joined_file
53
53
  end
54
54
 
55
55
  module UserDataFile
56
56
  def _user_data_file(file_name, vars = {})
57
57
  base64!(join!(Template.render('user_data', file_name, vars)))
58
58
  end
59
- alias_method :user_data_file!, :_user_data_file
59
+ alias user_data_file! _user_data_file
60
60
  end
61
61
  end
62
62
  end
63
63
 
64
- SparkleFormation::SparkleAttribute::Aws.send(:include, StackMaster::SparkleFormation::UserDataFile)
65
- SparkleFormation::SparkleAttribute::Aws.send(:include, StackMaster::SparkleFormation::JoinedFile)
64
+ SparkleFormation::SparkleAttribute::Aws.include StackMaster::SparkleFormation::UserDataFile
65
+ SparkleFormation::SparkleAttribute::Aws.include StackMaster::SparkleFormation::JoinedFile
@@ -19,9 +19,9 @@ module StackMaster
19
19
  alternate_identifier: {
20
20
  unique_attribute: {
21
21
  attribute_path: 'displayName',
22
- attribute_value: match[:group_name],
23
- },
24
- },
22
+ attribute_value: match[:group_name]
23
+ }
24
+ }
25
25
  }
26
26
  )
27
27
  return response.group_id
@@ -20,9 +20,8 @@ module StackMaster
20
20
  TemplateUtils
21
21
  .template_hash(template)
22
22
  .fetch('Parameters', {})
23
- .inject({}) do |result, (parameter_name, description)|
23
+ .each_with_object({}) do |(parameter_name, description), result|
24
24
  result[parameter_name] = description['Default']&.to_s
25
- result
26
25
  end
27
26
  end
28
27
 
@@ -35,19 +34,22 @@ module StackMaster
35
34
  cf_stack = cf.describe_stacks({ stack_name: stack_name }).stacks.first
36
35
  return unless cf_stack
37
36
 
38
- parameters = cf_stack.parameters.inject({}) do |params_hash, param_struct|
37
+ parameters = cf_stack.parameters.each_with_object({}) do |param_struct, params_hash|
39
38
  params_hash[param_struct.parameter_key] = param_struct.parameter_value
40
- params_hash
41
39
  end
42
40
  template_body ||= cf.get_template({ stack_name: stack_name, template_stage: 'Original' }).template_body
43
41
  template_format = TemplateUtils.identify_template_format(template_body)
44
42
  stack_policy_body ||= cf.get_stack_policy({ stack_name: stack_name }).stack_policy_body
45
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 || {}
46
47
 
47
48
  new(region: region,
48
49
  stack_name: stack_name,
49
50
  stack_id: cf_stack.stack_id,
50
51
  parameters: parameters,
52
+ tags: tags,
51
53
  template_body: template_body,
52
54
  template_format: template_format,
53
55
  outputs: outputs,
@@ -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 === other &&
47
+ other.is_a?(self.class) &&
48
48
  @region == other.region &&
49
49
  @stack_name == other.stack_name &&
50
50
  @template == other.template &&
@@ -74,13 +74,12 @@ module StackMaster
74
74
  end
75
75
 
76
76
  def s3_files
77
- files.inject({}) do |hash, file|
77
+ files.each_with_object({}) do |file, hash|
78
78
  path = File.join(files_dir, file)
79
79
  hash[file] = {
80
80
  path: path,
81
81
  body: File.read(path)
82
82
  }
83
- hash
84
83
  end
85
84
  end
86
85
 
@@ -1,5 +1,5 @@
1
- require "diffy"
2
- require "hashdiff"
1
+ require 'diffy'
2
+ require 'hashdiff'
3
3
 
4
4
  module StackMaster
5
5
  class StackDiffer
@@ -34,7 +34,7 @@ module StackMaster
34
34
  params = @proposed_stack.parameters_with_defaults
35
35
  if @current_stack
36
36
  noecho_keys.each do |key|
37
- params[key] = "****"
37
+ params[key] = '****'
38
38
  end
39
39
  end
40
40
  YAML.dump(sort_params(params))
@@ -61,20 +61,43 @@ module StackMaster
61
61
  after: proposed_parameters)
62
62
  end
63
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
+
64
88
  def output_diff
65
89
  body_diff.display
66
90
  parameters_diff.display
91
+ tags_diff.display
67
92
 
68
- unless noecho_keys.empty?
69
- StackMaster.stdout.puts " * can not tell if NoEcho parameters are different."
70
- end
71
- 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?
72
95
  end
73
96
 
74
97
  def noecho_keys
75
98
  if @current_stack
76
- @current_stack.parameters_with_defaults.select do |key, value|
77
- value == "****"
99
+ @current_stack.parameters_with_defaults.select do |_key, value|
100
+ value == '****'
78
101
  end.keys
79
102
  else
80
103
  []
@@ -88,7 +111,7 @@ module StackMaster
88
111
  return false if differences.count != 1
89
112
 
90
113
  diff = differences[0]
91
- diff[0] == "~" && diff[1] == param_name
114
+ diff[0] == '~' && diff[1] == param_name
92
115
  end
93
116
 
94
117
  private
@@ -10,7 +10,7 @@ module StackMaster
10
10
  end
11
11
  compiler.require_dependencies
12
12
  compiler.compile(template_dir, template, compile_time_parameters, compiler_options)
13
- rescue StandardError => e
13
+ rescue StandardError
14
14
  raise TemplateCompilationFailed, "Failed to compile #{template}"
15
15
  end
16
16
 
@@ -1,6 +1,6 @@
1
1
  module StackMaster::TemplateCompilers
2
2
  class Json
3
- MAX_TEMPLATE_SIZE = 51200
3
+ MAX_TEMPLATE_SIZE = 51_200
4
4
  private_constant :MAX_TEMPLATE_SIZE
5
5
 
6
6
  def self.require_dependencies
@@ -3,80 +3,82 @@ require 'stack_master/sparkle_formation/compile_time/definitions_validator'
3
3
  require 'stack_master/sparkle_formation/compile_time/state_builder'
4
4
 
5
5
  module StackMaster::TemplateCompilers
6
- class SparkleFormation
6
+ module SparkleFormation
7
7
  CompileTime = StackMaster::SparkleFormation::CompileTime
8
8
 
9
- def self.require_dependencies
10
- require 'sparkle_formation'
11
- require 'stack_master/sparkle_formation/template_file'
12
- end
9
+ class << self
10
+ def require_dependencies
11
+ require 'sparkle_formation'
12
+ require 'stack_master/sparkle_formation/template_file'
13
+ end
13
14
 
14
- def self.compile(template_dir, template, compile_time_parameters, compiler_options = {})
15
- sparkle_template = compile_sparkle_template(template_dir, template, compiler_options)
16
- definitions = sparkle_template.parameters
17
- validate_definitions(definitions)
18
- validate_parameters(definitions, compile_time_parameters)
15
+ def compile(template_dir, template, compile_time_parameters, compiler_options = {})
16
+ sparkle_template = compile_sparkle_template(template_dir, template, compiler_options)
17
+ definitions = sparkle_template.parameters
18
+ validate_definitions(definitions)
19
+ validate_parameters(definitions, compile_time_parameters)
19
20
 
20
- sparkle_template.compile_time_parameter_setter do
21
- sparkle_template.compile_state = create_state(definitions, compile_time_parameters)
21
+ sparkle_template.compile_time_parameter_setter do
22
+ sparkle_template.compile_state = create_state(definitions, compile_time_parameters)
23
+ end
24
+
25
+ JSON.pretty_generate(sparkle_template.dump)
22
26
  end
23
27
 
24
- JSON.pretty_generate(sparkle_template.dump)
25
- end
28
+ private
29
+
30
+ def compile_sparkle_template(template_dir, template, compiler_options)
31
+ sparkle_path =
32
+ if compiler_options['sparkle_path']
33
+ File.expand_path(compiler_options['sparkle_path'])
34
+ else
35
+ template_dir
36
+ end
37
+
38
+ collection = ::SparkleFormation::SparkleCollection.new
39
+ root_pack = ::SparkleFormation::Sparkle.new(
40
+ root: sparkle_path
41
+ )
42
+ collection.set_root(root_pack)
43
+ if compiler_options['sparkle_packs']
44
+ compiler_options['sparkle_packs'].each do |pack_name|
45
+ require pack_name
46
+ pack = ::SparkleFormation::SparklePack.new(name: pack_name)
47
+ collection.add_sparkle(pack)
48
+ end
49
+ end
26
50
 
27
- private
51
+ if compiler_options['sparkle_pack_template']
52
+ unless collection.templates['aws'].include? template
53
+ raise ArgumentError.new("Template #{template.inspect} not found in any sparkle pack")
54
+ end
28
55
 
29
- def self.compile_sparkle_template(template_dir, template, compiler_options)
30
- sparkle_path =
31
- if compiler_options['sparkle_path']
32
- File.expand_path(compiler_options['sparkle_path'])
56
+ template_file_path = collection.templates['aws'][template].top['path']
33
57
  else
34
- template_dir
58
+ template_file_path = File.join(template_dir, template)
35
59
  end
36
60
 
37
- collection = ::SparkleFormation::SparkleCollection.new
38
- root_pack = ::SparkleFormation::Sparkle.new(
39
- :root => sparkle_path,
40
- )
41
- collection.set_root(root_pack)
42
- if compiler_options['sparkle_packs']
43
- compiler_options['sparkle_packs'].each do |pack_name|
44
- require pack_name
45
- pack = ::SparkleFormation::SparklePack.new(:name => pack_name)
46
- collection.add_sparkle(pack)
47
- end
61
+ sparkle_template = compile_template_with_sparkle_path(template_file_path, sparkle_path)
62
+ sparkle_template.sparkle.apply(collection)
63
+ sparkle_template
48
64
  end
49
65
 
50
- if compiler_options['sparkle_pack_template']
51
- unless collection.templates['aws'].include? template
52
- raise ArgumentError.new("Template #{template.inspect} not found in any sparkle pack")
53
- end
54
-
55
- template_file_path = collection.templates['aws'][template].top['path']
56
- else
57
- template_file_path = File.join(template_dir, template)
66
+ def compile_template_with_sparkle_path(template_path, sparkle_path)
67
+ ::SparkleFormation.sparkle_path = sparkle_path
68
+ ::SparkleFormation.compile(template_path, :sparkle)
58
69
  end
59
70
 
60
- sparkle_template = compile_template_with_sparkle_path(template_file_path, sparkle_path)
61
- sparkle_template.sparkle.apply(collection)
62
- sparkle_template
63
- end
64
-
65
- def self.compile_template_with_sparkle_path(template_path, sparkle_path)
66
- ::SparkleFormation.sparkle_path = sparkle_path
67
- ::SparkleFormation.compile(template_path, :sparkle)
68
- end
69
-
70
- def self.validate_definitions(definitions)
71
- CompileTime::DefinitionsValidator.new(definitions).validate
72
- end
71
+ def validate_definitions(definitions)
72
+ CompileTime::DefinitionsValidator.new(definitions).validate
73
+ end
73
74
 
74
- def self.validate_parameters(definitions, compile_time_parameters)
75
- CompileTime::ParametersValidator.new(definitions, compile_time_parameters).validate
76
- end
75
+ def validate_parameters(definitions, compile_time_parameters)
76
+ CompileTime::ParametersValidator.new(definitions, compile_time_parameters).validate
77
+ end
77
78
 
78
- def self.create_state(definitions, compile_time_parameters)
79
- CompileTime::StateBuilder.new(definitions, compile_time_parameters).build
79
+ def create_state(definitions, compile_time_parameters)
80
+ CompileTime::StateBuilder.new(definitions, compile_time_parameters).build
81
+ end
80
82
  end
81
83
 
82
84
  StackMaster::TemplateCompiler.register(:sparkle_formation, self)
@@ -1,7 +1,7 @@
1
1
  module StackMaster
2
2
  module TemplateUtils
3
- MAX_TEMPLATE_SIZE = 51200
4
- MAX_S3_TEMPLATE_SIZE = 460800
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)
@@ -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
@@ -139,11 +147,11 @@ module StackMaster
139
147
  stack_name = options[:stack_name]
140
148
  stacks =
141
149
  if stack_name
142
- if @stacks[stack_name]
143
- [@stacks[stack_name]]
144
- else
150
+ unless @stacks[stack_name]
145
151
  raise Aws::CloudFormation::Errors::ValidationError.new('', 'Stack does not exist')
146
152
  end
153
+
154
+ [@stacks[stack_name]]
147
155
  else
148
156
  @stacks.values
149
157
  end
@@ -184,7 +192,7 @@ module StackMaster
184
192
  @stacks.delete(stack_name)
185
193
  end
186
194
 
187
- def validate_template(options)
195
+ def validate_template(_options)
188
196
  true
189
197
  end
190
198
 
@@ -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] = Hash.new }
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: {})
@@ -22,18 +22,16 @@ module StackMaster
22
22
  end
23
23
 
24
24
  def hash_to_aws_parameters(params)
25
- params.inject([]) do |params, (key, value)|
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
 
34
- tags.inject([]) do |aws_tags, (key, value)|
33
+ tags.each_with_object([]) do |(key, value), aws_tags|
35
34
  aws_tags << { key: key, value: value }
36
- aws_tags
37
35
  end
38
36
  end
39
37
 
@@ -42,9 +40,8 @@ module StackMaster
42
40
  end
43
41
 
44
42
  def underscore_keys_to_hyphen(hash)
45
- hash.inject({}) do |hash, (key, value)|
43
+ hash.each_with_object({}) do |(key, value), hash|
46
44
  hash[underscore_to_hyphen(key)] = value
47
- hash
48
45
  end
49
46
  end
50
47
  end
@@ -17,7 +17,7 @@ module StackMaster
17
17
  return false
18
18
  end
19
19
  cf.validate_template(template_body: TemplateUtils.maybe_compressed_template_body(stack.template_body))
20
- StackMaster.stdout.puts "valid"
20
+ StackMaster.stdout.puts 'valid'
21
21
  true
22
22
  rescue Aws::CloudFormation::Errors::ValidationError => e
23
23
  StackMaster.stdout.puts "invalid. #{e.message}"
@@ -1,3 +1,3 @@
1
1
  module StackMaster
2
- VERSION = "2.17.1"
2
+ VERSION = '2.19.0'
3
3
  end
data/lib/stack_master.rb CHANGED
@@ -172,7 +172,7 @@ module StackMaster
172
172
  @non_interactive_answer = 'y'
173
173
 
174
174
  def base_dir
175
- File.expand_path(File.join(File.dirname(__FILE__), ".."))
175
+ File.expand_path(File.join(File.dirname(__FILE__), '..'))
176
176
  end
177
177
 
178
178
  def cloud_formation_driver
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stack_master
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.17.1
4
+ version: 2.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Hodgkiss
@@ -173,7 +173,7 @@ dependencies:
173
173
  version: 4.6.0
174
174
  - - "<"
175
175
  - !ruby/object:Gem::Version
176
- version: '6'
176
+ version: '7'
177
177
  type: :runtime
178
178
  prerelease: false
179
179
  version_requirements: !ruby/object:Gem::Requirement
@@ -183,7 +183,7 @@ dependencies:
183
183
  version: 4.6.0
184
184
  - - "<"
185
185
  - !ruby/object:Gem::Version
186
- version: '6'
186
+ version: '7'
187
187
  - !ruby/object:Gem::Dependency
188
188
  name: aws-sdk-acm
189
189
  requirement: !ruby/object:Gem::Requirement
@@ -616,14 +616,15 @@ files:
616
616
  - stacktemplates/parameter_stack_name.yml
617
617
  - stacktemplates/stack.json.erb
618
618
  - stacktemplates/stack_master.yml.erb
619
- homepage: https://opensource.envato.com/projects/stack_master.html
619
+ homepage: https://github.com/envato/stack_master
620
620
  licenses:
621
621
  - MIT
622
622
  metadata:
623
+ allowed_push_host: https://rubygems.org
623
624
  bug_tracker_uri: https://github.com/envato/stack_master/issues
624
625
  changelog_uri: https://github.com/envato/stack_master/blob/master/CHANGELOG.md
625
- documentation_uri: https://www.rubydoc.info/gems/stack_master/2.17.1
626
- source_code_uri: https://github.com/envato/stack_master/tree/v2.17.1
626
+ documentation_uri: https://www.rubydoc.info/gems/stack_master/2.19.0
627
+ source_code_uri: https://github.com/envato/stack_master/tree/v2.19.0
627
628
  rdoc_options: []
628
629
  require_paths:
629
630
  - lib
@@ -638,7 +639,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
638
639
  - !ruby/object:Gem::Version
639
640
  version: '0'
640
641
  requirements: []
641
- rubygems_version: 4.0.2
642
+ rubygems_version: 4.0.16
642
643
  specification_version: 4
643
644
  summary: StackMaster is a sure-footed way of creating, updating and keeping track
644
645
  of Amazon (AWS) CloudFormation stacks.