sfn 3.0.28 → 3.0.30
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +5 -0
- data/docs/callbacks.md +1 -0
- data/lib/chef/knife/knife_plugin_seed.rb +11 -17
- data/lib/sfn.rb +0 -2
- data/lib/sfn/api_provider.rb +0 -2
- data/lib/sfn/api_provider/google.rb +6 -9
- data/lib/sfn/api_provider/terraform.rb +4 -6
- data/lib/sfn/cache.rb +36 -39
- data/lib/sfn/callback.rb +0 -2
- data/lib/sfn/callback/aws_assume_role.rb +7 -8
- data/lib/sfn/callback/aws_mfa.rb +7 -8
- data/lib/sfn/callback/stack_policy.rb +15 -17
- data/lib/sfn/command.rb +9 -11
- data/lib/sfn/command/conf.rb +7 -10
- data/lib/sfn/command/create.rb +8 -12
- data/lib/sfn/command/describe.rb +6 -8
- data/lib/sfn/command/destroy.rb +8 -10
- data/lib/sfn/command/diff.rb +18 -25
- data/lib/sfn/command/events.rb +15 -16
- data/lib/sfn/command/export.rb +13 -17
- data/lib/sfn/command/graph.rb +11 -13
- data/lib/sfn/command/graph/aws.rb +27 -29
- data/lib/sfn/command/graph/terraform.rb +22 -23
- data/lib/sfn/command/import.rb +13 -16
- data/lib/sfn/command/init.rb +5 -7
- data/lib/sfn/command/inspect.rb +26 -29
- data/lib/sfn/command/lint.rb +10 -12
- data/lib/sfn/command/list.rb +5 -8
- data/lib/sfn/command/print.rb +3 -5
- data/lib/sfn/command/promote.rb +0 -2
- data/lib/sfn/command/update.rb +42 -46
- data/lib/sfn/command/validate.rb +4 -6
- data/lib/sfn/command_module/base.rb +17 -25
- data/lib/sfn/command_module/callbacks.rb +12 -8
- data/lib/sfn/command_module/stack.rb +39 -43
- data/lib/sfn/command_module/template.rb +89 -90
- data/lib/sfn/config.rb +30 -31
- data/lib/sfn/config/conf.rb +1 -3
- data/lib/sfn/config/create.rb +5 -7
- data/lib/sfn/config/describe.rb +3 -5
- data/lib/sfn/config/diff.rb +1 -1
- data/lib/sfn/config/events.rb +6 -8
- data/lib/sfn/config/export.rb +4 -7
- data/lib/sfn/config/graph.rb +4 -6
- data/lib/sfn/config/import.rb +3 -5
- data/lib/sfn/config/init.rb +0 -1
- data/lib/sfn/config/inspect.rb +7 -9
- data/lib/sfn/config/lint.rb +4 -4
- data/lib/sfn/config/list.rb +3 -5
- data/lib/sfn/config/print.rb +3 -5
- data/lib/sfn/config/promote.rb +3 -5
- data/lib/sfn/config/update.rb +10 -12
- data/lib/sfn/config/validate.rb +18 -20
- data/lib/sfn/lint.rb +0 -2
- data/lib/sfn/lint/definition.rb +3 -5
- data/lib/sfn/lint/rule.rb +7 -8
- data/lib/sfn/lint/rule_set.rb +11 -20
- data/lib/sfn/monkey_patch/stack.rb +32 -34
- data/lib/sfn/monkey_patch/stack/azure.rb +0 -1
- data/lib/sfn/monkey_patch/stack/google.rb +15 -16
- data/lib/sfn/planner.rb +1 -3
- data/lib/sfn/planner/aws.rb +82 -89
- data/lib/sfn/provider.rb +21 -23
- data/lib/sfn/utils.rb +0 -2
- data/lib/sfn/utils/debug.rb +1 -2
- data/lib/sfn/utils/json.rb +3 -2
- data/lib/sfn/utils/object_storage.rb +1 -2
- data/lib/sfn/utils/output.rb +8 -9
- data/lib/sfn/utils/path_selector.rb +9 -10
- data/lib/sfn/utils/ssher.rb +2 -3
- data/lib/sfn/utils/stack_exporter.rb +20 -21
- data/lib/sfn/utils/stack_parameter_scrubber.rb +6 -7
- data/lib/sfn/utils/stack_parameter_validator.rb +14 -16
- data/lib/sfn/version.rb +1 -1
- data/sfn.gemspec +1 -1
- metadata +8 -8
@@ -22,11 +22,11 @@ module Sfn
|
|
22
22
|
#
|
23
23
|
# @param location [Symbol, String] name of location
|
24
24
|
# @return [Sfn::Provider]
|
25
|
-
def provider_for(location=nil)
|
25
|
+
def provider_for(location = nil)
|
26
26
|
key = ['provider', location].compact.map(&:to_s).join('_')
|
27
|
-
if
|
27
|
+
if location
|
28
28
|
credentials = config.get(:locations, location)
|
29
|
-
unless
|
29
|
+
unless credentials
|
30
30
|
raise ArgumentError.new "Failed to locate provider credentials for location `#{location}`!"
|
31
31
|
end
|
32
32
|
else
|
@@ -37,18 +37,15 @@ module Sfn
|
|
37
37
|
result = Sfn::Provider.new(
|
38
38
|
:miasma => credentials,
|
39
39
|
:async => false,
|
40
|
-
:fetch => false
|
40
|
+
:fetch => false,
|
41
41
|
)
|
42
|
-
result.connection.data[:stack_types] = (
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
] + custom_stack_types
|
48
|
-
).compact.uniq
|
42
|
+
result.connection.data[:stack_types] = ([
|
43
|
+
(result.connection.class.const_get(:RESOURCE_MAPPING).detect do |klass, info|
|
44
|
+
info[:api] == :orchestration
|
45
|
+
end || []).first,
|
46
|
+
] + custom_stack_types).compact.uniq
|
49
47
|
retry_config = config.fetch(:retry,
|
50
|
-
|
51
|
-
)
|
48
|
+
config.fetch(:retries, {}))
|
52
49
|
result.connection.data[:retry_ui] = ui
|
53
50
|
result.connection.data[:location] = location.to_s
|
54
51
|
result.connection.data[:locations] = config.fetch(:locations, {})
|
@@ -63,19 +60,19 @@ module Sfn
|
|
63
60
|
raise
|
64
61
|
end
|
65
62
|
end
|
66
|
-
alias_method :provider, :provider_for
|
67
63
|
|
64
|
+
alias_method :provider, :provider_for
|
68
65
|
|
69
66
|
# Write exception information if debug is enabled
|
70
67
|
#
|
71
68
|
# @param e [Exception]
|
72
69
|
# @param args [String] extra strings to output
|
73
70
|
def _debug(e, *args)
|
74
|
-
if
|
71
|
+
if config[:verbose] || config[:debug]
|
75
72
|
ui.fatal "Exception information: #{e.class}: #{e.message}"
|
76
|
-
if
|
73
|
+
if ENV['DEBUG'] || config[:debug]
|
77
74
|
puts "#{e.backtrace.join("\n")}\n"
|
78
|
-
if
|
75
|
+
if e.is_a?(Miasma::Error::ApiError)
|
79
76
|
ui.fatal "Response body: #{e.response.body.to_s.inspect}"
|
80
77
|
end
|
81
78
|
end
|
@@ -97,7 +94,7 @@ module Sfn
|
|
97
94
|
#
|
98
95
|
# @param name [String] name of stack
|
99
96
|
# @return [Miasma::Models::Orchestration::Stack]
|
100
|
-
def stack(name=nil)
|
97
|
+
def stack(name = nil)
|
101
98
|
name = name_args.first unless name
|
102
99
|
provider.stacks.get(name)
|
103
100
|
end
|
@@ -136,7 +133,7 @@ module Sfn
|
|
136
133
|
# @param message [String] failure message
|
137
134
|
# @yield block to wrap error handling
|
138
135
|
# @return [Object] result of yield
|
139
|
-
def get_things(stack=nil, message=nil)
|
136
|
+
def get_things(stack = nil, message = nil)
|
140
137
|
begin
|
141
138
|
yield
|
142
139
|
rescue => e
|
@@ -174,18 +171,16 @@ module Sfn
|
|
174
171
|
# @return [NilClass]
|
175
172
|
# @raise [ArgumentError]
|
176
173
|
def name_required!
|
177
|
-
if
|
174
|
+
if name_args.empty?
|
178
175
|
ui.error 'Name argument must be provided!'
|
179
176
|
raise ArgumentError.new 'Missing required name argument'
|
180
177
|
end
|
181
178
|
end
|
182
|
-
|
183
179
|
end
|
184
180
|
|
185
181
|
class << self
|
186
182
|
def included(klass)
|
187
183
|
klass.instance_eval do
|
188
|
-
|
189
184
|
include Sfn::CommandModule::Base::InstanceMethods
|
190
185
|
include Sfn::CommandModule::Callbacks
|
191
186
|
include Sfn::Utils::JSON
|
@@ -193,12 +188,9 @@ module Sfn
|
|
193
188
|
include Bogo::AnimalStrings
|
194
189
|
include Bogo::Memoization
|
195
190
|
include Bogo::Constants
|
196
|
-
|
197
191
|
end
|
198
|
-
|
199
192
|
end
|
200
193
|
end
|
201
194
|
end
|
202
|
-
|
203
195
|
end
|
204
196
|
end
|
@@ -5,7 +5,6 @@ module Sfn
|
|
5
5
|
module CommandModule
|
6
6
|
# Callback processor helpers
|
7
7
|
module Callbacks
|
8
|
-
|
9
8
|
include Bogo::Memoization
|
10
9
|
|
11
10
|
# Run expected callbacks around action
|
@@ -16,9 +15,15 @@ module Sfn
|
|
16
15
|
def api_action!(*args)
|
17
16
|
type = self.class.name.split('::').last.downcase
|
18
17
|
run_callbacks_for(["before_#{type}", :before], *args)
|
19
|
-
result =
|
20
|
-
|
21
|
-
|
18
|
+
result = nil
|
19
|
+
begin
|
20
|
+
result = yield if block_given?
|
21
|
+
run_callbacks_for(["after_#{type}", :after], *args)
|
22
|
+
result
|
23
|
+
rescue => err
|
24
|
+
run_callbacks_for(["failed_#{type}", :failed], *(args + [err]))
|
25
|
+
raise
|
26
|
+
end
|
22
27
|
end
|
23
28
|
|
24
29
|
# Process requested callbacks
|
@@ -34,7 +39,7 @@ module Sfn
|
|
34
39
|
callback_name, callback, quiet = item
|
35
40
|
quiet = true if config[:print_only]
|
36
41
|
ui.info "Callback #{ui.color(type.to_s, :bold)} #{callback_name}: #{ui.color('starting', :yellow)}" unless quiet
|
37
|
-
if
|
42
|
+
if args.empty?
|
38
43
|
callback.call
|
39
44
|
else
|
40
45
|
callback.call(*args)
|
@@ -57,15 +62,14 @@ module Sfn
|
|
57
62
|
klass.new(ui, config, arguments, provider)
|
58
63
|
rescue NameError => e
|
59
64
|
ui.debug "Callback type lookup error: #{e.class} - #{e}"
|
60
|
-
raise "Unknown #{type} callback requested: #{c_name} (not found)"
|
65
|
+
raise NameError.new("Unknown #{type} callback requested: #{c_name} (not found)")
|
61
66
|
end
|
62
67
|
end
|
63
|
-
if
|
68
|
+
if instance.respond_to?(type)
|
64
69
|
[c_name, instance.method(type), instance.respond_to?(:quiet) ? instance.quiet : false]
|
65
70
|
end
|
66
71
|
end.compact
|
67
72
|
end
|
68
|
-
|
69
73
|
end
|
70
74
|
end
|
71
75
|
end
|
@@ -5,7 +5,6 @@ module Sfn
|
|
5
5
|
module CommandModule
|
6
6
|
# Stack handling helper methods
|
7
7
|
module Stack
|
8
|
-
|
9
8
|
module InstanceMethods
|
10
9
|
|
11
10
|
# maximum number of attempts to get valid parameter value
|
@@ -30,7 +29,7 @@ module Sfn
|
|
30
29
|
stack_info.unshift(nil) if stack_info.size == 1
|
31
30
|
stack_location, stack_name = stack_info
|
32
31
|
remote_stack = provider_for(stack_location).stack(stack_name)
|
33
|
-
if
|
32
|
+
if remote_stack
|
34
33
|
apply_nested_stacks!(remote_stack, stack)
|
35
34
|
mappings = generate_custom_apply_mappings(remote_stack)
|
36
35
|
execute_apply_stack(remote_stack, stack, mappings)
|
@@ -49,7 +48,7 @@ module Sfn
|
|
49
48
|
# @return [Miasma::Models::Orchestration::Stack]
|
50
49
|
def apply_nested_stacks!(remote_stack, stack)
|
51
50
|
remote_stack.resources.all.each do |resource|
|
52
|
-
if
|
51
|
+
if valid_stack_types.include?(resource.type)
|
53
52
|
nested_stack = resource.expand
|
54
53
|
apply_nested_stacks!(nested_stack, stack)
|
55
54
|
mappings = generate_custom_apply_mappings(nested_stack)
|
@@ -64,7 +63,7 @@ module Sfn
|
|
64
63
|
# @param provider_stack [Miasma::Models::Orchestration::Stack] stack providing outputs
|
65
64
|
# @return [Hash] output to parameter mapping
|
66
65
|
def generate_custom_apply_mappings(provider_stack)
|
67
|
-
if
|
66
|
+
if config[:apply_mapping]
|
68
67
|
valid_keys = config[:apply_mapping].keys.find_all do |a_key|
|
69
68
|
a_key = a_key.to_s
|
70
69
|
key_parts = a_key.split('__')
|
@@ -81,7 +80,7 @@ module Sfn
|
|
81
80
|
end
|
82
81
|
end
|
83
82
|
to_remove = valid_keys.find_all do |key|
|
84
|
-
valid_keys.any?{|v_key| v_key.match(/__#{Regexp.escape(key)}$/)}
|
83
|
+
valid_keys.any? { |v_key| v_key.match(/__#{Regexp.escape(key)}$/) }
|
85
84
|
end
|
86
85
|
valid_keys -= to_remove
|
87
86
|
Hash[
|
@@ -130,15 +129,14 @@ module Sfn
|
|
130
129
|
|
131
130
|
# Format config defined parameters to ensure expected layout
|
132
131
|
def format_config_parameters!
|
133
|
-
if
|
132
|
+
if config.get(:parameter).is_a?(Array)
|
134
133
|
config[:parameter] = Smash[
|
135
134
|
*config.get(:parameter).map(&:to_a).flatten
|
136
135
|
]
|
137
136
|
end
|
138
|
-
if
|
137
|
+
if config.get(:parameters)
|
139
138
|
config.set(:parameters,
|
140
|
-
|
141
|
-
)
|
139
|
+
config.get(:parameters).merge(config.fetch(:parameter, Smash.new)))
|
142
140
|
else
|
143
141
|
config.set(:parameters, config.fetch(:parameter, Smash.new))
|
144
142
|
end
|
@@ -151,16 +149,16 @@ module Sfn
|
|
151
149
|
# @return [Array<String>] [expected_template_key, configuration_used_key]
|
152
150
|
def locate_config_parameter_key(parameter_prefix, parameter_name, root_name)
|
153
151
|
check_name = parameter_name.downcase.tr('-_', '')
|
154
|
-
check_prefix = parameter_prefix.map{|i| i.downcase.tr('-_', '') }
|
152
|
+
check_prefix = parameter_prefix.map { |i| i.downcase.tr('-_', '') }
|
155
153
|
key_match = config[:parameters].keys.detect do |cp_key|
|
156
|
-
cp_key = cp_key.to_s.downcase.split('__').map{|i| i.tr('-_', '') }.join('__')
|
154
|
+
cp_key = cp_key.to_s.downcase.split('__').map { |i| i.tr('-_', '') }.join('__')
|
157
155
|
non_root_matcher = (check_prefix + [check_name]).join('__')
|
158
156
|
root_matcher = ([root_name] + check_prefix + [check_name]).join('__')
|
159
157
|
cp_key == non_root_matcher ||
|
160
158
|
cp_key == root_matcher
|
161
159
|
end
|
162
160
|
actual_key = (parameter_prefix + [parameter_name]).compact.join('__')
|
163
|
-
if
|
161
|
+
if key_match
|
164
162
|
ui.debug "Remapping configuration runtime parameter `#{key_match}` -> `#{actual_key}`"
|
165
163
|
config[:parameters][actual_key] = config[:parameters].delete(key_match)
|
166
164
|
end
|
@@ -179,23 +177,23 @@ module Sfn
|
|
179
177
|
def set_parameter(sparkle, ns_key, param_name, param_value, current_parameters, param_banner)
|
180
178
|
valid = false
|
181
179
|
attempt = 0
|
182
|
-
if
|
183
|
-
if
|
180
|
+
if !valid && !param_banner
|
181
|
+
if sparkle.is_a?(SparkleFormation)
|
184
182
|
ui.info "#{ui.color('Stack runtime parameters:', :bold)} - template: #{ui.color(sparkle.root_path.map(&:name).map(&:to_s).join(' > '), :green, :bold)}"
|
185
183
|
else
|
186
184
|
ui.info ui.color('Stack runtime parameters:', :bold)
|
187
185
|
end
|
188
186
|
param_banner = true
|
189
187
|
end
|
190
|
-
until
|
188
|
+
until valid
|
191
189
|
attempt += 1
|
192
190
|
default = config[:parameters].fetch(
|
193
191
|
ns_key, current_parameters.fetch(
|
194
|
-
param_name, TEMPLATE_PARAMETER_DEFAULTS.map{|loc_key| param_value[loc_key]}.compact.first
|
192
|
+
param_name, TEMPLATE_PARAMETER_DEFAULTS.map { |loc_key| param_value[loc_key] }.compact.first
|
195
193
|
)
|
196
194
|
)
|
197
|
-
if
|
198
|
-
no_echo = !!TEMPLATE_PARAMETER_NOECHO.detect{|loc_key|
|
195
|
+
if config[:interactive_parameters]
|
196
|
+
no_echo = !!TEMPLATE_PARAMETER_NOECHO.detect { |loc_key|
|
199
197
|
param_value[loc_key].to_s.downcase == 'true'
|
200
198
|
}
|
201
199
|
sfn_no_echo = TEMPLATE_PARAMETER_SFN_NOECHO.map do |loc_key|
|
@@ -204,16 +202,16 @@ module Sfn
|
|
204
202
|
end.compact.first
|
205
203
|
no_echo = sfn_no_echo if sfn_no_echo
|
206
204
|
answer = ui.ask_question(
|
207
|
-
"#{param_name.split(/([A-Z]+[^A-Z]*)/).find_all{|s
|
205
|
+
"#{param_name.split(/([A-Z]+[^A-Z]*)/).find_all { |s| !s.empty? }.join(' ')}",
|
208
206
|
:default => default,
|
209
207
|
:hide_default => sfn_no_echo == 'all',
|
210
|
-
:no_echo => !!no_echo
|
208
|
+
:no_echo => !!no_echo,
|
211
209
|
)
|
212
210
|
else
|
213
211
|
answer = default
|
214
212
|
end
|
215
213
|
validation = validate_parameter(answer, param_value)
|
216
|
-
if
|
214
|
+
if validation == true
|
217
215
|
config[:parameters][ns_key] = answer
|
218
216
|
valid = true
|
219
217
|
else
|
@@ -221,7 +219,7 @@ module Sfn
|
|
221
219
|
ui.error validation_error.last
|
222
220
|
end
|
223
221
|
end
|
224
|
-
if
|
222
|
+
if attempt > MAX_PARAMETER_ATTEMPTS
|
225
223
|
ui.fatal 'Failed to receive allowed parameter!'
|
226
224
|
exit 1
|
227
225
|
end
|
@@ -236,29 +234,29 @@ module Sfn
|
|
236
234
|
# @option opts [Hash] :current_parameters current stack parameter values
|
237
235
|
# @option opts [Miasma::Models::Orchestration::Stack] :stack existing stack
|
238
236
|
# @return [Hash]
|
239
|
-
def populate_parameters!(sparkle, opts={})
|
237
|
+
def populate_parameters!(sparkle, opts = {})
|
240
238
|
current_parameters = opts[:current_parameters] || {}
|
241
239
|
current_stack = opts[:stack]
|
242
240
|
parameter_prefix, stack_parameters = prefix_parameters_setup(sparkle)
|
243
241
|
sparkle_root_name = sparkle.is_a?(SparkleFormation) ? sparkle.root.name : nil
|
244
|
-
unless
|
242
|
+
unless stack_parameters.empty?
|
245
243
|
format_config_parameters!
|
246
244
|
param_banner = false
|
247
245
|
stack_parameters.each do |param_name, param_value|
|
248
246
|
ns_key = locate_config_parameter_key(parameter_prefix, param_name, sparkle_root_name)
|
249
247
|
# When parameter is a hash type, it is being set via
|
250
248
|
# intrinsic function and we don't modify
|
251
|
-
if
|
252
|
-
if
|
249
|
+
if function_set_parameter?(current_parameters[param_name])
|
250
|
+
if !config[:parameters][ns_key].nil?
|
253
251
|
ui.warn "Overriding mapped parameter value with explicit assignment `#{ns_key}`!"
|
254
252
|
else
|
255
|
-
if
|
253
|
+
if current_stack
|
256
254
|
enable_set = validate_stack_parameter(current_stack, param_name, ns_key, current_parameters[param_name])
|
257
255
|
else
|
258
256
|
enable_set = true
|
259
257
|
end
|
260
258
|
end
|
261
|
-
if
|
259
|
+
if enable_set
|
262
260
|
# NOTE: direct set dumps the stack (nfi). Smash will
|
263
261
|
# auto dup it, and works, so yay i guess.
|
264
262
|
config[:parameters][ns_key] = current_parameters[param_name].is_a?(Hash) ?
|
@@ -267,22 +265,22 @@ module Sfn
|
|
267
265
|
valid = true
|
268
266
|
end
|
269
267
|
else
|
270
|
-
if
|
268
|
+
if current_stack && current_stack.data[:parent_stack]
|
271
269
|
use_expected = validate_stack_parameter(current_stack, param_name, ns_key, current_parameters[param_name])
|
272
|
-
unless
|
270
|
+
unless use_expected
|
273
271
|
current_parameters[param_name] = current_stack.parameters[param_name]
|
274
272
|
end
|
275
273
|
end
|
276
274
|
end
|
277
|
-
unless
|
275
|
+
unless valid
|
278
276
|
param_banner = set_parameter(sparkle, ns_key, param_name, param_value, current_parameters, param_banner)
|
279
277
|
end
|
280
278
|
end
|
281
279
|
end
|
282
280
|
Smash[
|
283
|
-
config.fetch(:parameters, {}).map do |k,v|
|
281
|
+
config.fetch(:parameters, {}).map do |k, v|
|
284
282
|
strip_key = parameter_prefix ? k.sub(/#{parameter_prefix.join('__')}_{2}?/, '') : k
|
285
|
-
unless
|
283
|
+
unless strip_key.include?('__')
|
286
284
|
[strip_key, v]
|
287
285
|
end
|
288
286
|
end.compact
|
@@ -300,7 +298,7 @@ module Sfn
|
|
300
298
|
# @return [Hash] parameters for root stack create/update
|
301
299
|
def config_root_parameters
|
302
300
|
Hash[
|
303
|
-
config.fetch(:parameters, {}).find_all do |k,v|
|
301
|
+
config.fetch(:parameters, {}).find_all do |k, v|
|
304
302
|
!k.include?('__')
|
305
303
|
end
|
306
304
|
]
|
@@ -320,19 +318,19 @@ module Sfn
|
|
320
318
|
def validate_stack_parameter(c_stack, p_key, p_ns_key, c_value)
|
321
319
|
stack_value = c_stack.parameters[p_key]
|
322
320
|
p_stack = c_stack.data[:parent_stack]
|
323
|
-
unless
|
324
|
-
if
|
321
|
+
unless config[:parameter_validation] == 'none'
|
322
|
+
if c_value.is_a?(Hash)
|
325
323
|
case c_value.keys.first
|
326
324
|
when 'Ref'
|
327
325
|
current_value = p_stack.parameters[c_value.values.first]
|
328
326
|
when 'Fn::Att'
|
329
327
|
resource_name, output_name = c_value.values.first.split('.', 2)
|
330
|
-
ref_stack = p_stack.nested_stacks.detect{|i| i.data[:logical_id] == resource_name}
|
331
|
-
if
|
328
|
+
ref_stack = p_stack.nested_stacks.detect { |i| i.data[:logical_id] == resource_name }
|
329
|
+
if ref_stack
|
332
330
|
output = ref_stack.outputs.detect do |o|
|
333
331
|
o.key == output_name
|
334
332
|
end
|
335
|
-
if
|
333
|
+
if output
|
336
334
|
current_value = output.value
|
337
335
|
end
|
338
336
|
end
|
@@ -340,8 +338,8 @@ module Sfn
|
|
340
338
|
else
|
341
339
|
current_value = c_value
|
342
340
|
end
|
343
|
-
if
|
344
|
-
if
|
341
|
+
if current_value && current_value.to_s != stack_value.to_s
|
342
|
+
if config[:parameter_validation] == 'default'
|
345
343
|
ui.warn 'Nested stack has been altered directly! This update may cause unexpected modifications!'
|
346
344
|
ui.warn "Stack name: #{c_stack.name}. Parameter: #{p_key}. Current value: #{stack_value}. Expected value: #{current_value} (via: #{c_value.inspect})"
|
347
345
|
answer = ui.ask_question("Use current value or expected value for #{p_key} [current/expected]?", :valid => ['current', 'expected'])
|
@@ -356,7 +354,6 @@ module Sfn
|
|
356
354
|
true
|
357
355
|
end
|
358
356
|
end
|
359
|
-
|
360
357
|
end
|
361
358
|
|
362
359
|
module ClassMethods
|
@@ -372,7 +369,6 @@ module Sfn
|
|
372
369
|
include Utils::StackParameterValidator
|
373
370
|
end
|
374
371
|
end
|
375
|
-
|
376
372
|
end
|
377
373
|
end
|
378
374
|
end
|
@@ -22,9 +22,9 @@ module Sfn
|
|
22
22
|
# @param thing [SparkleFormation, Hash]
|
23
23
|
# @param scrub [Truthy, Falsey] scrub nested templates
|
24
24
|
# @return [Hash]
|
25
|
-
def template_content(thing, scrub=false)
|
26
|
-
if
|
27
|
-
if
|
25
|
+
def template_content(thing, scrub = false)
|
26
|
+
if thing.is_a?(SparkleFormation)
|
27
|
+
if scrub
|
28
28
|
dump_stack_for_storage(thing)
|
29
29
|
else
|
30
30
|
config[:sparkle_dump] ? thing.sparkle_dump : thing.dump
|
@@ -45,41 +45,41 @@ module Sfn
|
|
45
45
|
# @option p_config [String, Symbol] :description
|
46
46
|
# @option p_config [String, Symbol] :multiple
|
47
47
|
# @return [Object]
|
48
|
-
def request_compile_parameter(p_name, p_config, cur_val, nested=false)
|
48
|
+
def request_compile_parameter(p_name, p_config, cur_val, nested = false)
|
49
49
|
result = nil
|
50
50
|
attempts = 0
|
51
51
|
parameter_type = p_config.fetch(:type, 'string').to_s.downcase.to_sym
|
52
|
-
if
|
52
|
+
if parameter_type == :complex
|
53
53
|
ui.debug "Compile time parameter `#{p_name}` is a complex type. Not requesting value from user."
|
54
|
-
if
|
54
|
+
if cur_val.nil?
|
55
55
|
raise ArgumentError.new "No value provided for `#{p_name}` parameter (Complex data type)"
|
56
56
|
else
|
57
57
|
cur_val
|
58
58
|
end
|
59
59
|
else
|
60
|
-
unless
|
60
|
+
unless cur_val || p_config[:default].nil?
|
61
61
|
cur_val = p_config[:default]
|
62
62
|
end
|
63
|
-
if
|
63
|
+
if cur_val.is_a?(Array)
|
64
64
|
cur_val = cur_val.map(&:to_s).join(',')
|
65
65
|
end
|
66
|
-
until
|
66
|
+
until result && (!result.respond_to?(:empty?) || !result.empty?)
|
67
67
|
attempts += 1
|
68
|
-
if
|
68
|
+
if config[:interactive_parameters] && (!nested || !p_config.key?(:prompt_when_nested) || p_config[:prompt_when_nested] == true)
|
69
69
|
result = ui.ask_question(
|
70
70
|
p_name.to_s.split('_').map(&:capitalize).join,
|
71
|
-
:default => cur_val.to_s.empty? ? nil : cur_val.to_s
|
71
|
+
:default => cur_val.to_s.empty? ? nil : cur_val.to_s,
|
72
72
|
)
|
73
73
|
else
|
74
74
|
result = cur_val.to_s
|
75
75
|
end
|
76
76
|
case parameter_type
|
77
77
|
when :string
|
78
|
-
if
|
78
|
+
if p_config[:multiple]
|
79
79
|
result = result.split(',').map(&:strip)
|
80
80
|
end
|
81
81
|
when :number
|
82
|
-
if
|
82
|
+
if p_config[:multiple]
|
83
83
|
result = result.split(',').map(&:strip)
|
84
84
|
new_result = result.map do |item|
|
85
85
|
new_item = item.to_i
|
@@ -94,14 +94,14 @@ module Sfn
|
|
94
94
|
raise ArgumentError.new "Unknown compile time parameter type provided: `#{p_config[:type].inspect}` (Parameter: #{p_name})"
|
95
95
|
end
|
96
96
|
valid = validate_parameter(result, p_config.to_smash)
|
97
|
-
unless
|
97
|
+
unless valid == true
|
98
98
|
result = nil
|
99
99
|
valid.each do |invalid_msg|
|
100
100
|
ui.error invalid_msg.last
|
101
101
|
end
|
102
102
|
end
|
103
|
-
if
|
104
|
-
if
|
103
|
+
if result.nil? || (result.respond_to?(:empty?) && result.empty?)
|
104
|
+
if attempts > MAX_PARAMETER_ATTEMPTS
|
105
105
|
ui.fatal "Failed to receive allowed parameter! (Parameter: #{p_name})"
|
106
106
|
exit 1
|
107
107
|
end
|
@@ -135,17 +135,17 @@ module Sfn
|
|
135
135
|
def sparkle_collection
|
136
136
|
memoize(:sparkle_collection) do
|
137
137
|
collection = SparkleFormation::SparkleCollection.new(
|
138
|
-
:provider => config.fetch(:credentials, :provider, DEFAULT_PROVIDER_NAME)
|
138
|
+
:provider => config.fetch(:credentials, :provider, DEFAULT_PROVIDER_NAME),
|
139
139
|
)
|
140
140
|
begin
|
141
|
-
if
|
141
|
+
if config[:base_directory]
|
142
142
|
root_pack = SparkleFormation::SparklePack.new(
|
143
143
|
:root => config[:base_directory],
|
144
|
-
:provider => config.fetch(:credentials, :provider, DEFAULT_PROVIDER_NAME)
|
144
|
+
:provider => config.fetch(:credentials, :provider, DEFAULT_PROVIDER_NAME),
|
145
145
|
)
|
146
146
|
else
|
147
147
|
root_pack = SparkleFormation::SparklePack.new(
|
148
|
-
:provider => config.fetch(:credentials, :provider, DEFAULT_PROVIDER_NAME)
|
148
|
+
:provider => config.fetch(:credentials, :provider, DEFAULT_PROVIDER_NAME),
|
149
149
|
)
|
150
150
|
end
|
151
151
|
collection.set_root(root_pack)
|
@@ -164,30 +164,30 @@ module Sfn
|
|
164
164
|
# @param args [Symbol] options (:allow_missing)
|
165
165
|
# @return [Hash] loaded template
|
166
166
|
def load_template_file(*args)
|
167
|
-
c_stack = (args.detect{|i| i.is_a?(Hash)} || {})[:stack]
|
168
|
-
unless
|
167
|
+
c_stack = (args.detect { |i| i.is_a?(Hash) } || {})[:stack]
|
168
|
+
unless config[:template]
|
169
169
|
set_paths_and_discover_file!
|
170
|
-
unless
|
171
|
-
unless
|
170
|
+
unless config[:file]
|
171
|
+
unless args.include?(:allow_missing)
|
172
172
|
ui.fatal "Invalid formation file path provided: #{config[:file]}"
|
173
173
|
raise IOError.new "Failed to locate file: #{config[:file]}"
|
174
174
|
end
|
175
175
|
end
|
176
176
|
end
|
177
|
-
if
|
177
|
+
if config[:template]
|
178
178
|
config[:template]
|
179
|
-
elsif
|
180
|
-
if
|
179
|
+
elsif config[:file]
|
180
|
+
if config[:processing]
|
181
181
|
compile_state = merge_compile_time_parameters
|
182
182
|
sf = SparkleFormation.compile(config[:file], :sparkle)
|
183
|
-
if
|
183
|
+
if name_args.first
|
184
184
|
sf.name = name_args.first
|
185
185
|
end
|
186
186
|
sf.compile_time_parameter_setter do |formation|
|
187
187
|
f_name = formation.root_path.map(&:name).map(&:to_s)
|
188
188
|
pathed_name = f_name.join(' > ')
|
189
189
|
f_name = f_name.join('__')
|
190
|
-
if
|
190
|
+
if formation.root? && compile_state[f_name].nil?
|
191
191
|
current_state = compile_state
|
192
192
|
else
|
193
193
|
current_state = compile_state.fetch(f_name, Smash.new)
|
@@ -196,40 +196,39 @@ module Sfn
|
|
196
196
|
# NOTE: Prevent nesting stack compile state within stack compile state
|
197
197
|
current_state.delete("#{f_name}__#{f_name}")
|
198
198
|
|
199
|
-
if
|
199
|
+
if formation.compile_state
|
200
200
|
current_state = current_state.merge(formation.compile_state)
|
201
201
|
end
|
202
|
-
unless
|
202
|
+
unless formation.parameters.empty?
|
203
203
|
ui.info "#{ui.color('Compile time parameters:', :bold)} - template: #{ui.color(pathed_name, :green, :bold)}" unless config[:print_only]
|
204
|
-
formation.parameters.each do |k,v|
|
204
|
+
formation.parameters.each do |k, v|
|
205
205
|
valid_keys = [
|
206
206
|
"#{f_name}__#{k}",
|
207
207
|
Bogo::Utility.camel("#{f_name}__#{k}").downcase,
|
208
208
|
k,
|
209
|
-
Bogo::Utility.camel(k).downcase
|
209
|
+
Bogo::Utility.camel(k).downcase,
|
210
210
|
]
|
211
211
|
current_value = valid_keys.map do |key|
|
212
212
|
current_state[key]
|
213
213
|
end.compact.first
|
214
214
|
primary_key, secondary_key = ["#{f_name}__#{k}", k]
|
215
215
|
current_state[k] = request_compile_parameter(k, v,
|
216
|
-
|
217
|
-
|
218
|
-
)
|
216
|
+
current_value,
|
217
|
+
!!formation.parent)
|
219
218
|
end
|
220
219
|
formation.compile_state = current_state
|
221
220
|
end
|
222
221
|
end
|
223
222
|
sf.sparkle.apply sparkle_collection
|
224
223
|
custom_stack_types.each do |s_type|
|
225
|
-
unless
|
224
|
+
unless sf.stack_resource_types.include?(s_type)
|
226
225
|
sf.stack_resource_types.push(s_type)
|
227
226
|
end
|
228
227
|
end
|
229
228
|
run_callbacks_for(:template, :stack_name => arguments.first, :sparkle_stack => sf)
|
230
|
-
if
|
229
|
+
if sf.nested? && config[:apply_nesting]
|
231
230
|
validate_nesting_bucket!
|
232
|
-
if
|
231
|
+
if config[:apply_nesting] == true
|
233
232
|
config[:apply_nesting] = :deep
|
234
233
|
end
|
235
234
|
case config[:apply_nesting].to_sym
|
@@ -262,12 +261,15 @@ module Sfn
|
|
262
261
|
compile_state = config.fetch(:compile_parameters, Smash.new)
|
263
262
|
ui.debug "Initial compile parameters - #{compile_state}"
|
264
263
|
compile_state.keys.each do |cs_key|
|
265
|
-
unless
|
264
|
+
unless cs_key.to_s.start_with?("#{arguments.first}__")
|
266
265
|
named_cs_key = [arguments.first, cs_key].compact.join('__')
|
267
266
|
non_named = compile_state.delete(cs_key)
|
268
|
-
if
|
267
|
+
if non_named && !compile_state.key?(named_cs_key)
|
269
268
|
ui.debug "Setting non-named compile parameter `#{cs_key}` into `#{named_cs_key}`"
|
270
269
|
compile_state[named_cs_key] = non_named
|
270
|
+
elsif non_named && compile_state.key?(named_cs_key)
|
271
|
+
ui.debug "Merging none-named `#{cs_key}` with named `#{named_cs_key}`"
|
272
|
+
compile_state[named_cs_key].merge!(non_named)
|
271
273
|
else
|
272
274
|
ui.debug "Discarding non-named compile parameter due to set named - `#{cs_key}` </> `#{named_cs_key}`"
|
273
275
|
end
|
@@ -279,7 +281,7 @@ module Sfn
|
|
279
281
|
|
280
282
|
# Force user friendly error if nesting bucket is not set within configuration
|
281
283
|
def validate_nesting_bucket!
|
282
|
-
if
|
284
|
+
if config[:nesting_bucket].to_s.empty?
|
283
285
|
ui.error 'Missing required configuration value for `nesting_bucket`. Cannot generated nested templates!'
|
284
286
|
raise ArgumentError.new 'Required configuration value for `nesting_bucket` not provided.'
|
285
287
|
end
|
@@ -290,17 +292,17 @@ module Sfn
|
|
290
292
|
# @param sf [SparkleFormation] stack formation
|
291
293
|
# @param c_stack [Miasma::Models::Orchestration::Stack] existing stack
|
292
294
|
# @return [Hash] dumped stack
|
293
|
-
def process_nested_stack_shallow(sf, c_stack=nil)
|
295
|
+
def process_nested_stack_shallow(sf, c_stack = nil)
|
294
296
|
sf.apply_nesting(:shallow) do |stack_name, stack, resource|
|
295
297
|
run_callbacks_for(:template, :stack_name => stack_name, :sparkle_stack => stack)
|
296
298
|
bucket = provider.connection.api_for(:storage).buckets.get(
|
297
299
|
config[:nesting_bucket]
|
298
300
|
)
|
299
|
-
if
|
301
|
+
if config[:print_only]
|
300
302
|
template_url = "http://example.com/bucket/#{name_args.first}_#{stack_name}.json"
|
301
303
|
else
|
302
304
|
stack_definition = dump_stack_for_storage(stack)
|
303
|
-
unless
|
305
|
+
unless bucket
|
304
306
|
raise "Failed to locate configured bucket for stack template storage (#{bucket})!"
|
305
307
|
end
|
306
308
|
file = bucket.files.build
|
@@ -320,13 +322,13 @@ module Sfn
|
|
320
322
|
# @param sf [SparkleFormation] stack
|
321
323
|
# @param c_stack [Miasma::Models::Orchestration::Stack] existing stack
|
322
324
|
# @return [SparkleFormation::SparkleStruct] compiled structure
|
323
|
-
def process_nested_stack_deep(sf, c_stack=nil)
|
325
|
+
def process_nested_stack_deep(sf, c_stack = nil)
|
324
326
|
sf.apply_nesting(:deep) do |stack_name, stack, resource|
|
325
327
|
run_callbacks_for(:template, :stack_name => stack_name, :sparkle_stack => stack)
|
326
328
|
stack_resource = resource._dump
|
327
|
-
current_stack = c_stack ? c_stack.nested_stacks.detect{|s| s.data[:logical_id] == stack_name} : nil
|
329
|
+
current_stack = c_stack ? c_stack.nested_stacks.detect { |s| s.data[:logical_id] == stack_name } : nil
|
328
330
|
current_parameters = extract_current_nested_template_parameters(stack, stack_name, current_stack)
|
329
|
-
if
|
331
|
+
if current_stack && current_stack.data[:parent_stack]
|
330
332
|
current_parameters.merge!(
|
331
333
|
current_stack.data[:parent_stack].template.fetch(
|
332
334
|
'Resources', stack_name, 'Properties', 'Parameters', current_stack.data[:parent_stack].template.fetch(
|
@@ -337,22 +339,21 @@ module Sfn
|
|
337
339
|
end
|
338
340
|
full_stack_name = [
|
339
341
|
config[:nesting_prefix],
|
340
|
-
stack.root_path.map(&:name).map(&:to_s).join('_')
|
342
|
+
stack.root_path.map(&:name).map(&:to_s).join('_'),
|
341
343
|
].compact.join('/')
|
342
|
-
unless
|
344
|
+
unless config[:print_only]
|
343
345
|
result = Smash.new(
|
344
346
|
:parameters => populate_parameters!(stack,
|
345
|
-
|
346
|
-
|
347
|
-
)
|
347
|
+
:stack => current_stack,
|
348
|
+
:current_parameters => current_parameters),
|
348
349
|
)
|
349
350
|
store_template(full_stack_name, stack, result)
|
350
351
|
else
|
351
352
|
result = Smash.new(
|
352
|
-
:url => "http://example.com/bucket/#{full_stack_name}.json"
|
353
|
+
:url => "http://example.com/bucket/#{full_stack_name}.json",
|
353
354
|
)
|
354
355
|
end
|
355
|
-
format_nested_stack_results(resource._self.provider, result).each do |k,v|
|
356
|
+
format_nested_stack_results(resource._self.provider, result).each do |k, v|
|
356
357
|
resource.properties.set!(k, v)
|
357
358
|
end
|
358
359
|
end
|
@@ -364,8 +365,8 @@ module Sfn
|
|
364
365
|
# @param stack_name [String]
|
365
366
|
# @param c_stack [Miasma::Models::Orchestration::Stack]
|
366
367
|
# @return [Hash]
|
367
|
-
def extract_current_nested_template_parameters(template, stack_name, c_stack=nil)
|
368
|
-
if
|
368
|
+
def extract_current_nested_template_parameters(template, stack_name, c_stack = nil)
|
369
|
+
if template.parent
|
369
370
|
current_parameters = template.parent.compile.resources.set!(stack_name).properties.parameters
|
370
371
|
current_parameters.nil? ? Smash.new : current_parameters._dump
|
371
372
|
else
|
@@ -384,7 +385,7 @@ module Sfn
|
|
384
385
|
bucket = provider.connection.api_for(:storage).buckets.get(
|
385
386
|
config[:nesting_bucket]
|
386
387
|
)
|
387
|
-
unless
|
388
|
+
unless bucket
|
388
389
|
raise "Failed to locate configured bucket for stack template storage (#{config[:nesting_bucket]})!"
|
389
390
|
end
|
390
391
|
file = bucket.files.build
|
@@ -393,7 +394,7 @@ module Sfn
|
|
393
394
|
file.body = MultiJson.dump(parameter_scrub!(stack_definition))
|
394
395
|
file.save
|
395
396
|
result.merge!(
|
396
|
-
:url => file.url
|
397
|
+
:url => file.url,
|
397
398
|
)
|
398
399
|
end
|
399
400
|
|
@@ -407,7 +408,7 @@ module Sfn
|
|
407
408
|
[nested_name, nested_resource, nested_resource.properties.delete!(:stack)]
|
408
409
|
end
|
409
410
|
stack_definition = template.dump
|
410
|
-
if
|
411
|
+
if config[:plan]
|
411
412
|
nested_stacks.each do |nested_name, nested_resource, nested_data|
|
412
413
|
nested_resource.properties.set!(:stack, nested_data)
|
413
414
|
end
|
@@ -422,7 +423,7 @@ module Sfn
|
|
422
423
|
def scrub_template(template)
|
423
424
|
template = parameter_scrub!(template)
|
424
425
|
(template['Resources'] || {}).each do |r_name, r_content|
|
425
|
-
if
|
426
|
+
if valid_stack_types.include?(r_content['Type'])
|
426
427
|
result = (r_content['Properties'] || {}).delete('Stack')
|
427
428
|
end
|
428
429
|
end
|
@@ -437,10 +438,10 @@ module Sfn
|
|
437
438
|
def format_nested_stack_results(provider, results)
|
438
439
|
case provider
|
439
440
|
when :aws
|
440
|
-
if
|
441
|
+
if results[:parameters]
|
441
442
|
results['Parameters'] = results.delete(:parameters)
|
442
443
|
end
|
443
|
-
if
|
444
|
+
if results[:url]
|
444
445
|
url = URI.parse(results.delete(:url))
|
445
446
|
results['TemplateURL'] = "#{url.scheme}://#{url.host}#{url.path}"
|
446
447
|
end
|
@@ -449,18 +450,18 @@ module Sfn
|
|
449
450
|
results[:template] = results.delete(:url)
|
450
451
|
results
|
451
452
|
when :azure
|
452
|
-
if
|
453
|
+
if results[:parameters]
|
453
454
|
results[:parameters] = Smash[
|
454
455
|
results[:parameters].map do |key, value|
|
455
456
|
[key,
|
456
|
-
|
457
|
+
value.is_a?(Hash) ? value : Smash.new(:value => value)]
|
457
458
|
end
|
458
459
|
]
|
459
460
|
end
|
460
|
-
if
|
461
|
+
if results[:url]
|
461
462
|
results[:templateLink] = Smash.new(
|
462
463
|
:uri => results.delete(:url),
|
463
|
-
:contentVersion => '1.0.0.0'
|
464
|
+
:contentVersion => '1.0.0.0',
|
464
465
|
)
|
465
466
|
end
|
466
467
|
results[:mode] = 'Incremental'
|
@@ -475,16 +476,16 @@ module Sfn
|
|
475
476
|
# @param template [Hash]
|
476
477
|
# @return [Hash]
|
477
478
|
def translate_template(template)
|
478
|
-
if
|
479
|
+
if klass_name = config[:translate]
|
479
480
|
klass = SparkleFormation::Translation.const_get(camel(klass_name))
|
480
481
|
args = {
|
481
|
-
:parameters => config.fetch(:options, :parameters, Smash.new)
|
482
|
+
:parameters => config.fetch(:options, :parameters, Smash.new),
|
482
483
|
}
|
483
|
-
if
|
484
|
+
if chunk_size = config[:translate_chunk_size]
|
484
485
|
args.merge!(
|
485
486
|
:options => {
|
486
|
-
:serialization_chunk_size => chunk_size
|
487
|
-
}
|
487
|
+
:serialization_chunk_size => chunk_size,
|
488
|
+
},
|
488
489
|
)
|
489
490
|
end
|
490
491
|
translator = klass.new(template, args)
|
@@ -499,12 +500,12 @@ module Sfn
|
|
499
500
|
#
|
500
501
|
# @return [TrueClass]
|
501
502
|
def set_paths_and_discover_file!
|
502
|
-
if
|
503
|
-
if
|
503
|
+
if config[:processing]
|
504
|
+
if !config[:file] && config[:file_path_prompt]
|
504
505
|
config[:file] = prompt_for_template
|
505
506
|
else
|
506
507
|
file_lookup_path = File.expand_path(config[:file])
|
507
|
-
unless
|
508
|
+
unless File.exists?(file_lookup_path)
|
508
509
|
file_lookup_path = config[:file]
|
509
510
|
end
|
510
511
|
config[:file] = sparkle_collection.get(
|
@@ -512,8 +513,8 @@ module Sfn
|
|
512
513
|
)[:path]
|
513
514
|
end
|
514
515
|
else
|
515
|
-
if
|
516
|
-
unless
|
516
|
+
if config[:file]
|
517
|
+
unless File.exists?(config[:file])
|
517
518
|
raise Errno::ENOENT.new("No such file - #{config[:file]}")
|
518
519
|
end
|
519
520
|
else
|
@@ -527,8 +528,8 @@ module Sfn
|
|
527
528
|
#
|
528
529
|
# @param prefix [String] prefix filter for names
|
529
530
|
# @return [String] path to template
|
530
|
-
def prompt_for_template(prefix=nil)
|
531
|
-
if
|
531
|
+
def prompt_for_template(prefix = nil)
|
532
|
+
if prefix
|
532
533
|
collection_name = prefix.split('__').map do |c_name|
|
533
534
|
c_name.split('_').map(&:capitalize).join(' ')
|
534
535
|
end.join(' / ')
|
@@ -541,26 +542,26 @@ module Sfn
|
|
541
542
|
end
|
542
543
|
collections = template_names.map do |t_name|
|
543
544
|
t_name = t_name.to_s.sub(/^#{Regexp.escape(prefix.to_s)}/, '')
|
544
|
-
if
|
545
|
+
if t_name.include?('__')
|
545
546
|
c_name = t_name.split('__').first
|
546
547
|
[[prefix, c_name].compact.join('') + '__', c_name]
|
547
548
|
end
|
548
549
|
end.compact.uniq(&:first)
|
549
550
|
templates = template_names.map do |t_name|
|
550
551
|
t_name = t_name.to_s.sub(/^#{Regexp.escape(prefix.to_s)}/, '')
|
551
|
-
unless
|
552
|
+
unless t_name.include?('__')
|
552
553
|
[[prefix, t_name].compact.join(''), t_name]
|
553
554
|
end
|
554
555
|
end.compact
|
555
|
-
if
|
556
|
+
if collections.empty? && templates.empty?
|
556
557
|
ui.error 'Failed to locate any templates!'
|
557
558
|
return nil
|
558
559
|
end
|
559
|
-
ui.info "Please select an entry#{
|
560
|
+
ui.info "Please select an entry#{'(or collection to list)' unless collections.empty?}:"
|
560
561
|
output = []
|
561
562
|
idx = 1
|
562
563
|
valid = {}
|
563
|
-
unless
|
564
|
+
unless collections.empty?
|
564
565
|
output << ui.color('Collections:', :bold)
|
565
566
|
collections.each do |full_name, part_name|
|
566
567
|
valid[idx] = {:name => full_name, :type => :collection}
|
@@ -568,7 +569,7 @@ module Sfn
|
|
568
569
|
idx += 1
|
569
570
|
end
|
570
571
|
end
|
571
|
-
unless
|
572
|
+
unless templates.empty?
|
572
573
|
output << ui.color('Templates:', :bold)
|
573
574
|
templates.each do |full_name, part_name|
|
574
575
|
valid[idx] = {:name => full_name, :type => :template}
|
@@ -578,7 +579,7 @@ module Sfn
|
|
578
579
|
end
|
579
580
|
max = idx.to_s.length
|
580
581
|
output.map! do |line|
|
581
|
-
if
|
582
|
+
if line.is_a?(Array)
|
582
583
|
" #{line.first}.#{' ' * (max - line.first.to_s.length)} #{line.last}"
|
583
584
|
else
|
584
585
|
line
|
@@ -586,17 +587,16 @@ module Sfn
|
|
586
587
|
end
|
587
588
|
ui.puts "#{output.join("\n")}\n"
|
588
589
|
response = nil
|
589
|
-
until
|
590
|
+
until valid[response]
|
590
591
|
response = ui.ask_question('Enter selection').to_i
|
591
592
|
end
|
592
593
|
entry = valid[response]
|
593
|
-
if
|
594
|
+
if entry[:type] == :collection
|
594
595
|
prompt_for_template(entry[:name])
|
595
596
|
else
|
596
597
|
sparkle_collection.get(:template, entry[:name])[:path]
|
597
598
|
end
|
598
599
|
end
|
599
|
-
|
600
600
|
end
|
601
601
|
|
602
602
|
module ClassMethods
|
@@ -614,7 +614,6 @@ module Sfn
|
|
614
614
|
include Sfn::Utils::StackParameterValidator
|
615
615
|
end
|
616
616
|
end
|
617
|
-
|
618
617
|
end
|
619
618
|
end
|
620
619
|
end
|