stack_master 2.5.0 → 2.9.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 +43 -2
- data/lib/stack_master.rb +16 -2
- data/lib/stack_master/aws_driver/cloud_formation.rb +4 -1
- data/lib/stack_master/change_set.rb +2 -2
- data/lib/stack_master/cli.rb +12 -0
- data/lib/stack_master/commands/drift.rb +118 -0
- data/lib/stack_master/commands/tidy.rb +1 -1
- data/lib/stack_master/config.rb +3 -0
- data/lib/stack_master/diff.rb +45 -0
- data/lib/stack_master/parameter_loader.rb +3 -4
- data/lib/stack_master/parameter_validator.rb +24 -7
- data/lib/stack_master/stack.rb +2 -2
- data/lib/stack_master/stack_definition.rb +27 -10
- data/lib/stack_master/stack_differ.rb +15 -39
- data/lib/stack_master/stack_events/presenter.rb +1 -1
- data/lib/stack_master/template_utils.rb +9 -3
- data/lib/stack_master/version.rb +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ba7ee171f7c218998ce73593d18bb62165c40a201f088b51192a7cfc1d8fb10
|
4
|
+
data.tar.gz: 368a14a56b4cbd7678d71860902c1e1d360756c6272bd1fc6a7b1b6f5ac8f7c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6eb37d06cd15fbf0852f7c798a18b35747b001d8ffe1edfcdf55c25c629e46d731c47dc5466e8af7275512f6f2dac54b492c714069dfded2c78ab6920bea04af
|
7
|
+
data.tar.gz: 03c242ee5d588b817a53d1bf74e6808c762d37047805fcd79b18c73d5f2e84ce87ed8114efe7ea583f05d71449b4d64b50f4cce7395b72784d9c44837cb9579d
|
data/README.md
CHANGED
@@ -123,6 +123,7 @@ stack_defaults:
|
|
123
123
|
```
|
124
124
|
|
125
125
|
Additional files can be configured to be uploaded to S3 alongside the templates:
|
126
|
+
|
126
127
|
```yaml
|
127
128
|
stacks:
|
128
129
|
production:
|
@@ -131,6 +132,7 @@ stacks:
|
|
131
132
|
files:
|
132
133
|
- userdata.sh
|
133
134
|
```
|
135
|
+
|
134
136
|
## Directories
|
135
137
|
|
136
138
|
- `templates` - CloudFormation, SparkleFormation or CfnDsl templates.
|
@@ -155,7 +157,8 @@ template_compilers:
|
|
155
157
|
|
156
158
|
## Parameters
|
157
159
|
|
158
|
-
|
160
|
+
By default, parameters are loaded from multiple YAML files, merged from the
|
161
|
+
following lookup paths from bottom to top:
|
159
162
|
|
160
163
|
- parameters/[stack_name].yaml
|
161
164
|
- parameters/[stack_name].yml
|
@@ -170,6 +173,30 @@ A simple parameter file could look like this:
|
|
170
173
|
key_name: myapp-us-east-1
|
171
174
|
```
|
172
175
|
|
176
|
+
Alternatively, a `parameter_files` array can be defined to explicitly list
|
177
|
+
parameter files that will be loaded. If `parameter_files` are defined, the
|
178
|
+
automatic search locations will not be used.
|
179
|
+
|
180
|
+
```yaml
|
181
|
+
parameters_dir: parameters # the default
|
182
|
+
stacks:
|
183
|
+
us-east-1:
|
184
|
+
my-app:
|
185
|
+
parameter_files:
|
186
|
+
- my-app.yml # parameters/my-app.yml
|
187
|
+
```
|
188
|
+
|
189
|
+
Parameters can also be defined inline with stack definitions:
|
190
|
+
|
191
|
+
```yaml
|
192
|
+
stacks:
|
193
|
+
us-east-1:
|
194
|
+
my-app:
|
195
|
+
parameters:
|
196
|
+
VpcId:
|
197
|
+
stack_output: my-vpc/VpcId
|
198
|
+
```
|
199
|
+
|
173
200
|
### Compile Time Parameters
|
174
201
|
|
175
202
|
Compile time parameters can be used for [SparkleFormation](http://www.sparkleformation.io) templates. It conforms and
|
@@ -661,6 +688,7 @@ stacks:
|
|
661
688
|
|
662
689
|
In the cases where you want to bypass the account check, there is the StackMaster flag `--skip-account-check` that can be used.
|
663
690
|
|
691
|
+
|
664
692
|
## Commands
|
665
693
|
|
666
694
|
```bash
|
@@ -674,6 +702,7 @@ stack_master apply # Create or update all stacks
|
|
674
702
|
stack_master --changed apply # Create or update all stacks that have changed
|
675
703
|
stack_master --yes apply [region-or-alias] [stack-name] # Create or update a stack non-interactively (forcing yes)
|
676
704
|
stack_master diff [region-or-alias] [stack-name] # Display a stack template and parameter diff
|
705
|
+
stack_master drift [region-or-alias] [stack-name] # Detects and displays stack drift using the CloudFormation Drift API
|
677
706
|
stack_master delete [region-or-alias] [stack-name] # Delete a stack
|
678
707
|
stack_master events [region-or-alias] [stack-name] # Display events for a stack
|
679
708
|
stack_master outputs [region-or-alias] [stack-name] # Display outputs for a stack
|
@@ -682,7 +711,7 @@ stack_master status # Displays the status of each stack
|
|
682
711
|
stack_master tidy # Find missing or extra templates or parameter files
|
683
712
|
```
|
684
713
|
|
685
|
-
## Applying updates
|
714
|
+
## Applying updates - `stack_master apply`
|
686
715
|
|
687
716
|
The apply command does the following:
|
688
717
|
|
@@ -699,6 +728,18 @@ Demo:
|
|
699
728
|
|
700
729
|

|
701
730
|
|
731
|
+
## Drift Detection - `stack_master drift`
|
732
|
+
|
733
|
+
`stack_master drift us-east-1 mystack` uses the CloudFormation APIs to trigger drift detection and display resources
|
734
|
+
that have changed outside of the CloudFormation stack. This can happen if a resource has been updated via the console or
|
735
|
+
CLI directly rather than via a stack update.
|
736
|
+
|
737
|
+
## Diff - `stack_master diff`
|
738
|
+
|
739
|
+
`stack_master diff us-east-1 mystack` displays whether the computed parameters or template differ to what was last
|
740
|
+
applied in CloudFormation. This can happen if the template or computed parameters have changed in code and the change
|
741
|
+
hasn't been applied to this stack.
|
742
|
+
|
702
743
|
## Maintainers
|
703
744
|
|
704
745
|
- [Steve Hodgkiss](https://github.com/stevehodgkiss)
|
data/lib/stack_master.rb
CHANGED
@@ -8,7 +8,7 @@ require 'aws-sdk-s3'
|
|
8
8
|
require 'aws-sdk-sns'
|
9
9
|
require 'aws-sdk-ssm'
|
10
10
|
require 'aws-sdk-iam'
|
11
|
-
require '
|
11
|
+
require 'rainbow'
|
12
12
|
require 'active_support/core_ext/hash/keys'
|
13
13
|
require 'active_support/core_ext/object/blank'
|
14
14
|
require 'active_support/core_ext/string/inflections'
|
@@ -24,6 +24,7 @@ module StackMaster
|
|
24
24
|
autoload :CLI, 'stack_master/cli'
|
25
25
|
autoload :CtrlC, 'stack_master/ctrl_c'
|
26
26
|
autoload :Command, 'stack_master/command'
|
27
|
+
autoload :Diff, 'stack_master/diff'
|
27
28
|
autoload :VERSION, 'stack_master/version'
|
28
29
|
autoload :Stack, 'stack_master/stack'
|
29
30
|
autoload :Prompter, 'stack_master/prompter'
|
@@ -56,6 +57,7 @@ module StackMaster
|
|
56
57
|
module Commands
|
57
58
|
autoload :TerminalHelper, 'stack_master/commands/terminal_helper'
|
58
59
|
autoload :Apply, 'stack_master/commands/apply'
|
60
|
+
autoload :Drift, 'stack_master/commands/drift'
|
59
61
|
autoload :Events, 'stack_master/commands/events'
|
60
62
|
autoload :Outputs, 'stack_master/commands/outputs'
|
61
63
|
autoload :Init, 'stack_master/commands/init'
|
@@ -130,7 +132,7 @@ module StackMaster
|
|
130
132
|
|
131
133
|
def debug(message)
|
132
134
|
return unless debug?
|
133
|
-
stderr.puts "[DEBUG] #{message}".
|
135
|
+
stderr.puts Rainbow("[DEBUG] #{message}").color(:green)
|
134
136
|
end
|
135
137
|
|
136
138
|
def quiet!
|
@@ -198,4 +200,16 @@ module StackMaster
|
|
198
200
|
def stderr=(io)
|
199
201
|
@stderr = io
|
200
202
|
end
|
203
|
+
|
204
|
+
def colorize(text, color)
|
205
|
+
if colorize?
|
206
|
+
Rainbow(text).color(color)
|
207
|
+
else
|
208
|
+
text
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def colorize?
|
213
|
+
ENV.fetch('COLORIZE') { 'true' } == 'true'
|
214
|
+
end
|
201
215
|
end
|
@@ -75,7 +75,7 @@ io.puts "========================================"
|
|
75
75
|
end
|
76
76
|
message = "#{action_name} #{resource_change.resource_type} #{resource_change.logical_resource_id}"
|
77
77
|
color = action_color(action_name)
|
78
|
-
io.puts message.
|
78
|
+
io.puts Rainbow(message).color(color)
|
79
79
|
resource_change.details.each do |detail|
|
80
80
|
display_resource_change_detail(io, action_name, color, detail)
|
81
81
|
end
|
@@ -92,7 +92,7 @@ io.puts "========================================"
|
|
92
92
|
triggered_by << "(#{detail.evaluation})"
|
93
93
|
end
|
94
94
|
detail_messages << "Triggered by: #{triggered_by}"
|
95
|
-
io.puts "- #{detail_messages.join('. ')}. ".
|
95
|
+
io.puts Rainbow("- #{detail_messages.join('. ')}. ").color(color)
|
96
96
|
end
|
97
97
|
|
98
98
|
def action_color(action_name)
|
data/lib/stack_master/cli.rb
CHANGED
@@ -215,6 +215,18 @@ module StackMaster
|
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
218
|
+
command :drift do |c|
|
219
|
+
c.syntax = 'stack_master drift [region_or_alias] [stack_name]'
|
220
|
+
c.summary = 'Detects and displays stack drift using the CloudFormation Drift API'
|
221
|
+
c.description = 'Detects and displays stack drift'
|
222
|
+
c.option '--timeout SECONDS', Integer, "The number of seconds to wait for drift detection to complete"
|
223
|
+
c.example 'view stack drift for a stack named myapp-vpc in us-east-1', 'stack_master drift us-east-1 myapp-vpc'
|
224
|
+
c.action do |args, options|
|
225
|
+
options.default config: default_config_file, timeout: 120
|
226
|
+
execute_stacks_command(StackMaster::Commands::Drift, args, options)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
218
230
|
run!
|
219
231
|
end
|
220
232
|
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'diffy'
|
2
|
+
|
3
|
+
module StackMaster
|
4
|
+
module Commands
|
5
|
+
class Drift
|
6
|
+
include Command
|
7
|
+
include Commander::UI
|
8
|
+
|
9
|
+
DETECTION_COMPLETE_STATES = [
|
10
|
+
'DETECTION_COMPLETE',
|
11
|
+
'DETECTION_FAILED'
|
12
|
+
]
|
13
|
+
|
14
|
+
def perform
|
15
|
+
detect_stack_drift_result = cf.detect_stack_drift(stack_name: stack_name)
|
16
|
+
drift_results = wait_for_drift_results(detect_stack_drift_result.stack_drift_detection_id)
|
17
|
+
|
18
|
+
puts colorize("Drift Status: #{drift_results.stack_drift_status}", stack_drift_status_color(drift_results.stack_drift_status))
|
19
|
+
return if drift_results.stack_drift_status == 'IN_SYNC'
|
20
|
+
|
21
|
+
failed
|
22
|
+
|
23
|
+
resp = cf.describe_stack_resource_drifts(stack_name: stack_name)
|
24
|
+
resp.stack_resource_drifts.each do |drift|
|
25
|
+
display_drift(drift)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def cf
|
32
|
+
@cf ||= StackMaster.cloud_formation_driver
|
33
|
+
end
|
34
|
+
|
35
|
+
def display_drift(drift)
|
36
|
+
color = drift_color(drift)
|
37
|
+
puts colorize([drift.stack_resource_drift_status,
|
38
|
+
drift.resource_type,
|
39
|
+
drift.logical_resource_id,
|
40
|
+
drift.physical_resource_id].join(' '), color)
|
41
|
+
return unless drift.stack_resource_drift_status == 'MODIFIED'
|
42
|
+
|
43
|
+
unless drift.property_differences.empty?
|
44
|
+
puts colorize(' Property differences:', color)
|
45
|
+
end
|
46
|
+
drift.property_differences.each do |property_difference|
|
47
|
+
puts colorize(" - #{property_difference.difference_type} #{property_difference.property_path}", color)
|
48
|
+
end
|
49
|
+
puts colorize(' Resource diff:', color)
|
50
|
+
display_resource_drift(drift)
|
51
|
+
end
|
52
|
+
|
53
|
+
def display_resource_drift(drift)
|
54
|
+
diff = ::StackMaster::Diff.new(before: prettify_json(drift.expected_properties),
|
55
|
+
after: prettify_json(drift.actual_properties))
|
56
|
+
diff.display_colorized_diff
|
57
|
+
end
|
58
|
+
|
59
|
+
def prettify_json(string)
|
60
|
+
JSON.pretty_generate(JSON.parse(string)) + "\n"
|
61
|
+
rescue StandardError => e
|
62
|
+
puts "Failed to prettify drifted resource: #{e.message}"
|
63
|
+
string
|
64
|
+
end
|
65
|
+
|
66
|
+
def stack_drift_status_color(stack_drift_status)
|
67
|
+
case stack_drift_status
|
68
|
+
when 'IN_SYNC'
|
69
|
+
:green
|
70
|
+
when 'DRIFTED'
|
71
|
+
:yellow
|
72
|
+
else
|
73
|
+
:blue
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def drift_color(drift)
|
78
|
+
case drift.stack_resource_drift_status
|
79
|
+
when 'IN_SYNC'
|
80
|
+
:green
|
81
|
+
when 'MODIFIED'
|
82
|
+
:yellow
|
83
|
+
when 'DELETED'
|
84
|
+
:red
|
85
|
+
else
|
86
|
+
:blue
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def wait_for_drift_results(detection_id)
|
91
|
+
resp = nil
|
92
|
+
start_time = Time.now
|
93
|
+
loop do
|
94
|
+
resp = cf.describe_stack_drift_detection_status(stack_drift_detection_id: detection_id)
|
95
|
+
break if DETECTION_COMPLETE_STATES.include?(resp.detection_status)
|
96
|
+
|
97
|
+
elapsed_time = Time.now - start_time
|
98
|
+
if elapsed_time > @options.timeout
|
99
|
+
raise "Timeout waiting for stack drift detection"
|
100
|
+
end
|
101
|
+
|
102
|
+
sleep SLEEP_SECONDS
|
103
|
+
end
|
104
|
+
resp
|
105
|
+
end
|
106
|
+
|
107
|
+
def puts(string)
|
108
|
+
StackMaster.stdout.puts(string)
|
109
|
+
end
|
110
|
+
|
111
|
+
extend Forwardable
|
112
|
+
def_delegators :@stack_definition, :stack_name, :region
|
113
|
+
def_delegators :StackMaster, :colorize
|
114
|
+
|
115
|
+
SLEEP_SECONDS = 1
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -12,7 +12,7 @@ module StackMaster
|
|
12
12
|
parameter_files = Set.new(find_parameter_files())
|
13
13
|
|
14
14
|
status = @config.stacks.each do |stack_definition|
|
15
|
-
parameter_files.subtract(stack_definition.
|
15
|
+
parameter_files.subtract(stack_definition.parameter_files_from_globs)
|
16
16
|
template = File.absolute_path(stack_definition.template_file_path)
|
17
17
|
|
18
18
|
if template
|
data/lib/stack_master/config.rb
CHANGED
@@ -17,6 +17,7 @@ module StackMaster
|
|
17
17
|
attr_accessor :stacks,
|
18
18
|
:base_dir,
|
19
19
|
:template_dir,
|
20
|
+
:parameters_dir,
|
20
21
|
:stack_defaults,
|
21
22
|
:region_defaults,
|
22
23
|
:region_aliases,
|
@@ -39,6 +40,7 @@ module StackMaster
|
|
39
40
|
@config = config
|
40
41
|
@base_dir = base_dir
|
41
42
|
@template_dir = config.fetch('template_dir', nil)
|
43
|
+
@parameters_dir = config.fetch('parameters_dir', nil)
|
42
44
|
@stack_defaults = config.fetch('stack_defaults', {})
|
43
45
|
@region_aliases = Utils.underscore_keys_to_hyphen(config.fetch('region_aliases', {}))
|
44
46
|
@region_to_aliases = @region_aliases.inject({}) do |hash, (key, value)|
|
@@ -115,6 +117,7 @@ module StackMaster
|
|
115
117
|
'stack_name' => stack_name,
|
116
118
|
'base_dir' => @base_dir,
|
117
119
|
'template_dir' => @template_dir,
|
120
|
+
'parameters_dir' => @parameters_dir,
|
118
121
|
'additional_parameter_lookup_dirs' => @region_to_aliases[region])
|
119
122
|
stack_attributes['allowed_accounts'] = attributes['allowed_accounts'] if attributes['allowed_accounts']
|
120
123
|
@stacks << StackDefinition.new(stack_attributes)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module StackMaster
|
2
|
+
class Diff
|
3
|
+
def initialize(name: nil, before:, after:, context: 10_000)
|
4
|
+
@name = name
|
5
|
+
@before = before
|
6
|
+
@after = after
|
7
|
+
@context = context
|
8
|
+
end
|
9
|
+
|
10
|
+
def display
|
11
|
+
stdout.print "#{@name} diff: "
|
12
|
+
if diff == ''
|
13
|
+
stdout.puts "No changes"
|
14
|
+
else
|
15
|
+
stdout.puts
|
16
|
+
display_colorized_diff
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def display_colorized_diff
|
21
|
+
diff.each_line do |line|
|
22
|
+
if line.start_with?('+')
|
23
|
+
stdout.print colorize(line, :green)
|
24
|
+
elsif line.start_with?('-')
|
25
|
+
stdout.print colorize(line, :red)
|
26
|
+
else
|
27
|
+
stdout.print line
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def different?
|
33
|
+
diff != ''
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def diff
|
39
|
+
@diff ||= Diffy::Diff.new(@before, @after, context: @context).to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
extend Forwardable
|
43
|
+
def_delegators :StackMaster, :colorize, :stdout
|
44
|
+
end
|
45
|
+
end
|
@@ -5,10 +5,10 @@ module StackMaster
|
|
5
5
|
|
6
6
|
COMPILE_TIME_PARAMETERS_KEY = 'compile_time_parameters'
|
7
7
|
|
8
|
-
def self.load(parameter_files)
|
8
|
+
def self.load(parameter_files: [], parameters: {})
|
9
9
|
StackMaster.debug 'Searching for parameter files...'
|
10
|
-
parameter_files.
|
11
|
-
|
10
|
+
all_parameters = parameter_files.map { |file_name| load_parameters(file_name) } + [parameters]
|
11
|
+
all_parameters.reduce({template_parameters: {}, compile_time_parameters: {}}) do |hash, parameters|
|
12
12
|
template_parameters = create_template_parameters(parameters)
|
13
13
|
compile_time_parameters = create_compile_time_parameters(parameters)
|
14
14
|
|
@@ -16,7 +16,6 @@ module StackMaster
|
|
16
16
|
merge_and_camelize(hash[:compile_time_parameters], compile_time_parameters)
|
17
17
|
hash
|
18
18
|
end
|
19
|
-
|
20
19
|
end
|
21
20
|
|
22
21
|
private
|
@@ -9,15 +9,14 @@ module StackMaster
|
|
9
9
|
|
10
10
|
def error_message
|
11
11
|
return nil unless missing_parameters?
|
12
|
-
message = "Empty/blank parameters detected. Please provide values for these parameters
|
12
|
+
message = "Empty/blank parameters detected. Please provide values for these parameters:\n"
|
13
13
|
missing_parameters.each do |parameter_name|
|
14
|
-
message << "
|
14
|
+
message << " - #{parameter_name}\n"
|
15
15
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
message << "\n - #{parameter_file}"
|
16
|
+
if @stack_definition.parameter_files.empty?
|
17
|
+
message << message_for_parameter_globs
|
18
|
+
else
|
19
|
+
message << message_for_parameter_files
|
21
20
|
end
|
22
21
|
message
|
23
22
|
end
|
@@ -28,6 +27,24 @@ module StackMaster
|
|
28
27
|
|
29
28
|
private
|
30
29
|
|
30
|
+
def message_for_parameter_files
|
31
|
+
"Parameters are configured to be read from the following files:\n".tap do |message|
|
32
|
+
@stack_definition.parameter_files.each do |parameter_file|
|
33
|
+
message << " - #{parameter_file}\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def message_for_parameter_globs
|
39
|
+
"Parameters will be read from files matching the following globs:\n".tap do |message|
|
40
|
+
base_dir = Pathname.new(@stack_definition.base_dir)
|
41
|
+
@stack_definition.parameter_file_globs.each do |glob|
|
42
|
+
parameter_file = Pathname.new(glob).relative_path_from(base_dir)
|
43
|
+
message << " - #{parameter_file}\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
31
48
|
def missing_parameters
|
32
49
|
@missing_parameters ||=
|
33
50
|
@stack.parameters_with_defaults.select { |_key, value| value.nil? }.keys
|
data/lib/stack_master/stack.rb
CHANGED
@@ -56,7 +56,7 @@ module StackMaster
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def self.generate(stack_definition, config)
|
59
|
-
parameter_hash = ParameterLoader.load(stack_definition.
|
59
|
+
parameter_hash = ParameterLoader.load(parameter_files: stack_definition.all_parameter_files, parameters: stack_definition.parameters)
|
60
60
|
template_parameters = ParameterResolver.resolve(config, stack_definition, parameter_hash[:template_parameters])
|
61
61
|
compile_time_parameters = ParameterResolver.resolve(config, stack_definition, parameter_hash[:compile_time_parameters])
|
62
62
|
template_body = TemplateCompiler.compile(config, stack_definition.compiler, stack_definition.template_dir, stack_definition.template, compile_time_parameters, stack_definition.compiler_options)
|
@@ -76,7 +76,7 @@ module StackMaster
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def self.generate_without_parameters(stack_definition, config)
|
79
|
-
parameter_hash = ParameterLoader.load(stack_definition.
|
79
|
+
parameter_hash = ParameterLoader.load(parameter_files: stack_definition.all_parameter_files, parameters: stack_definition.parameters)
|
80
80
|
compile_time_parameters = ParameterResolver.resolve(config, stack_definition, parameter_hash[:compile_time_parameters])
|
81
81
|
template_body = TemplateCompiler.compile(config, stack_definition.compiler, stack_definition.template_dir, stack_definition.template, compile_time_parameters, stack_definition.compiler_options)
|
82
82
|
template_format = TemplateUtils.identify_template_format(template_body)
|
@@ -16,7 +16,10 @@ module StackMaster
|
|
16
16
|
:additional_parameter_lookup_dirs,
|
17
17
|
:s3,
|
18
18
|
:files,
|
19
|
-
:compiler_options
|
19
|
+
:compiler_options,
|
20
|
+
:parameters_dir,
|
21
|
+
:parameters,
|
22
|
+
:parameter_files
|
20
23
|
|
21
24
|
attr_reader :compiler
|
22
25
|
|
@@ -32,8 +35,12 @@ module StackMaster
|
|
32
35
|
@compiler = nil
|
33
36
|
super
|
34
37
|
@additional_parameter_lookup_dirs ||= []
|
38
|
+
@base_dir ||= ""
|
35
39
|
@template_dir ||= File.join(@base_dir, 'templates')
|
40
|
+
@parameters_dir ||= File.join(@base_dir, 'parameters')
|
36
41
|
@allowed_accounts = Array(@allowed_accounts)
|
42
|
+
@parameters ||= {}
|
43
|
+
@parameter_files ||= []
|
37
44
|
end
|
38
45
|
|
39
46
|
def ==(other)
|
@@ -56,13 +63,9 @@ module StackMaster
|
|
56
63
|
@compiler_options == other.compiler_options
|
57
64
|
end
|
58
65
|
|
59
|
-
def compiler=(compiler)
|
60
|
-
@compiler = compiler.&to_sym
|
61
|
-
end
|
62
|
-
|
63
66
|
def template_file_path
|
64
67
|
return unless template
|
65
|
-
File.expand_path(
|
68
|
+
File.expand_path(template, template_dir)
|
66
69
|
end
|
67
70
|
|
68
71
|
def files_dir
|
@@ -85,7 +88,15 @@ module StackMaster
|
|
85
88
|
Utils.change_extension(template, 'json')
|
86
89
|
end
|
87
90
|
|
88
|
-
def
|
91
|
+
def all_parameter_files
|
92
|
+
if parameter_files.empty?
|
93
|
+
parameter_files_from_globs
|
94
|
+
else
|
95
|
+
parameter_files
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def parameter_files_from_globs
|
89
100
|
parameter_file_globs.map(&Dir.method(:glob)).flatten
|
90
101
|
end
|
91
102
|
|
@@ -101,20 +112,26 @@ module StackMaster
|
|
101
112
|
!s3.nil?
|
102
113
|
end
|
103
114
|
|
115
|
+
def parameter_files
|
116
|
+
Array(@parameter_files).map do |file|
|
117
|
+
File.expand_path(file, parameters_dir)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
104
121
|
private
|
105
122
|
|
106
123
|
def additional_parameter_lookup_globs
|
107
124
|
additional_parameter_lookup_dirs.map do |a|
|
108
|
-
File.join(
|
125
|
+
File.join(parameters_dir, a, "#{stack_name_glob}.y*ml")
|
109
126
|
end
|
110
127
|
end
|
111
128
|
|
112
129
|
def region_parameter_glob
|
113
|
-
File.join(
|
130
|
+
File.join(parameters_dir, "#{region}", "#{stack_name_glob}.y*ml")
|
114
131
|
end
|
115
132
|
|
116
133
|
def default_parameter_glob
|
117
|
-
File.join(
|
134
|
+
File.join(parameters_dir, "#{stack_name_glob}.y*ml")
|
118
135
|
end
|
119
136
|
|
120
137
|
def stack_name_glob
|
@@ -10,13 +10,13 @@ module StackMaster
|
|
10
10
|
|
11
11
|
def proposed_template
|
12
12
|
return @proposed_stack.template_body unless @proposed_stack.template_format == :json
|
13
|
-
JSON.pretty_generate(JSON.parse(@proposed_stack.template_body))
|
13
|
+
JSON.pretty_generate(JSON.parse(@proposed_stack.template_body)) + "\n"
|
14
14
|
end
|
15
15
|
|
16
16
|
def current_template
|
17
17
|
return '' unless @current_stack
|
18
18
|
return @current_stack.template_body unless @current_stack.template_format == :json
|
19
|
-
JSON.pretty_generate(TemplateUtils.template_hash(@current_stack.template_body))
|
19
|
+
JSON.pretty_generate(TemplateUtils.template_hash(@current_stack.template_body)) + "\n"
|
20
20
|
end
|
21
21
|
|
22
22
|
def current_parameters
|
@@ -39,24 +39,30 @@ module StackMaster
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def body_different?
|
42
|
-
body_diff
|
42
|
+
body_diff.different?
|
43
43
|
end
|
44
44
|
|
45
45
|
def body_diff
|
46
|
-
@body_diff ||=
|
46
|
+
@body_diff ||= Diff.new(name: 'Stack',
|
47
|
+
before: current_template,
|
48
|
+
after: proposed_template,
|
49
|
+
context: 7)
|
47
50
|
end
|
48
51
|
|
49
52
|
def params_different?
|
50
|
-
|
53
|
+
parameters_diff.different?
|
51
54
|
end
|
52
55
|
|
53
|
-
def
|
54
|
-
@param_diff ||=
|
56
|
+
def parameters_diff
|
57
|
+
@param_diff ||= Diff.new(name: 'Parameters',
|
58
|
+
before: current_parameters,
|
59
|
+
after: proposed_parameters)
|
55
60
|
end
|
56
61
|
|
57
62
|
def output_diff
|
58
|
-
|
59
|
-
|
63
|
+
body_diff.display
|
64
|
+
parameters_diff.display
|
65
|
+
|
60
66
|
unless noecho_keys.empty?
|
61
67
|
StackMaster.stdout.puts " * can not tell if NoEcho parameters are different."
|
62
68
|
end
|
@@ -83,38 +89,8 @@ module StackMaster
|
|
83
89
|
|
84
90
|
private
|
85
91
|
|
86
|
-
def display_diff(thing, diff)
|
87
|
-
StackMaster.stdout.print "#{thing} diff: "
|
88
|
-
if diff == ''
|
89
|
-
StackMaster.stdout.puts "No changes"
|
90
|
-
else
|
91
|
-
StackMaster.stdout.puts
|
92
|
-
diff.each_line do |line|
|
93
|
-
if line.start_with?('+')
|
94
|
-
StackMaster.stdout.print colorize(line, :green)
|
95
|
-
elsif line.start_with?('-')
|
96
|
-
StackMaster.stdout.print colorize(line, :red)
|
97
|
-
else
|
98
|
-
StackMaster.stdout.print line
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
92
|
def sort_params(hash)
|
105
93
|
hash.sort.to_h
|
106
94
|
end
|
107
|
-
|
108
|
-
def colorize(text, color)
|
109
|
-
if colorize?
|
110
|
-
text.colorize(color)
|
111
|
-
else
|
112
|
-
text
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def colorize?
|
117
|
-
ENV.fetch('COLORIZE') { 'true' } == 'true'
|
118
|
-
end
|
119
95
|
end
|
120
96
|
end
|
@@ -10,7 +10,7 @@ module StackMaster
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def print_event(event)
|
13
|
-
@io.puts "#{event.timestamp.localtime} #{event.logical_resource_id} #{event.resource_type} #{event.resource_status} #{event.resource_status_reason}".
|
13
|
+
@io.puts Rainbow("#{event.timestamp.localtime} #{event.logical_resource_id} #{event.resource_type} #{event.resource_status} #{event.resource_status_reason}").color(event_colour(event))
|
14
14
|
end
|
15
15
|
|
16
16
|
def event_colour(event)
|
@@ -2,12 +2,18 @@ module StackMaster
|
|
2
2
|
module TemplateUtils
|
3
3
|
MAX_TEMPLATE_SIZE = 51200
|
4
4
|
MAX_S3_TEMPLATE_SIZE = 460800
|
5
|
+
# Matches if the first non-whitespace character is a '{', handling cases
|
6
|
+
# with leading whitespace and extra (whitespace-only) lines.
|
7
|
+
JSON_IDENTIFICATION_PATTERN = Regexp.new('\A\s*{', Regexp::MULTILINE)
|
5
8
|
|
6
9
|
extend self
|
7
10
|
|
8
11
|
def identify_template_format(template_body)
|
9
|
-
|
10
|
-
|
12
|
+
if template_body =~ JSON_IDENTIFICATION_PATTERN
|
13
|
+
:json
|
14
|
+
else
|
15
|
+
:yaml
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
19
|
def template_hash(template_body=nil)
|
@@ -28,4 +34,4 @@ module StackMaster
|
|
28
34
|
JSON.dump(template_hash(template_body))
|
29
35
|
end
|
30
36
|
end
|
31
|
-
end
|
37
|
+
end
|
data/lib/stack_master/version.rb
CHANGED
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.
|
4
|
+
version: 2.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Hodgkiss
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-
|
12
|
+
date: 2020-06-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -298,7 +298,7 @@ dependencies:
|
|
298
298
|
- !ruby/object:Gem::Version
|
299
299
|
version: '0'
|
300
300
|
- !ruby/object:Gem::Dependency
|
301
|
-
name:
|
301
|
+
name: rainbow
|
302
302
|
requirement: !ruby/object:Gem::Requirement
|
303
303
|
requirements:
|
304
304
|
- - ">="
|
@@ -459,6 +459,7 @@ files:
|
|
459
459
|
- lib/stack_master/commands/compile.rb
|
460
460
|
- lib/stack_master/commands/delete.rb
|
461
461
|
- lib/stack_master/commands/diff.rb
|
462
|
+
- lib/stack_master/commands/drift.rb
|
462
463
|
- lib/stack_master/commands/events.rb
|
463
464
|
- lib/stack_master/commands/init.rb
|
464
465
|
- lib/stack_master/commands/lint.rb
|
@@ -471,6 +472,7 @@ files:
|
|
471
472
|
- lib/stack_master/commands/validate.rb
|
472
473
|
- lib/stack_master/config.rb
|
473
474
|
- lib/stack_master/ctrl_c.rb
|
475
|
+
- lib/stack_master/diff.rb
|
474
476
|
- lib/stack_master/identity.rb
|
475
477
|
- lib/stack_master/paged_response_accumulator.rb
|
476
478
|
- lib/stack_master/parameter_loader.rb
|
@@ -539,8 +541,8 @@ licenses:
|
|
539
541
|
metadata:
|
540
542
|
bug_tracker_uri: https://github.com/envato/stack_master/issues
|
541
543
|
changelog_uri: https://github.com/envato/stack_master/blob/master/CHANGELOG.md
|
542
|
-
documentation_uri: https://www.rubydoc.info/gems/stack_master/2.
|
543
|
-
source_code_uri: https://github.com/envato/stack_master/tree/v2.
|
544
|
+
documentation_uri: https://www.rubydoc.info/gems/stack_master/2.9.0
|
545
|
+
source_code_uri: https://github.com/envato/stack_master/tree/v2.9.0
|
544
546
|
post_install_message:
|
545
547
|
rdoc_options: []
|
546
548
|
require_paths:
|
@@ -556,7 +558,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
556
558
|
- !ruby/object:Gem::Version
|
557
559
|
version: '0'
|
558
560
|
requirements: []
|
559
|
-
rubygems_version: 3.
|
561
|
+
rubygems_version: 3.0.3
|
560
562
|
signing_key:
|
561
563
|
specification_version: 4
|
562
564
|
summary: StackMaster is a sure-footed way of creating, updating and keeping track
|