sfn 3.0.28 → 3.0.30
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 +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
|