sfn 0.5.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -8
- data/README.md +85 -53
- data/bin/generate_sfn_docs +37 -0
- data/lib/sfn/callback/stack_policy.rb +94 -0
- data/lib/sfn/callback.rb +48 -0
- data/lib/sfn/command/create.rb +19 -20
- data/lib/sfn/command/describe.rb +16 -10
- data/lib/sfn/command/destroy.rb +8 -3
- data/lib/sfn/command/events.rb +24 -22
- data/lib/sfn/command/inspect.rb +8 -6
- data/lib/sfn/command/update.rb +49 -72
- data/lib/sfn/command/validate.rb +30 -4
- data/lib/sfn/command.rb +8 -5
- data/lib/sfn/command_module/base.rb +17 -2
- data/lib/sfn/command_module/callbacks.rb +69 -0
- data/lib/sfn/command_module/stack.rb +107 -9
- data/lib/sfn/command_module/template.rb +201 -129
- data/lib/sfn/command_module.rb +1 -0
- data/lib/sfn/config/update.rb +0 -4
- data/lib/sfn/config/validate.rb +12 -2
- data/lib/sfn/monkey_patch/stack.rb +72 -0
- data/lib/sfn/utils/path_selector.rb +3 -0
- data/lib/sfn/version.rb +1 -1
- data/lib/sfn.rb +1 -0
- data/sfn.gemspec +8 -10
- metadata +19 -22
@@ -10,71 +10,33 @@ module Sfn
|
|
10
10
|
|
11
11
|
# cloudformation directories that should be ignored
|
12
12
|
TEMPLATE_IGNORE_DIRECTORIES = %w(components dynamics registry)
|
13
|
-
# maximum number of attempts to get valid parameter value
|
14
|
-
MAX_PARAMETER_ATTEMPTS = 5
|
15
13
|
|
16
14
|
module InstanceMethods
|
17
15
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
# @param nested [TrueClass, FalseClass] template is nested
|
24
|
-
# @option p_config [String, Symbol] :type
|
25
|
-
# @option p_config [String, Symbol] :default
|
26
|
-
# @option p_config [String, Symbol] :description
|
27
|
-
# @option p_config [String, Symbol] :multiple
|
28
|
-
# @return [Object]
|
29
|
-
def request_compile_parameter(p_name, p_config, cur_val, nested=false)
|
30
|
-
result = nil
|
31
|
-
attempts = 0
|
32
|
-
unless(cur_val || p_config[:default].nil?)
|
33
|
-
cur_val = p_config[:default]
|
34
|
-
end
|
35
|
-
if(cur_val.is_a?(Array))
|
36
|
-
cur_val = cur_val.map(&:to_s).join(',')
|
37
|
-
end
|
38
|
-
until(result && (!result.respond_to?(:empty?) || !result.empty?))
|
39
|
-
attempts += 1
|
40
|
-
if(config[:interactive_parameters] && (!nested || !p_config.key?(:prompt_when_nested) || p_config[:prompt_when_nested] == true))
|
41
|
-
result = ui.ask_question(
|
42
|
-
p_name.to_s.split('_').map(&:capitalize).join,
|
43
|
-
:default => cur_val.to_s.empty? ? nil : cur_val.to_s
|
44
|
-
)
|
45
|
-
else
|
46
|
-
result = cur_val.to_s
|
16
|
+
# @return [Array<SparkleFormation::SparklePack>]
|
17
|
+
def sparkle_packs
|
18
|
+
memoize(:sparkle_packs) do
|
19
|
+
config.fetch(:sparkle_pack, []).map do |sparkle_name|
|
20
|
+
SparkleFormation::Sparkle.new(:name => sparkle_name)
|
47
21
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
result = new_result.size == result.size ? new_result : []
|
61
|
-
else
|
62
|
-
new_result = result.to_i
|
63
|
-
result = new_result.to_s == result ? new_result : nil
|
64
|
-
end
|
65
|
-
else
|
66
|
-
raise ArgumentError.new "Unknown compile time parameter type provided: `#{p_config[:type].inspect}` (Parameter: #{p_name})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [SparkleFormation::SparkleCollection]
|
26
|
+
def sparkle_collection
|
27
|
+
memoize(:sparkle_collection) do
|
28
|
+
collection = SparkleFormation::SparkleCollection.new
|
29
|
+
begin
|
30
|
+
root_pack = SparkleFormation::SparklePack.new(:root => config[:base_directory])
|
31
|
+
collection.set_root(root_pack)
|
32
|
+
rescue Errno::ENOENT
|
33
|
+
ui.warn 'No local SparkleFormation files detected'
|
67
34
|
end
|
68
|
-
|
69
|
-
|
70
|
-
ui.fatal 'Failed to receive allowed parameter!'
|
71
|
-
exit 1
|
72
|
-
else
|
73
|
-
ui.error "Invalid value provided for parameter. Must be type: `#{p_config[:type].to_s.capitalize}`"
|
74
|
-
end
|
35
|
+
sparkle_packs.each do |pack|
|
36
|
+
collection.add_sparkle(pack)
|
75
37
|
end
|
38
|
+
collection
|
76
39
|
end
|
77
|
-
result
|
78
40
|
end
|
79
41
|
|
80
42
|
# Load the template file
|
@@ -82,9 +44,10 @@ module Sfn
|
|
82
44
|
# @param args [Symbol] options (:allow_missing)
|
83
45
|
# @return [Hash] loaded template
|
84
46
|
def load_template_file(*args)
|
47
|
+
c_stack = (args.detect{|i| i.is_a?(Hash)} || {})[:stack]
|
85
48
|
unless(config[:template])
|
86
49
|
set_paths_and_discover_file!
|
87
|
-
unless(
|
50
|
+
unless(config[:file])
|
88
51
|
unless(args.include?(:allow_missing))
|
89
52
|
ui.fatal "Invalid formation file path provided: #{config[:file]}"
|
90
53
|
raise IOError.new "Failed to locate file: #{config[:file]}"
|
@@ -95,59 +58,110 @@ module Sfn
|
|
95
58
|
config[:template]
|
96
59
|
elsif(config[:file])
|
97
60
|
if(config[:processing])
|
98
|
-
compile_state = config.fetch(:compile_parameters, Smash.new)
|
99
61
|
sf = SparkleFormation.compile(config[:file], :sparkle)
|
100
|
-
|
101
|
-
sf.
|
102
|
-
end
|
103
|
-
sf.compile_time_parameter_setter do |formation|
|
104
|
-
f_name = []
|
105
|
-
f_form = formation
|
106
|
-
while(f_form)
|
107
|
-
f_name.push f_form.name
|
108
|
-
f_form = f_form.parent
|
109
|
-
end
|
110
|
-
f_name = f_name.reverse.map(&:to_s).join('_')
|
111
|
-
current_state = compile_state.fetch(f_name, Smash.new)
|
112
|
-
if(formation.compile_state)
|
113
|
-
current_state = current_state.merge(formation.compile_state)
|
114
|
-
end
|
115
|
-
ui.info "#{ui.color('Compile time parameters:', :bold)} - template: #{ui.color(formation.name, :green, :bold)}"
|
116
|
-
formation.parameters.each do |k,v|
|
117
|
-
current_state[k] = request_compile_parameter(k, v, current_state[k], !!formation.parent)
|
118
|
-
end
|
119
|
-
formation.compile_state = current_state
|
62
|
+
sparkle_packs.each do |pack|
|
63
|
+
sf.sparkle.add_sparkle(pack)
|
120
64
|
end
|
121
65
|
if(sf.nested? && !sf.isolated_nests?)
|
122
|
-
raise TypeError.new('Template does not contain isolated stack nesting!
|
66
|
+
raise TypeError.new('Template does not contain isolated stack nesting! Sfn does not support mixed mixed resources within root stack!')
|
123
67
|
end
|
68
|
+
run_callbacks_for(:template, :stack_name => arguments.first, :sparkle_stack => sf)
|
124
69
|
if(sf.nested? && config[:apply_nesting])
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
end
|
136
|
-
file = bucket.files.build
|
137
|
-
file.name = "#{name_args.first}_#{stack_name}.json"
|
138
|
-
file.content_type = 'text/json'
|
139
|
-
file.body = MultiJson.dump(Sfn::Utils::StackParameterScrubber.scrub!(stack_definition))
|
140
|
-
file.save
|
141
|
-
# TODO: what if we need extra params?
|
142
|
-
url = URI.parse(file.url)
|
143
|
-
"#{url.scheme}://#{url.host}#{url.path}"
|
144
|
-
end
|
70
|
+
if(config[:apply_nesting] == true)
|
71
|
+
config[:apply_nesting] = :deep
|
72
|
+
end
|
73
|
+
case config[:apply_nesting].to_sym
|
74
|
+
when :deep
|
75
|
+
process_nested_stack_deep(sf, c_stack)
|
76
|
+
when :shallow
|
77
|
+
process_nested_stack_shallow(sf, c_stack)
|
78
|
+
else
|
79
|
+
raise ArgumentError.new "Unknown nesting style requested: #{config[:apply_nesting].inspect}!"
|
145
80
|
end
|
146
81
|
else
|
147
82
|
sf.dump.merge('sfn_nested_stack' => !!sf.nested?)
|
148
83
|
end
|
149
84
|
else
|
150
|
-
_from_json(File.read(config[:file]))
|
85
|
+
template = _from_json(File.read(config[:file]))
|
86
|
+
run_callbacks_for(:template, :stack_name => arguments.first, :hash_stack => template)
|
87
|
+
template
|
88
|
+
end
|
89
|
+
else
|
90
|
+
raise ArgumentError.new 'Failed to locate template for processing!'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Processes template using the original shallow workflow
|
95
|
+
#
|
96
|
+
# @param sf [SparkleFormation] stack formation
|
97
|
+
# @param c_stack [Miasma::Models::Orchestration::Stack] existing stack
|
98
|
+
# @return [Hash] dumped stack
|
99
|
+
def process_nested_stack_shallow(sf, c_stack=nil)
|
100
|
+
sf.apply_nesting(:shallow) do |stack_name, stack, resource|
|
101
|
+
run_callbacks_for(:template, :stack_name => stack_name, :sparkle_stack => stack)
|
102
|
+
stack_definition = stack.compile.dump!
|
103
|
+
bucket = provider.connection.api_for(:storage).buckets.get(
|
104
|
+
config[:nesting_bucket]
|
105
|
+
)
|
106
|
+
if(config[:print_only])
|
107
|
+
template_url = "http://example.com/bucket/#{name_args.first}_#{stack_name}.json"
|
108
|
+
else
|
109
|
+
resource.properties.delete!(:stack)
|
110
|
+
unless(bucket)
|
111
|
+
raise "Failed to locate configured bucket for stack template storage (#{bucket})!"
|
112
|
+
end
|
113
|
+
file = bucket.files.build
|
114
|
+
file.name = "#{name_args.first}_#{stack_name}.json"
|
115
|
+
file.content_type = 'text/json'
|
116
|
+
file.body = MultiJson.dump(Sfn::Utils::StackParameterScrubber.scrub!(stack_definition))
|
117
|
+
file.save
|
118
|
+
url = URI.parse(file.url)
|
119
|
+
template_url = "#{url.scheme}://#{url.host}#{url.path}"
|
120
|
+
end
|
121
|
+
resource.properties.set!('TemplateURL', template_url)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Processes template using new deep workflow
|
126
|
+
#
|
127
|
+
# @param sf [SparkleFormation] stack
|
128
|
+
# @param c_stack [Miasma::Models::Orchestration::Stack] existing stack
|
129
|
+
# @return [Hash] dumped stack
|
130
|
+
def process_nested_stack_deep(sf, c_stack=nil)
|
131
|
+
sf.apply_nesting(:deep) do |stack_name, stack, resource|
|
132
|
+
run_callbacks_for(:template, :stack_name => stack_name, :sparkle_stack => stack)
|
133
|
+
stack_definition = stack.compile.dump!
|
134
|
+
stack_resource = resource._dump
|
135
|
+
unless(config[:print_only])
|
136
|
+
result = Smash.new(
|
137
|
+
'Parameters' => populate_parameters!(stack,
|
138
|
+
:stack => c_stack ? c_stack.nested_stacks.detect{|s| s.data[:logical_id] == stack_name} : nil,
|
139
|
+
:current_parameters => c_stack ? c_stack.template.fetch('Resources', stack_name, 'Properties', 'Parameters', Smash.new) : stack_resource['Properties'].fetch('Parameters', {})
|
140
|
+
)
|
141
|
+
)
|
142
|
+
resource.properties.delete!(:stack)
|
143
|
+
bucket = provider.connection.api_for(:storage).buckets.get(
|
144
|
+
config[:nesting_bucket]
|
145
|
+
)
|
146
|
+
unless(bucket)
|
147
|
+
raise "Failed to locate configured bucket for stack template storage (#{bucket})!"
|
148
|
+
end
|
149
|
+
file = bucket.files.build
|
150
|
+
file.name = "#{name_args.first}_#{stack_name}.json"
|
151
|
+
file.content_type = 'text/json'
|
152
|
+
file.body = MultiJson.dump(Sfn::Utils::StackParameterScrubber.scrub!(stack_definition))
|
153
|
+
file.save
|
154
|
+
url = URI.parse(file.url)
|
155
|
+
result.merge!(
|
156
|
+
'TemplateURL' => "#{url.scheme}://#{url.host}#{url.path}"
|
157
|
+
)
|
158
|
+
else
|
159
|
+
result = Smash.new(
|
160
|
+
'TemplateURL' => "http://example.com/bucket/#{name_args.first}_#{stack_name}.json"
|
161
|
+
)
|
162
|
+
end
|
163
|
+
result.each do |k,v|
|
164
|
+
resource.properties.set!(k, v)
|
151
165
|
end
|
152
166
|
end
|
153
167
|
end
|
@@ -181,40 +195,98 @@ module Sfn
|
|
181
195
|
#
|
182
196
|
# @return [TrueClass]
|
183
197
|
def set_paths_and_discover_file!
|
184
|
-
if(config[:
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
File.join(Dir.pwd, 'cloudformation')
|
191
|
-
)
|
192
|
-
).split('/')
|
193
|
-
bucket = root.pop
|
194
|
-
root = root.join('/')
|
195
|
-
directory = File.join(root, bucket)
|
196
|
-
config[:file] = prompt_for_file(directory,
|
197
|
-
:directories_name => 'Collections',
|
198
|
-
:files_name => 'Templates',
|
199
|
-
:ignore_directories => TEMPLATE_IGNORE_DIRECTORIES
|
200
|
-
)
|
198
|
+
if(config[:processing])
|
199
|
+
if(!config[:file] && config[:file_path_prompt])
|
200
|
+
config[:file] = prompt_for_template
|
201
|
+
else
|
202
|
+
config[:file] = sparkle_collection.get(:template, config[:file])[:path]
|
203
|
+
end
|
201
204
|
else
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
pwd = Dir.pwd
|
206
|
-
config[:file] = [
|
207
|
-
File.join(base_dir, file),
|
208
|
-
File.join(pwd, file),
|
209
|
-
File.join(pwd, 'cloudformation', file)
|
210
|
-
].detect do |file_path|
|
211
|
-
File.file?(file_path)
|
205
|
+
if(config[:file])
|
206
|
+
unless(File.exists?(config[:file]))
|
207
|
+
raise Errno::ENOENT.new("No such file - #{config[:file]}")
|
212
208
|
end
|
209
|
+
else
|
210
|
+
raise "Template processing is disabled. Path to serialized template via `--file` required!"
|
213
211
|
end
|
214
212
|
end
|
215
213
|
true
|
216
214
|
end
|
217
215
|
|
216
|
+
# Prompt user for template selection
|
217
|
+
#
|
218
|
+
# @param prefix [String] prefix filter for names
|
219
|
+
# @return [String] path to template
|
220
|
+
def prompt_for_template(prefix=nil)
|
221
|
+
if(prefix)
|
222
|
+
collection_name = prefix.split('__').map do |c_name|
|
223
|
+
c_name.split('_').map(&:capitalize).join(' ')
|
224
|
+
end.join(' / ')
|
225
|
+
ui.info "Viewing collection: #{ui.color(collection_name, :bold)}"
|
226
|
+
template_names = sparkle_collection.templates.keys.find_all do |t_name|
|
227
|
+
t_name.to_s.start_with?(prefix.to_s)
|
228
|
+
end
|
229
|
+
else
|
230
|
+
template_names = sparkle_collection.templates.keys
|
231
|
+
end
|
232
|
+
collections = template_names.map do |t_name|
|
233
|
+
t_name = t_name.to_s.sub(/^#{Regexp.escape(prefix.to_s)}/, '')
|
234
|
+
if(t_name.include?('__'))
|
235
|
+
c_name = t_name.split('__').first
|
236
|
+
[[prefix, c_name].compact.join('') + '__', c_name]
|
237
|
+
end
|
238
|
+
end.compact.uniq(&:first)
|
239
|
+
templates = template_names.map do |t_name|
|
240
|
+
t_name = t_name.to_s.sub(/^#{Regexp.escape(prefix.to_s)}/, '')
|
241
|
+
unless(t_name.include?('__'))
|
242
|
+
[[prefix, t_name].compact.join(''), t_name]
|
243
|
+
end
|
244
|
+
end.compact
|
245
|
+
if(collections.empty? && templates.empty?)
|
246
|
+
ui.error 'Failed to locate any templates!'
|
247
|
+
return nil
|
248
|
+
end
|
249
|
+
ui.info "Please select an entry#{ '(or collection to list)' unless collections.empty?}:"
|
250
|
+
output = []
|
251
|
+
idx = 1
|
252
|
+
valid = {}
|
253
|
+
unless(collections.empty?)
|
254
|
+
output << ui.color('Collections:', :bold)
|
255
|
+
collections.each do |full_name, part_name|
|
256
|
+
valid[idx] = {:name => full_name, :type => :collection}
|
257
|
+
output << [idx, part_name.split('_').map(&:capitalize).join(' ')]
|
258
|
+
idx += 1
|
259
|
+
end
|
260
|
+
end
|
261
|
+
unless(templates.empty?)
|
262
|
+
output << ui.color('Templates:', :bold)
|
263
|
+
templates.each do |full_name, part_name|
|
264
|
+
valid[idx] = {:name => full_name, :type => :template}
|
265
|
+
output << [idx, part_name.split('_').map(&:capitalize).join(' ')]
|
266
|
+
idx += 1
|
267
|
+
end
|
268
|
+
end
|
269
|
+
max = idx.to_s.length
|
270
|
+
output.map! do |line|
|
271
|
+
if(line.is_a?(Array))
|
272
|
+
" #{line.first}.#{' ' * (max - line.first.to_s.length)} #{line.last}"
|
273
|
+
else
|
274
|
+
line
|
275
|
+
end
|
276
|
+
end
|
277
|
+
ui.puts "#{output.join("\n")}\n"
|
278
|
+
response = nil
|
279
|
+
until(valid[response])
|
280
|
+
response = ui.ask_question('Enter selection').to_i
|
281
|
+
end
|
282
|
+
entry = valid[response]
|
283
|
+
if(entry[:type] == :collection)
|
284
|
+
prompt_for_template(entry[:name])
|
285
|
+
else
|
286
|
+
sparkle_collection.get(:template, entry[:name])[:path]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
218
290
|
end
|
219
291
|
|
220
292
|
module ClassMethods
|
data/lib/sfn/command_module.rb
CHANGED
data/lib/sfn/config/update.rb
CHANGED
data/lib/sfn/config/validate.rb
CHANGED
@@ -37,14 +37,24 @@ module Sfn
|
|
37
37
|
:coerce => lambda{|v| v.to_i}
|
38
38
|
)
|
39
39
|
attribute(
|
40
|
-
:apply_nesting, [
|
41
|
-
:default =>
|
40
|
+
:apply_nesting, [String, Symbol],
|
41
|
+
:default => 'deep',
|
42
42
|
:description => 'Apply stack nesting'
|
43
43
|
)
|
44
44
|
attribute(
|
45
45
|
:nesting_bucket, String,
|
46
46
|
:description => 'Bucket to use for storing nested stack templates'
|
47
47
|
)
|
48
|
+
attribute(
|
49
|
+
:print_only, [TrueClass, FalseClass],
|
50
|
+
:description => 'Print the resulting stack template'
|
51
|
+
)
|
52
|
+
attribute(
|
53
|
+
:sparkle_pack, String,
|
54
|
+
:multiple => true,
|
55
|
+
:description => 'Load SparklePack gem',
|
56
|
+
:coerce => lambda{|s| require s; s}
|
57
|
+
)
|
48
58
|
|
49
59
|
end
|
50
60
|
end
|
@@ -187,6 +187,78 @@ module Sfn
|
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
+
# Return all stacks contained within this stack
|
191
|
+
#
|
192
|
+
# @param recurse [TrueClass, FalseClass] recurse to fetch _all_ stacks
|
193
|
+
# @return [Array<Miasma::Models::Orchestration::Stack>]
|
194
|
+
def nested_stacks(recurse=true)
|
195
|
+
resources.all.map do |resource|
|
196
|
+
if(self.api.class.const_get(:RESOURCE_MAPPING).fetch(resource.type, {})[:api] == :orchestration)
|
197
|
+
n_stack = resource.expand
|
198
|
+
if(n_stack)
|
199
|
+
n_stack.data[:logical_id] = resource.name
|
200
|
+
n_stack.data[:parent_stack] = self
|
201
|
+
if(recurse)
|
202
|
+
[n_stack] + n_stack.nested_stacks(recurse)
|
203
|
+
else
|
204
|
+
n_stack
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end.flatten.compact
|
209
|
+
end
|
210
|
+
|
211
|
+
# @return [TrueClass, FalseClass] stack contains nested stacks
|
212
|
+
def nested?
|
213
|
+
!!resources.detect do |resource|
|
214
|
+
self.api.class.const_get(:RESOURCE_MAPPING).fetch(resource.type, {})[:api] == :orchestration
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Return stack policy if available
|
219
|
+
#
|
220
|
+
# @return [Smash, NilClass]
|
221
|
+
def policy
|
222
|
+
if(self.api.provider == :aws) # cause this is the only one
|
223
|
+
begin
|
224
|
+
result = self.api.request(
|
225
|
+
:path => '/',
|
226
|
+
:form => Smash.new(
|
227
|
+
'Action' => 'GetStackPolicy',
|
228
|
+
'StackName' => self.id
|
229
|
+
)
|
230
|
+
)
|
231
|
+
serialized_policy = result.get(:body, 'GetStackPolicyResult', 'StackPolicyBody')
|
232
|
+
MultiJson.load(serialized_policy).to_smash
|
233
|
+
rescue Miasma::Error::ApiError::RequestError => e
|
234
|
+
if(e.response.code == 404)
|
235
|
+
nil
|
236
|
+
else
|
237
|
+
raise
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Detect the nesting style in use by the stack
|
244
|
+
#
|
245
|
+
# @return [Symbol, NilClass] style of nesting (:shallow, :deep)
|
246
|
+
# or `nil` if no nesting detected
|
247
|
+
# @note in shallow nesting style, stack resources will not
|
248
|
+
# contain any direct values for parameters (which is what we
|
249
|
+
# are testing for)
|
250
|
+
def nesting_style
|
251
|
+
if(nested?)
|
252
|
+
self.template['Resources'].find_all do |t_resource|
|
253
|
+
t_resource['Type'] == self.api.class.const_get(:RESOURCE_MAPPING).key(self.class)
|
254
|
+
end.detect do |t_resource|
|
255
|
+
t_resource['Properties'].fetch('Parameters', {}).values.detect do |t_value|
|
256
|
+
!t_value.is_a?(Hash)
|
257
|
+
end
|
258
|
+
end ? :deep : :shallow
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
190
262
|
end
|
191
263
|
end
|
192
264
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'sfn'
|
2
|
+
require 'pathname'
|
2
3
|
|
3
4
|
module Sfn
|
4
5
|
module Utils
|
@@ -87,6 +88,8 @@ module Sfn
|
|
87
88
|
entry = valid[response.to_i]
|
88
89
|
if(entry[:type] == :directory)
|
89
90
|
prompt_for_file(entry[:path], opts)
|
91
|
+
elsif Pathname(entry[:path]).absolute?
|
92
|
+
entry[:path]
|
90
93
|
else
|
91
94
|
"/#{entry[:path]}"
|
92
95
|
end
|
data/lib/sfn/version.rb
CHANGED
data/lib/sfn.rb
CHANGED
data/sfn.gemspec
CHANGED
@@ -11,24 +11,22 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.license = 'Apache-2.0'
|
12
12
|
s.require_path = 'lib'
|
13
13
|
s.add_dependency 'bogo-cli', '~> 0.1.21'
|
14
|
-
s.add_dependency 'miasma', '~> 0.2.
|
14
|
+
s.add_dependency 'miasma', '~> 0.2.27'
|
15
15
|
s.add_dependency 'miasma-aws', '~> 0.1.16'
|
16
16
|
s.add_dependency 'net-ssh'
|
17
|
-
s.add_dependency 'sparkle_formation', '
|
17
|
+
s.add_dependency 'sparkle_formation', '~> 1.0'
|
18
18
|
s.executables << 'sfn'
|
19
19
|
s.files = Dir['{lib,bin}/**/*'] + %w(sfn.gemspec README.md CHANGELOG.md LICENSE)
|
20
20
|
s.post_install_message = <<-EOF
|
21
21
|
|
22
|
-
This
|
23
|
-
|
24
|
-
|
22
|
+
This is an install of the sfn gem from the 1.0 release tree. If you
|
23
|
+
are upgrading from a pre-1.0 version, please review the CHANGELOG and
|
24
|
+
test your environment _before_ continuing on!
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
will be updated.
|
26
|
+
* https://github.com/sparkleformation/sfn/blob/master/CHANGELOG.md
|
27
|
+
|
28
|
+
Happy stacking!
|
30
29
|
|
31
|
-
Thanks and happy stacking!
|
32
30
|
EOF
|
33
31
|
|
34
32
|
end
|