stack_master 2.17.0 → 2.17.1
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 +0 -5
- data/lib/stack_master/aws_driver/cloud_formation.rb +21 -19
- data/lib/stack_master/aws_driver/s3.rb +18 -11
- data/lib/stack_master/change_set.rb +5 -5
- data/lib/stack_master/cli.rb +24 -9
- data/lib/stack_master/commands/apply.rb +5 -1
- data/lib/stack_master/commands/delete.rb +2 -3
- data/lib/stack_master/commands/drift.rb +6 -3
- data/lib/stack_master/commands/init.rb +6 -5
- data/lib/stack_master/commands/nag.rb +0 -1
- data/lib/stack_master/commands/resources.rb +9 -1
- data/lib/stack_master/commands/tidy.rb +4 -1
- data/lib/stack_master/config.rb +16 -11
- data/lib/stack_master/identity.rb +2 -1
- data/lib/stack_master/parameter_loader.rb +3 -4
- data/lib/stack_master/parameter_resolver.rb +4 -1
- data/lib/stack_master/parameter_resolvers/acm_certificate.rb +4 -1
- data/lib/stack_master/parameter_resolvers/ami_finder.rb +1 -1
- data/lib/stack_master/parameter_resolvers/ejson.rb +7 -4
- data/lib/stack_master/parameter_resolvers/env.rb +1 -2
- data/lib/stack_master/parameter_resolvers/latest_container.rb +6 -4
- data/lib/stack_master/parameter_resolvers/one_password.rb +7 -3
- 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 +2 -1
- data/lib/stack_master/parameter_resolvers/stack_output.rb +3 -1
- data/lib/stack_master/parameter_validator.rb +1 -0
- data/lib/stack_master/prompter.rb +11 -10
- data/lib/stack_master/resolver_array.rb +0 -1
- data/lib/stack_master/role_assumer.rb +7 -5
- data/lib/stack_master/security_group_finder.rb +3 -1
- data/lib/stack_master/sns_topic_finder.rb +3 -2
- data/lib/stack_master/sparkle_formation/compile_time/allowed_pattern_validator.rb +1 -2
- 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 +5 -5
- 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 +0 -2
- data/lib/stack_master/sparkle_formation/compile_time/value_validator.rb +2 -3
- data/lib/stack_master/sparkle_formation/compile_time/value_validator_factory.rb +11 -13
- data/lib/stack_master/sparkle_formation/template_file.rb +0 -2
- data/lib/stack_master/sso_group_id_finder.rb +14 -11
- data/lib/stack_master/stack.rb +47 -16
- data/lib/stack_master/stack_definition.rb +3 -1
- data/lib/stack_master/stack_differ.rb +4 -0
- 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/sparkle_formation.rb +10 -7
- data/lib/stack_master/template_utils.rb +6 -2
- data/lib/stack_master/test_driver/cloud_formation.rb +27 -10
- data/lib/stack_master/utils.rb +1 -0
- data/lib/stack_master/validator.rb +6 -5
- data/lib/stack_master/version.rb +1 -1
- data/lib/stack_master.rb +2 -0
- metadata +19 -9
- data/lib/stack_master/parameter_resolvers/accounts_by_tags.rb +0 -60
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e73f1b3d79a230203a083898d14d256ac7c61fb0dd4a49627af097759341ca96
|
|
4
|
+
data.tar.gz: 193c5824b8972d6ddc36f66dd23d83360c1af43562a3b038c413bb6ff624626a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41d00dda80224d66ae46e73746b43f1e0738ef075ad8ce3122494db579c545b6cd2b15497622cf5aa683efa2bf6f67f411a031c1cb5063c0a94b40dcef8261a0
|
|
7
|
+
data.tar.gz: e26a3fa7eeae1b2be191fa579e585f34f8f781c4b94e68d42c0619a3d1effbe86cd0761b119a79058a86d98f113d6db573dfbfd0b733792ca9d21554de98684c
|
data/README.md
CHANGED
|
@@ -794,11 +794,6 @@ CLI directly rather than via a stack update.
|
|
|
794
794
|
applied in CloudFormation. This can happen if the template or computed parameters have changed in code and the change
|
|
795
795
|
hasn't been applied to this stack.
|
|
796
796
|
|
|
797
|
-
## Maintainers
|
|
798
|
-
|
|
799
|
-
- [Steve Hodgkiss](https://github.com/stevehodgkiss)
|
|
800
|
-
- [Glen Stampoultzis](https://github.com/gstamp)
|
|
801
|
-
|
|
802
797
|
## License
|
|
803
798
|
|
|
804
799
|
StackMaster uses the MIT license. See [LICENSE.txt](https://github.com/envato/stack_master/blob/master/LICENSE.txt) for details.
|
|
@@ -14,31 +14,33 @@ module StackMaster
|
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def_delegators
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
def_delegators(
|
|
18
|
+
:cf,
|
|
19
|
+
:create_change_set,
|
|
20
|
+
:describe_change_set,
|
|
21
|
+
:execute_change_set,
|
|
22
|
+
:delete_change_set,
|
|
23
|
+
:delete_stack,
|
|
24
|
+
:cancel_update_stack,
|
|
25
|
+
:describe_stack_resources,
|
|
26
|
+
:get_template,
|
|
27
|
+
:get_stack_policy,
|
|
28
|
+
:set_stack_policy,
|
|
29
|
+
:describe_stack_events,
|
|
30
|
+
:update_stack,
|
|
31
|
+
:create_stack,
|
|
32
|
+
:validate_template,
|
|
33
|
+
:describe_stacks,
|
|
34
|
+
:detect_stack_drift,
|
|
35
|
+
:describe_stack_drift_detection_status,
|
|
36
|
+
:describe_stack_resource_drifts
|
|
37
|
+
)
|
|
35
38
|
|
|
36
39
|
private
|
|
37
40
|
|
|
38
41
|
def cf
|
|
39
42
|
@cf ||= Aws::CloudFormation::Client.new({ region: region, retry_limit: 10 })
|
|
40
43
|
end
|
|
41
|
-
|
|
42
44
|
end
|
|
43
45
|
end
|
|
44
46
|
end
|
|
@@ -11,16 +11,20 @@ module StackMaster
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def upload_files(bucket: nil, prefix: nil, region: nil, files: {})
|
|
14
|
-
|
|
14
|
+
unless bucket
|
|
15
|
+
raise StackMaster::AwsDriver::S3ConfigurationError, 'A bucket must be specified in order to use S3'
|
|
16
|
+
end
|
|
15
17
|
|
|
16
18
|
return if files.empty?
|
|
17
19
|
|
|
18
20
|
s3 = new_s3_client(region: region)
|
|
19
21
|
|
|
20
|
-
current_objects = s3.list_objects(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
current_objects = s3.list_objects(
|
|
23
|
+
{
|
|
24
|
+
prefix: prefix,
|
|
25
|
+
bucket: bucket
|
|
26
|
+
}
|
|
27
|
+
).map(&:contents).flatten.inject({}) { |h, obj|
|
|
24
28
|
h.merge(obj.key => obj)
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -35,15 +39,18 @@ module StackMaster
|
|
|
35
39
|
s3_md5 = current_objects[object_key] ? current_objects[object_key].etag.gsub("\"", '') : nil
|
|
36
40
|
|
|
37
41
|
next if compiled_template_md5 == s3_md5
|
|
42
|
+
|
|
38
43
|
s3_uri = "s3://#{bucket}/#{object_key}"
|
|
39
44
|
StackMaster.stdout.print "- #{File.basename(path)} => #{s3_uri} "
|
|
40
45
|
|
|
41
|
-
s3.put_object(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
s3.put_object(
|
|
47
|
+
{
|
|
48
|
+
bucket: bucket,
|
|
49
|
+
key: object_key,
|
|
50
|
+
body: body,
|
|
51
|
+
metadata: { md5: compiled_template_md5 }
|
|
52
|
+
}
|
|
53
|
+
)
|
|
47
54
|
StackMaster.stdout.puts "done."
|
|
48
55
|
end
|
|
49
56
|
end
|
|
@@ -42,15 +42,15 @@ module StackMaster
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def display(io)
|
|
45
|
-
io.puts
|
|
45
|
+
io.puts <<~EOL
|
|
46
46
|
|
|
47
|
-
========================================
|
|
48
|
-
Proposed change set:
|
|
49
|
-
EOL
|
|
47
|
+
========================================
|
|
48
|
+
Proposed change set:
|
|
49
|
+
EOL
|
|
50
50
|
@response.changes.each do |change|
|
|
51
51
|
display_resource_change(io, change.resource_change)
|
|
52
52
|
end
|
|
53
|
-
io.puts "========================================"
|
|
53
|
+
io.puts "========================================"
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def failed?
|
data/lib/stack_master/cli.rb
CHANGED
|
@@ -5,7 +5,7 @@ module StackMaster
|
|
|
5
5
|
class CLI
|
|
6
6
|
include Commander::Methods
|
|
7
7
|
|
|
8
|
-
def initialize(argv, stdin=STDIN, stdout=STDOUT, stderr=STDERR, kernel=Kernel)
|
|
8
|
+
def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
|
|
9
9
|
@argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
|
|
10
10
|
Commander::Runner.instance_variable_set('@instance', Commander::Runner.new(argv))
|
|
11
11
|
StackMaster.stdout = @stdout
|
|
@@ -41,9 +41,14 @@ module StackMaster
|
|
|
41
41
|
command :apply do |c|
|
|
42
42
|
c.syntax = 'stack_master apply [region_or_alias] [stack_name]'
|
|
43
43
|
c.summary = 'Creates or updates a stack'
|
|
44
|
-
c.description = "Creates or updates a stack. Shows a diff of the proposed stack's template and parameters.
|
|
44
|
+
c.description = "Creates or updates a stack. Shows a diff of the proposed stack's template and parameters. " \
|
|
45
|
+
'Tails stack events until CloudFormation has completed.'
|
|
45
46
|
c.example 'update a stack named myapp-vpc in us-east-1', 'stack_master apply us-east-1 myapp-vpc'
|
|
46
|
-
c.option '--on-failure ACTION', String,
|
|
47
|
+
c.option '--on-failure ACTION', String,
|
|
48
|
+
'Action to take on CREATE_FAILURE. ' \
|
|
49
|
+
'Valid Values: [ DO_NOTHING | ROLLBACK | DELETE ]. ' \
|
|
50
|
+
"Default: ROLLBACK\n" \
|
|
51
|
+
'Note: You cannot use this option with Serverless Application Model (SAM) templates.'
|
|
47
52
|
c.option '--yes-param PARAM_NAME', String, "Auto-approve stack updates when only parameter PARAM_NAME changes"
|
|
48
53
|
c.action do |args, options|
|
|
49
54
|
options.default config: default_config_file
|
|
@@ -171,11 +176,13 @@ module StackMaster
|
|
|
171
176
|
command :status do |c|
|
|
172
177
|
c.syntax = 'stack_master status'
|
|
173
178
|
c.summary = 'Check the current status stacks.'
|
|
174
|
-
c.description = 'Checks the status of all stacks defined in the stack_master.yml file.
|
|
179
|
+
c.description = 'Checks the status of all stacks defined in the stack_master.yml file. ' \
|
|
180
|
+
'Warning this operation can be somewhat slow.'
|
|
175
181
|
c.example 'description', 'Check the status of all stack definitions'
|
|
176
182
|
c.action do |args, options|
|
|
177
183
|
options.default config: default_config_file
|
|
178
184
|
say "Invalid arguments. stack_master status" and return unless args.size == 0
|
|
185
|
+
|
|
179
186
|
config = load_config(options.config)
|
|
180
187
|
StackMaster::Commands::Status.perform(config, nil, options)
|
|
181
188
|
end
|
|
@@ -184,11 +191,13 @@ module StackMaster
|
|
|
184
191
|
command :tidy do |c|
|
|
185
192
|
c.syntax = 'stack_master tidy'
|
|
186
193
|
c.summary = 'Try to identify extra & missing files.'
|
|
187
|
-
c.description = 'Cross references stack_master.yml with the template
|
|
194
|
+
c.description = 'Cross references stack_master.yml with the template ' \
|
|
195
|
+
'and parameter directories to identify extra or missing files.'
|
|
188
196
|
c.example 'description', 'Check for missing or extra files'
|
|
189
197
|
c.action do |args, options|
|
|
190
198
|
options.default config: default_config_file
|
|
191
199
|
say "Invalid arguments. stack_master tidy" and return unless args.size == 0
|
|
200
|
+
|
|
192
201
|
config = load_config(options.config)
|
|
193
202
|
StackMaster::Commands::Tidy.perform(config, nil, options)
|
|
194
203
|
end
|
|
@@ -269,11 +278,14 @@ module StackMaster
|
|
|
269
278
|
success = false
|
|
270
279
|
end
|
|
271
280
|
stack_definitions = stack_definitions.select do |stack_definition|
|
|
272
|
-
running_in_allowed_account?(stack_definition.allowed_accounts) &&
|
|
281
|
+
running_in_allowed_account?(stack_definition.allowed_accounts) &&
|
|
282
|
+
StackStatus.new(config, stack_definition).changed?
|
|
273
283
|
end if options.changed
|
|
274
284
|
stack_definitions.each do |stack_definition|
|
|
275
285
|
StackMaster.cloud_formation_driver.set_region(stack_definition.region)
|
|
276
|
-
StackMaster.stdout.puts
|
|
286
|
+
StackMaster.stdout.puts(
|
|
287
|
+
"Executing #{command.command_name} on #{stack_definition.stack_name} in #{stack_definition.region}"
|
|
288
|
+
)
|
|
277
289
|
success = execute_if_allowed_account(stack_definition.allowed_accounts) do
|
|
278
290
|
command.perform(config, stack_definition, options).success?
|
|
279
291
|
end
|
|
@@ -283,7 +295,7 @@ module StackMaster
|
|
|
283
295
|
end
|
|
284
296
|
|
|
285
297
|
def show_other_region_candidates(config, stack_name)
|
|
286
|
-
candidates = config.filter(region="", stack_name=stack_name)
|
|
298
|
+
candidates = config.filter(region = "", stack_name = stack_name)
|
|
287
299
|
return if candidates.empty?
|
|
288
300
|
|
|
289
301
|
StackMaster.stdout.puts "Stack name #{stack_name} exists in regions: #{candidates.map(&:region).join(', ')}"
|
|
@@ -291,12 +303,15 @@ module StackMaster
|
|
|
291
303
|
|
|
292
304
|
def execute_if_allowed_account(allowed_accounts, &block)
|
|
293
305
|
raise ArgumentError, "Block required to execute this method" unless block_given?
|
|
306
|
+
|
|
294
307
|
if running_in_allowed_account?(allowed_accounts)
|
|
295
308
|
block.call
|
|
296
309
|
else
|
|
297
310
|
account_text = "'#{identity.account}'"
|
|
298
311
|
account_text << " (#{identity.account_aliases.join(', ')})" if identity.account_aliases.any?
|
|
299
|
-
StackMaster.stdout.puts
|
|
312
|
+
StackMaster.stdout.puts(
|
|
313
|
+
"Account #{account_text} is not an allowed account. Allowed accounts are #{allowed_accounts}."
|
|
314
|
+
)
|
|
300
315
|
false
|
|
301
316
|
end
|
|
302
317
|
end
|
|
@@ -144,6 +144,7 @@ module StackMaster
|
|
|
144
144
|
|
|
145
145
|
def upload_files
|
|
146
146
|
return unless use_s3?
|
|
147
|
+
|
|
147
148
|
s3.upload_files(**s3_options)
|
|
148
149
|
end
|
|
149
150
|
|
|
@@ -153,7 +154,8 @@ module StackMaster
|
|
|
153
154
|
|
|
154
155
|
def template_value
|
|
155
156
|
if use_s3?
|
|
156
|
-
s3.url(bucket: @s3_config['bucket'], prefix: @s3_config['prefix'], region: @s3_config['region'],
|
|
157
|
+
s3.url(bucket: @s3_config['bucket'], prefix: @s3_config['prefix'], region: @s3_config['region'],
|
|
158
|
+
template: @stack_definition.s3_template_file_name)
|
|
157
159
|
else
|
|
158
160
|
proposed_stack.template
|
|
159
161
|
end
|
|
@@ -161,6 +163,7 @@ module StackMaster
|
|
|
161
163
|
|
|
162
164
|
def files_to_upload
|
|
163
165
|
return {} unless use_s3?
|
|
166
|
+
|
|
164
167
|
@stack_definition.s3_files.tap do |files|
|
|
165
168
|
files[@stack_definition.s3_template_file_name] = {
|
|
166
169
|
path: @stack_definition.template_file_path,
|
|
@@ -218,6 +221,7 @@ module StackMaster
|
|
|
218
221
|
proposed_policy = proposed_stack.stack_policy_body
|
|
219
222
|
# No need to reset a stack policy if it's nil or not changed
|
|
220
223
|
return if proposed_policy.nil? || proposed_policy == current_policy
|
|
224
|
+
|
|
221
225
|
StackMaster.stdout.print 'Setting a stack policy...'
|
|
222
226
|
cf.set_stack_policy(
|
|
223
227
|
stack_name: stack_name,
|
|
@@ -12,7 +12,6 @@ module StackMaster
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def perform
|
|
15
|
-
|
|
16
15
|
return unless check_exists
|
|
17
16
|
|
|
18
17
|
unless ask?("Really delete stack #{@stack_name} (y/n)? ")
|
|
@@ -27,11 +26,11 @@ module StackMaster
|
|
|
27
26
|
private
|
|
28
27
|
|
|
29
28
|
def delete_stack
|
|
30
|
-
cf.delete_stack({stack_name: @stack_name})
|
|
29
|
+
cf.delete_stack({ stack_name: @stack_name })
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
def check_exists
|
|
34
|
-
cf.describe_stacks({stack_name: @stack_name})
|
|
33
|
+
cf.describe_stacks({ stack_name: @stack_name })
|
|
35
34
|
true
|
|
36
35
|
rescue Aws::CloudFormation::Errors::ValidationError
|
|
37
36
|
failed("Stack does not exist")
|
|
@@ -15,7 +15,8 @@ module StackMaster
|
|
|
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
|
|
@@ -51,8 +52,10 @@ module StackMaster
|
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
def display_resource_drift(drift)
|
|
54
|
-
diff = ::StackMaster::Diff.new(
|
|
55
|
-
|
|
55
|
+
diff = ::StackMaster::Diff.new(
|
|
56
|
+
before: prettify_json(drift.expected_properties),
|
|
57
|
+
after: prettify_json(drift.actual_properties)
|
|
58
|
+
)
|
|
56
59
|
diff.display_colorized_diff
|
|
57
60
|
end
|
|
58
61
|
|
|
@@ -28,11 +28,12 @@ module StackMaster
|
|
|
28
28
|
@region_parameters_filename = File.join("parameters", @region, "#{@stack_name}.yml")
|
|
29
29
|
|
|
30
30
|
if !@options.overwrite
|
|
31
|
-
[@stack_master_filename, @stack_json_filename, @parameters_filename,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
@@ -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
|
|
@@ -19,7 +19,10 @@ module StackMaster
|
|
|
19
19
|
templates.delete(template)
|
|
20
20
|
|
|
21
21
|
if !File.exist?(template)
|
|
22
|
-
StackMaster.stdout.puts
|
|
22
|
+
StackMaster.stdout.puts(
|
|
23
|
+
"Stack \"#{stack_definition.stack_name}\" in \"#{stack_definition.region}\" " \
|
|
24
|
+
"missing template \"#{rel_path(template)}\""
|
|
25
|
+
)
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
data/lib/stack_master/config.rb
CHANGED
|
@@ -14,14 +14,16 @@ 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
29
|
return config_file unless File.dirname(config_file) == "."
|
|
@@ -52,6 +54,7 @@ module StackMaster
|
|
|
52
54
|
@stacks = []
|
|
53
55
|
|
|
54
56
|
raise ConfigParseError.new("Stack defaults can't be undefined") if @stack_defaults.nil?
|
|
57
|
+
|
|
55
58
|
load_template_compilers(config)
|
|
56
59
|
load_config
|
|
57
60
|
end
|
|
@@ -72,6 +75,7 @@ module StackMaster
|
|
|
72
75
|
end
|
|
73
76
|
|
|
74
77
|
private
|
|
78
|
+
|
|
75
79
|
def load_template_compilers(config)
|
|
76
80
|
@template_compilers = {}
|
|
77
81
|
populate_template_compilers(config.fetch('template_compilers', {}))
|
|
@@ -92,9 +96,9 @@ module StackMaster
|
|
|
92
96
|
{
|
|
93
97
|
rb: :sparkle_formation,
|
|
94
98
|
json: :json,
|
|
95
|
-
yml:
|
|
99
|
+
yml: :yaml,
|
|
96
100
|
yaml: :yaml,
|
|
97
|
-
erb:
|
|
101
|
+
erb: :yaml_erb,
|
|
98
102
|
}
|
|
99
103
|
end
|
|
100
104
|
|
|
@@ -123,7 +127,8 @@ module StackMaster
|
|
|
123
127
|
'base_dir' => @base_dir,
|
|
124
128
|
'template_dir' => @template_dir,
|
|
125
129
|
'parameters_dir' => @parameters_dir,
|
|
126
|
-
'additional_parameter_lookup_dirs' => @region_to_aliases[region]
|
|
130
|
+
'additional_parameter_lookup_dirs' => @region_to_aliases[region]
|
|
131
|
+
)
|
|
127
132
|
stack_attributes['allowed_accounts'] = attributes['allowed_accounts'] if attributes['allowed_accounts']
|
|
128
133
|
@stacks << StackDefinition.new(stack_attributes)
|
|
129
134
|
end
|
|
@@ -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,13 +2,12 @@ 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.reduce({template_parameters: {}, compile_time_parameters: {}}) do |hash, parameters|
|
|
10
|
+
all_parameters.reduce({ template_parameters: {}, compile_time_parameters: {} }) do |hash, parameters|
|
|
12
11
|
template_parameters = create_template_parameters(parameters)
|
|
13
12
|
compile_time_parameters = create_compile_time_parameters(parameters)
|
|
14
13
|
|
|
@@ -32,7 +31,8 @@ module StackMaster
|
|
|
32
31
|
|
|
33
32
|
def self.create_template_parameters(parameters)
|
|
34
33
|
parameters.deep_dup.tap do |parameters_clone|
|
|
35
|
-
parameters_clone.delete(COMPILE_TIME_PARAMETERS_KEY) ||
|
|
34
|
+
parameters_clone.delete(COMPILE_TIME_PARAMETERS_KEY) ||
|
|
35
|
+
parameters_clone.delete(COMPILE_TIME_PARAMETERS_KEY.camelize)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -43,6 +43,5 @@ module StackMaster
|
|
|
43
43
|
def self.merge_and_camelize(hash, parameters)
|
|
44
44
|
parameters.each { |key, value| hash[key.camelize] = value }
|
|
45
45
|
end
|
|
46
|
-
|
|
47
46
|
end
|
|
48
47
|
end
|
|
@@ -49,6 +49,7 @@ module StackMaster
|
|
|
49
49
|
return parameter_value.to_s if Numeric === parameter_value || parameter_value == true || parameter_value == false
|
|
50
50
|
return resolve_array_parameter_values(key, parameter_value).join(',') if Array === parameter_value
|
|
51
51
|
return parameter_value unless Hash === parameter_value
|
|
52
|
+
|
|
52
53
|
resolve_parameter_resolver_hash(key, parameter_value)
|
|
53
54
|
rescue Aws::CloudFormation::Errors::ValidationError
|
|
54
55
|
raise InvalidParameter, $!.message
|
|
@@ -77,6 +78,7 @@ module StackMaster
|
|
|
77
78
|
if account.nil? || role.nil?
|
|
78
79
|
raise InvalidParameter, "Both 'account' and 'role' are required to assume role for parameter '#{key}'"
|
|
79
80
|
end
|
|
81
|
+
|
|
80
82
|
role_assumer.assume_role(account, role) do
|
|
81
83
|
yield
|
|
82
84
|
end
|
|
@@ -105,7 +107,8 @@ module StackMaster
|
|
|
105
107
|
begin
|
|
106
108
|
@resolvers[class_name] = resolver_class_const(class_name).new(@config, @stack_definition)
|
|
107
109
|
rescue NameError
|
|
108
|
-
raise ResolverNotFound,
|
|
110
|
+
raise ResolverNotFound,
|
|
111
|
+
"Could not find parameter resolver called #{class_name}, please double check your configuration"
|
|
109
112
|
end
|
|
110
113
|
end
|
|
111
114
|
end
|
|
@@ -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
|
|
|
@@ -15,7 +15,7 @@ module StackMaster
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def build_filters_from_hash(hash)
|
|
18
|
-
hash.map { |key, value| {name: key, values: Array(value.to_s)}}
|
|
18
|
+
hash.map { |key, value| { name: key, values: Array(value.to_s) } }
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def find_latest_ami(filters, owners = ['self'])
|
|
@@ -23,16 +23,19 @@ module StackMaster
|
|
|
23
23
|
|
|
24
24
|
def validate_ejson_file_specified
|
|
25
25
|
if @stack_definition.ejson_file.nil?
|
|
26
|
-
raise ArgumentError,
|
|
26
|
+
raise ArgumentError, 'No ejson_file defined for stack definition ' \
|
|
27
|
+
"#{@stack_definition.stack_name} in #{@stack_definition.region}"
|
|
27
28
|
end
|
|
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
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
module StackMaster
|
|
2
2
|
module ParameterResolvers
|
|
3
3
|
class Env < Resolver
|
|
4
|
-
|
|
5
4
|
def initialize(config, stack_definition)
|
|
6
5
|
@config = config
|
|
7
6
|
@stack_definition = stack_definition
|
|
@@ -10,9 +9,9 @@ module StackMaster
|
|
|
10
9
|
def resolve(value)
|
|
11
10
|
environment_variable = ENV[value]
|
|
12
11
|
raise ArgumentError, "The environment variable #{value} is not set" if environment_variable.nil?
|
|
12
|
+
|
|
13
13
|
environment_variable
|
|
14
14
|
end
|
|
15
|
-
|
|
16
15
|
end
|
|
17
16
|
end
|
|
18
17
|
end
|
|
@@ -28,7 +28,8 @@ module StackMaster
|
|
|
28
28
|
latest_image = images.first
|
|
29
29
|
|
|
30
30
|
# aws_account_id.dkr.ecr.region.amazonaws.com/repository@sha256:digest
|
|
31
|
-
"#{latest_image.registry_id}.dkr.ecr.#{@region}.amazonaws.com/#{parameters['repository_name']}
|
|
31
|
+
"#{latest_image.registry_id}.dkr.ecr.#{@region}.amazonaws.com/#{parameters['repository_name']}@" \
|
|
32
|
+
"#{latest_image.image_digest}"
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
private
|
|
@@ -36,15 +37,16 @@ module StackMaster
|
|
|
36
37
|
def fetch_images(repository_name, registry_id, ecr)
|
|
37
38
|
images = []
|
|
38
39
|
next_token = nil
|
|
39
|
-
while
|
|
40
|
-
|
|
40
|
+
while resp = ecr.describe_images(
|
|
41
|
+
{
|
|
41
42
|
repository_name: repository_name,
|
|
42
43
|
registry_id: registry_id,
|
|
43
44
|
next_token: next_token,
|
|
44
45
|
filter: {
|
|
45
46
|
tag_status: "TAGGED",
|
|
46
47
|
},
|
|
47
|
-
}
|
|
48
|
+
}
|
|
49
|
+
)
|
|
48
50
|
|
|
49
51
|
images += resp.image_details
|
|
50
52
|
next_token = resp.next_token
|
|
@@ -13,8 +13,12 @@ module StackMaster
|
|
|
13
13
|
@stack_definition = stack_definition
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def resolve(params={})
|
|
17
|
-
|
|
16
|
+
def resolve(params = {})
|
|
17
|
+
if ENV.keys.grep(/OP_SESSION_\w+$/).empty?
|
|
18
|
+
raise OnePasswordNotAbleToAuthenticate,
|
|
19
|
+
"1password requires the `OP_SESSION_<name>` to be set, (remember to sign in?)"
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
get_items(params)
|
|
19
23
|
end
|
|
20
24
|
|
|
@@ -40,7 +44,7 @@ module StackMaster
|
|
|
40
44
|
end
|
|
41
45
|
|
|
42
46
|
def password_item(data)
|
|
43
|
-
data.details.password
|
|
47
|
+
data.details.password
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
def login_item(data)
|