sfn 3.0.30 → 3.0.32
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/CHANGELOG.md +6 -0
- data/bin/sfn +16 -14
- data/lib/chef/knife/knife_plugin_seed.rb +12 -12
- data/lib/sfn.rb +17 -17
- data/lib/sfn/api_provider.rb +3 -3
- data/lib/sfn/api_provider/google.rb +2 -2
- data/lib/sfn/api_provider/terraform.rb +2 -2
- data/lib/sfn/cache.rb +9 -9
- data/lib/sfn/callback.rb +6 -6
- data/lib/sfn/callback/aws_assume_role.rb +5 -5
- data/lib/sfn/callback/aws_mfa.rb +8 -6
- data/lib/sfn/callback/stack_policy.rb +15 -15
- data/lib/sfn/command.rb +37 -36
- data/lib/sfn/command/conf.rb +12 -12
- data/lib/sfn/command/create.rb +9 -9
- data/lib/sfn/command/describe.rb +6 -6
- data/lib/sfn/command/destroy.rb +8 -8
- data/lib/sfn/command/diff.rb +31 -31
- data/lib/sfn/command/events.rb +6 -6
- data/lib/sfn/command/export.rb +8 -8
- data/lib/sfn/command/graph.rb +21 -21
- data/lib/sfn/command/graph/aws.rb +34 -34
- data/lib/sfn/command/graph/provider.rb +1 -1
- data/lib/sfn/command/graph/terraform.rb +41 -41
- data/lib/sfn/command/import.rb +17 -17
- data/lib/sfn/command/init.rb +15 -15
- data/lib/sfn/command/inspect.rb +16 -16
- data/lib/sfn/command/lint.rb +6 -6
- data/lib/sfn/command/list.rb +2 -2
- data/lib/sfn/command/plan.rb +227 -0
- data/lib/sfn/command/print.rb +4 -4
- data/lib/sfn/command/promote.rb +2 -2
- data/lib/sfn/command/update.rb +19 -144
- data/lib/sfn/command/validate.rb +17 -13
- data/lib/sfn/command_module.rb +6 -5
- data/lib/sfn/command_module/base.rb +8 -8
- data/lib/sfn/command_module/callbacks.rb +5 -5
- data/lib/sfn/command_module/planning.rb +151 -0
- data/lib/sfn/command_module/stack.rb +34 -34
- data/lib/sfn/command_module/template.rb +50 -50
- data/lib/sfn/config.rb +46 -44
- data/lib/sfn/config/conf.rb +3 -3
- data/lib/sfn/config/create.rb +9 -9
- data/lib/sfn/config/describe.rb +7 -7
- data/lib/sfn/config/destroy.rb +1 -1
- data/lib/sfn/config/diff.rb +3 -3
- data/lib/sfn/config/events.rb +9 -9
- data/lib/sfn/config/export.rb +5 -5
- data/lib/sfn/config/graph.rb +10 -10
- data/lib/sfn/config/import.rb +4 -4
- data/lib/sfn/config/init.rb +1 -1
- data/lib/sfn/config/inspect.rb +16 -16
- data/lib/sfn/config/lint.rb +5 -5
- data/lib/sfn/config/list.rb +6 -6
- data/lib/sfn/config/plan.rb +28 -0
- data/lib/sfn/config/print.rb +5 -5
- data/lib/sfn/config/promote.rb +4 -4
- data/lib/sfn/config/update.rb +18 -18
- data/lib/sfn/config/validate.rb +30 -30
- data/lib/sfn/lint.rb +5 -5
- data/lib/sfn/lint/definition.rb +3 -3
- data/lib/sfn/lint/rule.rb +3 -3
- data/lib/sfn/lint/rule_set.rb +2 -2
- data/lib/sfn/monkey_patch.rb +2 -2
- data/lib/sfn/monkey_patch/stack.rb +27 -27
- data/lib/sfn/monkey_patch/stack/azure.rb +1 -1
- data/lib/sfn/monkey_patch/stack/google.rb +5 -5
- data/lib/sfn/planner.rb +4 -4
- data/lib/sfn/planner/aws.rb +114 -70
- data/lib/sfn/provider.rb +13 -13
- data/lib/sfn/utils.rb +10 -10
- data/lib/sfn/utils/debug.rb +2 -2
- data/lib/sfn/utils/json.rb +1 -1
- data/lib/sfn/utils/object_storage.rb +3 -3
- data/lib/sfn/utils/output.rb +4 -4
- data/lib/sfn/utils/path_selector.rb +15 -15
- data/lib/sfn/utils/ssher.rb +4 -4
- data/lib/sfn/utils/stack_exporter.rb +16 -16
- data/lib/sfn/utils/stack_parameter_scrubber.rb +6 -6
- data/lib/sfn/utils/stack_parameter_validator.rb +22 -22
- data/lib/sfn/version.rb +1 -1
- data/sfn.gemspec +32 -32
- metadata +16 -13
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
require "sfn"
|
|
2
|
+
require "sparkle_formation"
|
|
3
|
+
|
|
4
|
+
module Sfn
|
|
5
|
+
module CommandModule
|
|
6
|
+
# Planning helpers
|
|
7
|
+
module Planning
|
|
8
|
+
# Create a new planner instance
|
|
9
|
+
#
|
|
10
|
+
# @param [Miasma::Models::Orchestration::Stack]
|
|
11
|
+
# @return [Sfn::Planner]
|
|
12
|
+
def build_planner(stack)
|
|
13
|
+
klass_name = stack.api.class.to_s.split("::").last
|
|
14
|
+
if Planner.const_defined?(klass_name)
|
|
15
|
+
Planner.const_get(klass_name).new(ui, config, arguments, stack)
|
|
16
|
+
else
|
|
17
|
+
warn "Failed to build planner for current provider. No provider implemented. (`#{klass_name}`)"
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Display plan result on the UI
|
|
23
|
+
#
|
|
24
|
+
# @param result [Miasma::Models::Orchestration::Stack::Plan]
|
|
25
|
+
def display_plan_information(result)
|
|
26
|
+
ui.info ui.color("Pre-update resource planning report:", :bold)
|
|
27
|
+
unless print_plan_result(result, [result.name])
|
|
28
|
+
ui.info "No resources life cycle changes detected in this update!"
|
|
29
|
+
end
|
|
30
|
+
cmd = self.class.to_s.split("::").last.downcase
|
|
31
|
+
ui.confirm "Apply this stack #{cmd}?" unless config[:plan_only]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Print plan information to the UI
|
|
35
|
+
#
|
|
36
|
+
# @param info [Miasma::Models::Orchestration::Stack::Plan]
|
|
37
|
+
# @param names [Array<String>] nested names
|
|
38
|
+
def print_plan_result(info, names = [])
|
|
39
|
+
said_any_things = false
|
|
40
|
+
unless Array(info.stacks).empty?
|
|
41
|
+
info.stacks.each do |s_name, s_info|
|
|
42
|
+
result = print_plan_result(s_info, [*names, s_name].compact)
|
|
43
|
+
said_any_things ||= result
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
if !names.flatten.compact.empty? || info.name
|
|
47
|
+
said_things = false
|
|
48
|
+
output_name = names.empty? ? info.name : names.join(" > ")
|
|
49
|
+
ui.puts
|
|
50
|
+
ui.puts " #{ui.color("Update plan for:", :bold)} #{ui.color(names.join(" > "), :blue)}"
|
|
51
|
+
unless Array(info.unknown).empty?
|
|
52
|
+
ui.puts " #{ui.color("!!! Unknown update effect:", :red, :bold)}"
|
|
53
|
+
print_plan_items(info, :unknown, :red)
|
|
54
|
+
ui.puts
|
|
55
|
+
said_any_things = said_things = true
|
|
56
|
+
end
|
|
57
|
+
unless Array(info.unavailable).empty?
|
|
58
|
+
ui.puts " #{ui.color("Update request not allowed:", :red, :bold)}"
|
|
59
|
+
print_plan_items(info, :unavailable, :red)
|
|
60
|
+
ui.puts
|
|
61
|
+
said_any_things = said_things = true
|
|
62
|
+
end
|
|
63
|
+
unless Array(info.replace).empty?
|
|
64
|
+
ui.puts " #{ui.color("Resources to be replaced:", :red, :bold)}"
|
|
65
|
+
print_plan_items(info, :replace, :red)
|
|
66
|
+
ui.puts
|
|
67
|
+
said_any_things = said_things = true
|
|
68
|
+
end
|
|
69
|
+
unless Array(info.interrupt).empty?
|
|
70
|
+
ui.puts " #{ui.color("Resources to be interrupted:", :yellow, :bold)}"
|
|
71
|
+
print_plan_items(info, :interrupt, :yellow)
|
|
72
|
+
ui.puts
|
|
73
|
+
said_any_things = said_things = true
|
|
74
|
+
end
|
|
75
|
+
unless Array(info.remove).empty?
|
|
76
|
+
ui.puts " #{ui.color("Resources to be removed:", :red, :bold)}"
|
|
77
|
+
print_plan_items(info, :remove, :red)
|
|
78
|
+
ui.puts
|
|
79
|
+
said_any_things = said_things = true
|
|
80
|
+
end
|
|
81
|
+
unless Array(info.add).empty?
|
|
82
|
+
ui.puts " #{ui.color("Resources to be added:", :green, :bold)}"
|
|
83
|
+
print_plan_items(info, :add, :green)
|
|
84
|
+
ui.puts
|
|
85
|
+
said_any_things = said_things = true
|
|
86
|
+
end
|
|
87
|
+
unless said_things
|
|
88
|
+
ui.puts " #{ui.color("No resource lifecycle changes detected!", :green)}"
|
|
89
|
+
ui.puts
|
|
90
|
+
said_any_things = true
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
said_any_things
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Print planning items
|
|
97
|
+
#
|
|
98
|
+
# @param info [Miasma::Models::Orchestration::Stack::Plan] plan
|
|
99
|
+
# @param key [Symbol] key of items
|
|
100
|
+
# @param color [Symbol] color to flag
|
|
101
|
+
def print_plan_items(info, key, color)
|
|
102
|
+
collection = info.send(key)
|
|
103
|
+
max_name = collection.map(&:name).map(&:size).max
|
|
104
|
+
max_type = collection.map(&:type).map(&:size).max
|
|
105
|
+
max_p = collection.map(&:diffs).flatten(1).map(&:name).map(&:to_s).map(&:size).max
|
|
106
|
+
max_o = collection.map(&:diffs).flatten(1).map(&:current).map(&:to_s).map(&:size).max
|
|
107
|
+
collection.each do |val|
|
|
108
|
+
name = val.name
|
|
109
|
+
ui.print " " * 6
|
|
110
|
+
ui.print ui.color("[#{val.type}]", color)
|
|
111
|
+
ui.print " " * (max_type - val.type.size)
|
|
112
|
+
ui.print " " * 4
|
|
113
|
+
ui.print ui.color(name, :bold)
|
|
114
|
+
properties = Array(val.diffs).map(&:name)
|
|
115
|
+
unless properties.empty?
|
|
116
|
+
ui.print " " * (max_name - name.size)
|
|
117
|
+
ui.print " " * 4
|
|
118
|
+
ui.print "Reason: `#{properties.join("`, `")}`"
|
|
119
|
+
end
|
|
120
|
+
ui.puts
|
|
121
|
+
if config[:diffs]
|
|
122
|
+
unless val.diffs.empty?
|
|
123
|
+
p_name = nil
|
|
124
|
+
val.diffs.each do |diff|
|
|
125
|
+
if !diff.proposed.nil? || !diff.current.nil?
|
|
126
|
+
p_name = diff.name
|
|
127
|
+
ui.print " " * 8
|
|
128
|
+
ui.print "#{p_name}: "
|
|
129
|
+
ui.print " " * (max_p - p_name.size)
|
|
130
|
+
ui.print ui.color("-#{diff.current}", :red) if diff.current
|
|
131
|
+
ui.print " " * (max_o - diff.current.to_s.size)
|
|
132
|
+
ui.print " "
|
|
133
|
+
if diff.proposed == Sfn::Planner::RUNTIME_MODIFIED
|
|
134
|
+
ui.puts ui.color("+#{diff.current} <Dependency Modified>", :green)
|
|
135
|
+
else
|
|
136
|
+
if diff.proposed.nil?
|
|
137
|
+
ui.puts
|
|
138
|
+
else
|
|
139
|
+
ui.puts ui.color("+#{diff.proposed.to_s.gsub("__MODIFIED_REFERENCE_VALUE__", "<Dependency Modified>")}", :green)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
ui.puts if p_name
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "sfn"
|
|
2
|
+
require "sparkle_formation"
|
|
3
3
|
|
|
4
4
|
module Sfn
|
|
5
5
|
module CommandModule
|
|
@@ -10,13 +10,13 @@ module Sfn
|
|
|
10
10
|
# maximum number of attempts to get valid parameter value
|
|
11
11
|
MAX_PARAMETER_ATTEMPTS = 5
|
|
12
12
|
# Template parameter locations
|
|
13
|
-
TEMPLATE_PARAMETER_LOCATIONS = [
|
|
13
|
+
TEMPLATE_PARAMETER_LOCATIONS = ["Parameters", "parameters"]
|
|
14
14
|
# Template parameter default value locations
|
|
15
|
-
TEMPLATE_PARAMETER_DEFAULTS = [
|
|
15
|
+
TEMPLATE_PARAMETER_DEFAULTS = ["Default", "defaultValue", "default"]
|
|
16
16
|
# Template parameter no echo locations
|
|
17
|
-
TEMPLATE_PARAMETER_NOECHO = [
|
|
17
|
+
TEMPLATE_PARAMETER_NOECHO = ["NoEcho"]
|
|
18
18
|
# Template parameter no echo custom
|
|
19
|
-
TEMPLATE_PARAMETER_SFN_NOECHO = [
|
|
19
|
+
TEMPLATE_PARAMETER_SFN_NOECHO = ["Quiet", "quiet"]
|
|
20
20
|
|
|
21
21
|
# Apply any defined remote stacks
|
|
22
22
|
#
|
|
@@ -25,7 +25,7 @@ module Sfn
|
|
|
25
25
|
def apply_stacks!(stack)
|
|
26
26
|
remote_stacks = [config[:apply_stack]].flatten.compact
|
|
27
27
|
remote_stacks.each do |stack_name|
|
|
28
|
-
stack_info = stack_name.split(
|
|
28
|
+
stack_info = stack_name.split("__")
|
|
29
29
|
stack_info.unshift(nil) if stack_info.size == 1
|
|
30
30
|
stack_location, stack_name = stack_info
|
|
31
31
|
remote_stack = provider_for(stack_location).stack(stack_name)
|
|
@@ -66,7 +66,7 @@ module Sfn
|
|
|
66
66
|
if config[:apply_mapping]
|
|
67
67
|
valid_keys = config[:apply_mapping].keys.find_all do |a_key|
|
|
68
68
|
a_key = a_key.to_s
|
|
69
|
-
key_parts = a_key.split(
|
|
69
|
+
key_parts = a_key.split("__")
|
|
70
70
|
case key_parts.size
|
|
71
71
|
when 3
|
|
72
72
|
provider_stack.api.data[:location] == key_parts[0] &&
|
|
@@ -85,7 +85,7 @@ module Sfn
|
|
|
85
85
|
valid_keys -= to_remove
|
|
86
86
|
Hash[
|
|
87
87
|
valid_keys.map do |a_key|
|
|
88
|
-
cut_key = a_key.split(
|
|
88
|
+
cut_key = a_key.split("__").last
|
|
89
89
|
[cut_key, config[:apply_mapping][a_key]]
|
|
90
90
|
end
|
|
91
91
|
]
|
|
@@ -148,16 +148,16 @@ module Sfn
|
|
|
148
148
|
# @param parameter_name [String] parameter name
|
|
149
149
|
# @return [Array<String>] [expected_template_key, configuration_used_key]
|
|
150
150
|
def locate_config_parameter_key(parameter_prefix, parameter_name, root_name)
|
|
151
|
-
check_name = parameter_name.downcase.tr(
|
|
152
|
-
check_prefix = parameter_prefix.map { |i| i.downcase.tr(
|
|
151
|
+
check_name = parameter_name.downcase.tr("-_", "")
|
|
152
|
+
check_prefix = parameter_prefix.map { |i| i.downcase.tr("-_", "") }
|
|
153
153
|
key_match = config[:parameters].keys.detect do |cp_key|
|
|
154
|
-
cp_key = cp_key.to_s.downcase.split(
|
|
155
|
-
non_root_matcher = (check_prefix + [check_name]).join(
|
|
156
|
-
root_matcher = ([root_name] + check_prefix + [check_name]).join(
|
|
154
|
+
cp_key = cp_key.to_s.downcase.split("__").map { |i| i.tr("-_", "") }.join("__")
|
|
155
|
+
non_root_matcher = (check_prefix + [check_name]).join("__")
|
|
156
|
+
root_matcher = ([root_name] + check_prefix + [check_name]).join("__")
|
|
157
157
|
cp_key == non_root_matcher ||
|
|
158
158
|
cp_key == root_matcher
|
|
159
159
|
end
|
|
160
|
-
actual_key = (parameter_prefix + [parameter_name]).compact.join(
|
|
160
|
+
actual_key = (parameter_prefix + [parameter_name]).compact.join("__")
|
|
161
161
|
if key_match
|
|
162
162
|
ui.debug "Remapping configuration runtime parameter `#{key_match}` -> `#{actual_key}`"
|
|
163
163
|
config[:parameters][actual_key] = config[:parameters].delete(key_match)
|
|
@@ -179,9 +179,9 @@ module Sfn
|
|
|
179
179
|
attempt = 0
|
|
180
180
|
if !valid && !param_banner
|
|
181
181
|
if sparkle.is_a?(SparkleFormation)
|
|
182
|
-
ui.info "#{ui.color(
|
|
182
|
+
ui.info "#{ui.color("Stack runtime parameters:", :bold)} - template: #{ui.color(sparkle.root_path.map(&:name).map(&:to_s).join(" > "), :green, :bold)}"
|
|
183
183
|
else
|
|
184
|
-
ui.info ui.color(
|
|
184
|
+
ui.info ui.color("Stack runtime parameters:", :bold)
|
|
185
185
|
end
|
|
186
186
|
param_banner = true
|
|
187
187
|
end
|
|
@@ -194,17 +194,17 @@ module Sfn
|
|
|
194
194
|
)
|
|
195
195
|
if config[:interactive_parameters]
|
|
196
196
|
no_echo = !!TEMPLATE_PARAMETER_NOECHO.detect { |loc_key|
|
|
197
|
-
param_value[loc_key].to_s.downcase ==
|
|
197
|
+
param_value[loc_key].to_s.downcase == "true"
|
|
198
198
|
}
|
|
199
199
|
sfn_no_echo = TEMPLATE_PARAMETER_SFN_NOECHO.map do |loc_key|
|
|
200
200
|
res = param_value.delete(loc_key).to_s.downcase
|
|
201
|
-
res if !res.empty? && res !=
|
|
201
|
+
res if !res.empty? && res != "false"
|
|
202
202
|
end.compact.first
|
|
203
203
|
no_echo = sfn_no_echo if sfn_no_echo
|
|
204
204
|
answer = ui.ask_question(
|
|
205
|
-
"#{param_name.split(/([A-Z]+[^A-Z]*)/).find_all { |s| !s.empty? }.join(
|
|
205
|
+
"#{param_name.split(/([A-Z]+[^A-Z]*)/).find_all { |s| !s.empty? }.join(" ")}",
|
|
206
206
|
:default => default,
|
|
207
|
-
:hide_default => sfn_no_echo ==
|
|
207
|
+
:hide_default => sfn_no_echo == "all",
|
|
208
208
|
:no_echo => !!no_echo,
|
|
209
209
|
)
|
|
210
210
|
else
|
|
@@ -216,11 +216,11 @@ module Sfn
|
|
|
216
216
|
valid = true
|
|
217
217
|
else
|
|
218
218
|
validation.each do |validation_error|
|
|
219
|
-
ui.error validation_error.last
|
|
219
|
+
ui.error "#{param_name}: #{validation_error.last}"
|
|
220
220
|
end
|
|
221
221
|
end
|
|
222
222
|
if attempt > MAX_PARAMETER_ATTEMPTS
|
|
223
|
-
ui.fatal
|
|
223
|
+
ui.fatal "Failed to receive allowed parameter!"
|
|
224
224
|
exit 1
|
|
225
225
|
end
|
|
226
226
|
end
|
|
@@ -279,8 +279,8 @@ module Sfn
|
|
|
279
279
|
end
|
|
280
280
|
Smash[
|
|
281
281
|
config.fetch(:parameters, {}).map do |k, v|
|
|
282
|
-
strip_key = parameter_prefix ? k.sub(/#{parameter_prefix.join(
|
|
283
|
-
unless strip_key.include?(
|
|
282
|
+
strip_key = parameter_prefix ? k.sub(/#{parameter_prefix.join("__")}_{2}?/, "") : k
|
|
283
|
+
unless strip_key.include?("__")
|
|
284
284
|
[strip_key, v]
|
|
285
285
|
end
|
|
286
286
|
end.compact
|
|
@@ -299,7 +299,7 @@ module Sfn
|
|
|
299
299
|
def config_root_parameters
|
|
300
300
|
Hash[
|
|
301
301
|
config.fetch(:parameters, {}).find_all do |k, v|
|
|
302
|
-
!k.include?(
|
|
302
|
+
!k.include?("__")
|
|
303
303
|
end
|
|
304
304
|
]
|
|
305
305
|
end
|
|
@@ -318,13 +318,13 @@ module Sfn
|
|
|
318
318
|
def validate_stack_parameter(c_stack, p_key, p_ns_key, c_value)
|
|
319
319
|
stack_value = c_stack.parameters[p_key]
|
|
320
320
|
p_stack = c_stack.data[:parent_stack]
|
|
321
|
-
unless config[:parameter_validation] ==
|
|
321
|
+
unless config[:parameter_validation] == "none"
|
|
322
322
|
if c_value.is_a?(Hash)
|
|
323
323
|
case c_value.keys.first
|
|
324
|
-
when
|
|
324
|
+
when "Ref"
|
|
325
325
|
current_value = p_stack.parameters[c_value.values.first]
|
|
326
|
-
when
|
|
327
|
-
resource_name, output_name = c_value.values.first.split(
|
|
326
|
+
when "Fn::Att"
|
|
327
|
+
resource_name, output_name = c_value.values.first.split(".", 2)
|
|
328
328
|
ref_stack = p_stack.nested_stacks.detect { |i| i.data[:logical_id] == resource_name }
|
|
329
329
|
if ref_stack
|
|
330
330
|
output = ref_stack.outputs.detect do |o|
|
|
@@ -339,14 +339,14 @@ module Sfn
|
|
|
339
339
|
current_value = c_value
|
|
340
340
|
end
|
|
341
341
|
if current_value && current_value.to_s != stack_value.to_s
|
|
342
|
-
if config[:parameter_validation] ==
|
|
343
|
-
ui.warn
|
|
342
|
+
if config[:parameter_validation] == "default"
|
|
343
|
+
ui.warn "Nested stack has been altered directly! This update may cause unexpected modifications!"
|
|
344
344
|
ui.warn "Stack name: #{c_stack.name}. Parameter: #{p_key}. Current value: #{stack_value}. Expected value: #{current_value} (via: #{c_value.inspect})"
|
|
345
|
-
answer = ui.ask_question("Use current value or expected value for #{p_key} [current/expected]?", :valid => [
|
|
345
|
+
answer = ui.ask_question("Use current value or expected value for #{p_key} [current/expected]?", :valid => ["current", "expected"])
|
|
346
346
|
else
|
|
347
347
|
answer = config[:parameter_validation]
|
|
348
348
|
end
|
|
349
|
-
answer ==
|
|
349
|
+
answer == "expected"
|
|
350
350
|
else
|
|
351
351
|
true
|
|
352
352
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "sfn"
|
|
2
|
+
require "sparkle_formation"
|
|
3
3
|
|
|
4
|
-
require
|
|
4
|
+
require "pathname"
|
|
5
5
|
|
|
6
6
|
module Sfn
|
|
7
7
|
module CommandModule
|
|
@@ -48,7 +48,7 @@ module Sfn
|
|
|
48
48
|
def request_compile_parameter(p_name, p_config, cur_val, nested = false)
|
|
49
49
|
result = nil
|
|
50
50
|
attempts = 0
|
|
51
|
-
parameter_type = p_config.fetch(:type,
|
|
51
|
+
parameter_type = p_config.fetch(:type, "string").to_s.downcase.to_sym
|
|
52
52
|
if parameter_type == :complex
|
|
53
53
|
ui.debug "Compile time parameter `#{p_name}` is a complex type. Not requesting value from user."
|
|
54
54
|
if cur_val.nil?
|
|
@@ -61,13 +61,13 @@ module Sfn
|
|
|
61
61
|
cur_val = p_config[:default]
|
|
62
62
|
end
|
|
63
63
|
if cur_val.is_a?(Array)
|
|
64
|
-
cur_val = cur_val.map(&:to_s).join(
|
|
64
|
+
cur_val = cur_val.map(&:to_s).join(",")
|
|
65
65
|
end
|
|
66
66
|
until result && (!result.respond_to?(:empty?) || !result.empty?)
|
|
67
67
|
attempts += 1
|
|
68
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
|
-
p_name.to_s.split(
|
|
70
|
+
p_name.to_s.split("_").map(&:capitalize).join,
|
|
71
71
|
:default => cur_val.to_s.empty? ? nil : cur_val.to_s,
|
|
72
72
|
)
|
|
73
73
|
else
|
|
@@ -76,11 +76,11 @@ module Sfn
|
|
|
76
76
|
case parameter_type
|
|
77
77
|
when :string
|
|
78
78
|
if p_config[:multiple]
|
|
79
|
-
result = result.split(
|
|
79
|
+
result = result.split(",").map(&:strip)
|
|
80
80
|
end
|
|
81
81
|
when :number
|
|
82
82
|
if p_config[:multiple]
|
|
83
|
-
result = result.split(
|
|
83
|
+
result = result.split(",").map(&:strip)
|
|
84
84
|
new_result = result.map do |item|
|
|
85
85
|
new_item = item.to_i
|
|
86
86
|
new_item if new_item.to_s == item
|
|
@@ -97,7 +97,7 @@ module Sfn
|
|
|
97
97
|
unless valid == true
|
|
98
98
|
result = nil
|
|
99
99
|
valid.each do |invalid_msg|
|
|
100
|
-
ui.error invalid_msg.last
|
|
100
|
+
ui.error "#{p_name}: #{invalid_msg.last}"
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
if result.nil? || (result.respond_to?(:empty?) && result.empty?)
|
|
@@ -150,7 +150,7 @@ module Sfn
|
|
|
150
150
|
end
|
|
151
151
|
collection.set_root(root_pack)
|
|
152
152
|
rescue Errno::ENOENT
|
|
153
|
-
ui.warn
|
|
153
|
+
ui.warn "No local SparkleFormation files detected"
|
|
154
154
|
end
|
|
155
155
|
sparkle_packs.each do |pack|
|
|
156
156
|
collection.add_sparkle(pack)
|
|
@@ -185,8 +185,8 @@ module Sfn
|
|
|
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
|
-
pathed_name = f_name.join(
|
|
189
|
-
f_name = f_name.join(
|
|
188
|
+
pathed_name = f_name.join(" > ")
|
|
189
|
+
f_name = f_name.join("__")
|
|
190
190
|
if formation.root? && compile_state[f_name].nil?
|
|
191
191
|
current_state = compile_state
|
|
192
192
|
else
|
|
@@ -200,7 +200,7 @@ module Sfn
|
|
|
200
200
|
current_state = current_state.merge(formation.compile_state)
|
|
201
201
|
end
|
|
202
202
|
unless formation.parameters.empty?
|
|
203
|
-
ui.info "#{ui.color(
|
|
203
|
+
ui.info "#{ui.color("Compile time parameters:", :bold)} - template: #{ui.color(pathed_name, :green, :bold)}" unless config[:print_only]
|
|
204
204
|
formation.parameters.each do |k, v|
|
|
205
205
|
valid_keys = [
|
|
206
206
|
"#{f_name}__#{k}",
|
|
@@ -251,7 +251,7 @@ module Sfn
|
|
|
251
251
|
template
|
|
252
252
|
end
|
|
253
253
|
else
|
|
254
|
-
raise ArgumentError.new
|
|
254
|
+
raise ArgumentError.new "Failed to locate template for processing!"
|
|
255
255
|
end
|
|
256
256
|
end
|
|
257
257
|
|
|
@@ -262,7 +262,7 @@ module Sfn
|
|
|
262
262
|
ui.debug "Initial compile parameters - #{compile_state}"
|
|
263
263
|
compile_state.keys.each do |cs_key|
|
|
264
264
|
unless cs_key.to_s.start_with?("#{arguments.first}__")
|
|
265
|
-
named_cs_key = [arguments.first, cs_key].compact.join(
|
|
265
|
+
named_cs_key = [arguments.first, cs_key].compact.join("__")
|
|
266
266
|
non_named = compile_state.delete(cs_key)
|
|
267
267
|
if non_named && !compile_state.key?(named_cs_key)
|
|
268
268
|
ui.debug "Setting non-named compile parameter `#{cs_key}` into `#{named_cs_key}`"
|
|
@@ -282,8 +282,8 @@ module Sfn
|
|
|
282
282
|
# Force user friendly error if nesting bucket is not set within configuration
|
|
283
283
|
def validate_nesting_bucket!
|
|
284
284
|
if config[:nesting_bucket].to_s.empty?
|
|
285
|
-
ui.error
|
|
286
|
-
raise ArgumentError.new
|
|
285
|
+
ui.error "Missing required configuration value for `nesting_bucket`. Cannot generated nested templates!"
|
|
286
|
+
raise ArgumentError.new "Required configuration value for `nesting_bucket` not provided."
|
|
287
287
|
end
|
|
288
288
|
end
|
|
289
289
|
|
|
@@ -307,13 +307,13 @@ module Sfn
|
|
|
307
307
|
end
|
|
308
308
|
file = bucket.files.build
|
|
309
309
|
file.name = "#{name_args.first}_#{stack_name}.json"
|
|
310
|
-
file.content_type =
|
|
310
|
+
file.content_type = "text/json"
|
|
311
311
|
file.body = MultiJson.dump(parameter_scrub!(stack_definition))
|
|
312
312
|
file.save
|
|
313
313
|
url = URI.parse(file.url)
|
|
314
314
|
template_url = "#{url.scheme}://#{url.host}#{url.path}"
|
|
315
315
|
end
|
|
316
|
-
resource.properties.set!(
|
|
316
|
+
resource.properties.set!("TemplateURL", template_url)
|
|
317
317
|
end
|
|
318
318
|
end
|
|
319
319
|
|
|
@@ -331,16 +331,16 @@ module Sfn
|
|
|
331
331
|
if current_stack && current_stack.data[:parent_stack]
|
|
332
332
|
current_parameters.merge!(
|
|
333
333
|
current_stack.data[:parent_stack].template.fetch(
|
|
334
|
-
|
|
335
|
-
|
|
334
|
+
"Resources", stack_name, "Properties", "Parameters", current_stack.data[:parent_stack].template.fetch(
|
|
335
|
+
"resources", stack_name, "properties", "parameters", Smash.new
|
|
336
336
|
)
|
|
337
337
|
)
|
|
338
338
|
)
|
|
339
339
|
end
|
|
340
340
|
full_stack_name = [
|
|
341
341
|
config[:nesting_prefix],
|
|
342
|
-
stack.root_path.map(&:name).map(&:to_s).join(
|
|
343
|
-
].compact.join(
|
|
342
|
+
stack.root_path.map(&:name).map(&:to_s).join("_"),
|
|
343
|
+
].compact.join("/")
|
|
344
344
|
unless config[:print_only]
|
|
345
345
|
result = Smash.new(
|
|
346
346
|
:parameters => populate_parameters!(stack,
|
|
@@ -390,7 +390,7 @@ module Sfn
|
|
|
390
390
|
end
|
|
391
391
|
file = bucket.files.build
|
|
392
392
|
file.name = "#{full_stack_name}.json"
|
|
393
|
-
file.content_type =
|
|
393
|
+
file.content_type = "text/json"
|
|
394
394
|
file.body = MultiJson.dump(parameter_scrub!(stack_definition))
|
|
395
395
|
file.save
|
|
396
396
|
result.merge!(
|
|
@@ -422,9 +422,9 @@ module Sfn
|
|
|
422
422
|
# @return [Hash]
|
|
423
423
|
def scrub_template(template)
|
|
424
424
|
template = parameter_scrub!(template)
|
|
425
|
-
(template[
|
|
426
|
-
if valid_stack_types.include?(r_content[
|
|
427
|
-
result = (r_content[
|
|
425
|
+
(template["Resources"] || {}).each do |r_name, r_content|
|
|
426
|
+
if valid_stack_types.include?(r_content["Type"])
|
|
427
|
+
result = (r_content["Properties"] || {}).delete("Stack")
|
|
428
428
|
end
|
|
429
429
|
end
|
|
430
430
|
template
|
|
@@ -439,11 +439,11 @@ module Sfn
|
|
|
439
439
|
case provider
|
|
440
440
|
when :aws
|
|
441
441
|
if results[:parameters]
|
|
442
|
-
results[
|
|
442
|
+
results["Parameters"] = results.delete(:parameters)
|
|
443
443
|
end
|
|
444
444
|
if results[:url]
|
|
445
445
|
url = URI.parse(results.delete(:url))
|
|
446
|
-
results[
|
|
446
|
+
results["TemplateURL"] = "#{url.scheme}://#{url.host}#{url.path}"
|
|
447
447
|
end
|
|
448
448
|
results
|
|
449
449
|
when :heat, :rackspace
|
|
@@ -461,10 +461,10 @@ module Sfn
|
|
|
461
461
|
if results[:url]
|
|
462
462
|
results[:templateLink] = Smash.new(
|
|
463
463
|
:uri => results.delete(:url),
|
|
464
|
-
:contentVersion =>
|
|
464
|
+
:contentVersion => "1.0.0.0",
|
|
465
465
|
)
|
|
466
466
|
end
|
|
467
|
-
results[:mode] =
|
|
467
|
+
results[:mode] = "Incremental"
|
|
468
468
|
results
|
|
469
469
|
else
|
|
470
470
|
raise "Unknown stack provider value given! `#{provider}`"
|
|
@@ -491,7 +491,7 @@ module Sfn
|
|
|
491
491
|
translator = klass.new(template, args)
|
|
492
492
|
translator.translate!
|
|
493
493
|
template = translator.translated
|
|
494
|
-
ui.info "#{ui.color(
|
|
494
|
+
ui.info "#{ui.color("Translation applied:", :bold)} #{ui.color(klass_name, :yellow)}"
|
|
495
495
|
end
|
|
496
496
|
template
|
|
497
497
|
end
|
|
@@ -530,9 +530,9 @@ module Sfn
|
|
|
530
530
|
# @return [String] path to template
|
|
531
531
|
def prompt_for_template(prefix = nil)
|
|
532
532
|
if prefix
|
|
533
|
-
collection_name = prefix.split(
|
|
534
|
-
c_name.split(
|
|
535
|
-
end.join(
|
|
533
|
+
collection_name = prefix.split("__").map do |c_name|
|
|
534
|
+
c_name.split("_").map(&:capitalize).join(" ")
|
|
535
|
+
end.join(" / ")
|
|
536
536
|
ui.info "Viewing collection: #{ui.color(collection_name, :bold)}"
|
|
537
537
|
template_names = sparkle_collection.templates.fetch(provider.connection.provider, {}).keys.find_all do |t_name|
|
|
538
538
|
t_name.to_s.start_with?(prefix.to_s)
|
|
@@ -541,46 +541,46 @@ module Sfn
|
|
|
541
541
|
template_names = sparkle_collection.templates.fetch(provider.connection.provider, {}).keys
|
|
542
542
|
end
|
|
543
543
|
collections = template_names.map do |t_name|
|
|
544
|
-
t_name = t_name.to_s.sub(/^#{Regexp.escape(prefix.to_s)}/,
|
|
545
|
-
if t_name.include?(
|
|
546
|
-
c_name = t_name.split(
|
|
547
|
-
[[prefix, c_name].compact.join(
|
|
544
|
+
t_name = t_name.to_s.sub(/^#{Regexp.escape(prefix.to_s)}/, "")
|
|
545
|
+
if t_name.include?("__")
|
|
546
|
+
c_name = t_name.split("__").first
|
|
547
|
+
[[prefix, c_name].compact.join("") + "__", c_name]
|
|
548
548
|
end
|
|
549
549
|
end.compact.uniq(&:first)
|
|
550
550
|
templates = template_names.map do |t_name|
|
|
551
|
-
t_name = t_name.to_s.sub(/^#{Regexp.escape(prefix.to_s)}/,
|
|
552
|
-
unless t_name.include?(
|
|
553
|
-
[[prefix, t_name].compact.join(
|
|
551
|
+
t_name = t_name.to_s.sub(/^#{Regexp.escape(prefix.to_s)}/, "")
|
|
552
|
+
unless t_name.include?("__")
|
|
553
|
+
[[prefix, t_name].compact.join(""), t_name]
|
|
554
554
|
end
|
|
555
555
|
end.compact
|
|
556
556
|
if collections.empty? && templates.empty?
|
|
557
|
-
ui.error
|
|
557
|
+
ui.error "Failed to locate any templates!"
|
|
558
558
|
return nil
|
|
559
559
|
end
|
|
560
|
-
ui.info "Please select an entry#{
|
|
560
|
+
ui.info "Please select an entry#{"(or collection to list)" unless collections.empty?}:"
|
|
561
561
|
output = []
|
|
562
562
|
idx = 1
|
|
563
563
|
valid = {}
|
|
564
564
|
unless collections.empty?
|
|
565
|
-
output << ui.color(
|
|
565
|
+
output << ui.color("Collections:", :bold)
|
|
566
566
|
collections.each do |full_name, part_name|
|
|
567
567
|
valid[idx] = {:name => full_name, :type => :collection}
|
|
568
|
-
output << [idx, part_name.split(
|
|
568
|
+
output << [idx, part_name.split("_").map(&:capitalize).join(" ")]
|
|
569
569
|
idx += 1
|
|
570
570
|
end
|
|
571
571
|
end
|
|
572
572
|
unless templates.empty?
|
|
573
|
-
output << ui.color(
|
|
573
|
+
output << ui.color("Templates:", :bold)
|
|
574
574
|
templates.each do |full_name, part_name|
|
|
575
575
|
valid[idx] = {:name => full_name, :type => :template}
|
|
576
|
-
output << [idx, part_name.split(
|
|
576
|
+
output << [idx, part_name.split("_").map(&:capitalize).join(" ")]
|
|
577
577
|
idx += 1
|
|
578
578
|
end
|
|
579
579
|
end
|
|
580
580
|
max = idx.to_s.length
|
|
581
581
|
output.map! do |line|
|
|
582
582
|
if line.is_a?(Array)
|
|
583
|
-
" #{line.first}.#{
|
|
583
|
+
" #{line.first}.#{" " * (max - line.first.to_s.length)} #{line.last}"
|
|
584
584
|
else
|
|
585
585
|
line
|
|
586
586
|
end
|
|
@@ -588,7 +588,7 @@ module Sfn
|
|
|
588
588
|
ui.puts "#{output.join("\n")}\n"
|
|
589
589
|
response = nil
|
|
590
590
|
until valid[response]
|
|
591
|
-
response = ui.ask_question(
|
|
591
|
+
response = ui.ask_question("Enter selection").to_i
|
|
592
592
|
end
|
|
593
593
|
entry = valid[response]
|
|
594
594
|
if entry[:type] == :collection
|