sfn 2.2.0 → 3.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.
@@ -0,0 +1,23 @@
1
+ require 'sfn'
2
+
3
+ module Sfn
4
+ module MonkeyPatch
5
+ module Stack
6
+ # Azure specific monkey patch implementations
7
+ module Azure
8
+
9
+ # @return [Hash] restructured azure template
10
+ # @note Will return #template if name collision encountered within resources
11
+ def sparkleish_template_azure
12
+ new_template = template.to_smash
13
+ resources = new_template.delete(:resources)
14
+ resources.each do |resource|
15
+ new_template.set(:resources, resource.delete(:name), resource)
16
+ end
17
+ resources.size == new_template[:resources].size ? new_template : template
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,129 @@
1
+ require 'sfn'
2
+
3
+ module Sfn
4
+ module MonkeyPatch
5
+ module Stack
6
+ # Google specific monkey patch implementations
7
+ module Google
8
+
9
+ # Helper module to allow nested stack behavior to function as expected
10
+ # internally within sfn
11
+ module PretendStack
12
+
13
+ # disable reload
14
+ def reload
15
+ self
16
+ end
17
+
18
+ # disable template load
19
+ def perform_template_load
20
+ Smash.new
21
+ end
22
+
23
+ # only show resources associated to this stack
24
+ def resources
25
+ collection = Miasma::Models::Orchestration::Stack::Resources.new(self)
26
+ collection.define_singleton_method(:perform_population) do
27
+ valid = stack.sparkleish_template.fetch(:resources, {}).keys
28
+ stack.custom[:resources].find_all{|r| valid.include?(r[:name])}.map do |attrs|
29
+ Miasma::Models::Orchestration::Stack::Resource.new(stack, attrs).valid_state
30
+ end
31
+ end
32
+ collection
33
+ end
34
+
35
+ # Sub-stacks never provide events
36
+ def events
37
+ collection = Miasma::Models::Orchestration::Stack::Events.new(self)
38
+ collection.define_singleton_method(:perform_population){ [] }
39
+ collection
40
+ end
41
+ end
42
+
43
+ # Return all stacks contained within this stack
44
+ #
45
+ # @param recurse [TrueClass, FalseClass] recurse to fetch _all_ stacks
46
+ # @return [Array<Miasma::Models::Orchestration::Stack>]
47
+ def nested_stacks_google(recurse=true)
48
+ my_template = sparkleish_template
49
+ if(my_template[:resources][name])
50
+ my_template = my_template.get(:resources, name, :properties, :stack)
51
+ end
52
+ n_stacks = my_template[:resources].map do |s_name, content|
53
+ if(content[:type] == 'sparkleformation.stack')
54
+ n_stack = self.class.new(api)
55
+ n_stack.extend PretendStack
56
+ n_layout = custom.fetch(:layout, {}).fetch(:resources, []).detect{|r| r[:name] == name}
57
+ n_layout = (n_layout || custom.fetch(:layout, {})).fetch(:resources, []).detect{|r| r[:name] == s_name} || Smash.new
58
+ n_stack.load_data(
59
+ :name => s_name,
60
+ :id => s_name,
61
+ :template => content.get(:properties, :stack),
62
+ :outputs => n_layout.fetch('outputs', []).map{|o_val| Smash.new(:key => o_val[:name], :value => o_val['finalValue'])},
63
+ :custom => {
64
+ :resources => resources.all.map(&:attributes),
65
+ :layout => n_layout
66
+ }
67
+ ).valid_state
68
+ n_stack.data[:logical_id] = s_name
69
+ n_stack.data[:parent_stack] = self
70
+ n_stack
71
+ end
72
+ end.compact
73
+ if(recurse)
74
+ (n_stacks + n_stacks.map(&:nested_stacks)).flatten.compact
75
+ else
76
+ n_stacks
77
+ end
78
+ end
79
+
80
+ # @return [Hash] restructured google template
81
+ def sparkleish_template_google(*args)
82
+ copy_template = template.to_smash
83
+ deref = lambda do |template|
84
+ result = template.to_smash
85
+ (result.delete(:resources) || []).each do |t_resource|
86
+ t_name = t_resource.delete(:name)
87
+ if(t_resource[:type].to_s.end_with?('.jinja'))
88
+ schema = copy_template.fetch(:config, :content, :imports, []).delete("#{t_resource[:type]}.schema")
89
+ schema_content = copy_template.fetch(:imports, []).detect do |s_item|
90
+ s_item[:name] == schema
91
+ end
92
+ if(schema_content)
93
+ t_resource.set(:parameters, schema_content.get(:content, :properties))
94
+ end
95
+ n_template = copy_template.fetch(:imports, []).detect do |s_item|
96
+ s_item[:name] == t_resource[:type]
97
+ end
98
+ if(n_template)
99
+ t_resource[:type] = 'sparkleformation.stack'
100
+ current_properties = t_resource.delete(:properties)
101
+ t_resource.set(:properties, :parameters, current_properties) if current_properties
102
+ t_resource.set(:properties, :stack, deref.call(n_template[:content]))
103
+ end
104
+ end
105
+ result.set(:resources, t_name, t_resource)
106
+ end
107
+ result
108
+ end
109
+ s_template = deref.call(Smash.new(:resources => copy_template.get(:config, :content, :resources)))
110
+ if(s_template.empty?)
111
+ template.to_smash
112
+ else
113
+ layout = custom.fetch(:layout, {}).to_smash
114
+ (layout.delete(:resources) || []).each do |l_resource|
115
+ layout.set(:resources, l_resource.delete(:name), l_resource)
116
+ end
117
+ args.include?(:remove_wrapper) ? s_template.get(:resources, name, :properties, :stack) : s_template
118
+ end
119
+ end
120
+
121
+ # @return [Hash]
122
+ def root_parameters_google
123
+ sparkleish_template.fetch(:resources, name, :properties, :parameters, Smash.new)
124
+ end
125
+
126
+ end
127
+ end
128
+ end
129
+ end
@@ -108,7 +108,8 @@ module Sfn
108
108
 
109
109
  # Resources that will be replaced on metadata init updates
110
110
  REPLACE_ON_CFN_INIT_UPDATE = [
111
- 'AWS::AutoScaling::LaunchConfiguration'
111
+ 'AWS::AutoScaling::LaunchConfiguration',
112
+ 'AWS::EC2::Instance'
112
113
  ]
113
114
 
114
115
  # @return [Smash] initialized translators
@@ -145,7 +146,6 @@ module Sfn
145
146
  :unavailable => Smash.new,
146
147
  :unknown => Smash.new
147
148
  )
148
- # scrub_stack_properties(template)
149
149
  result
150
150
  end
151
151
 
@@ -203,17 +203,21 @@ module Sfn
203
203
  origin_template = dereference_template(
204
204
  "#{stack.data.checksum}_origin",
205
205
  stack.template,
206
- Smash[stack.parameters.map{|k,v| [k, v.to_s]}].merge(get_global_parameters(stack))
206
+ Smash[
207
+ stack.parameters.map do |k,v|
208
+ [k, v.to_s]
209
+ end
210
+ ].merge(get_global_parameters(stack))
207
211
  )
208
212
 
209
- t_key = "#{stack.data.checksum}_#{stack.data.fetch(:logical_id, stack.name)}"
210
- run_stack_diff(stack, t_key, plan_results, origin_template, new_template, new_parameters)
213
+ translator_key = "#{stack.data.checksum}_#{stack.data.fetch(:logical_id, stack.name)}"
214
+ run_stack_diff(stack, translator_key, plan_results, origin_template, new_template, new_parameters)
211
215
 
212
216
  new_checksum = nil
213
217
  current_checksum = false
214
218
  until(new_checksum == current_checksum)
215
219
  current_checksum = plan_results.checksum
216
- run_stack_diff(stack, t_key, plan_results, origin_template, new_template, new_parameters)
220
+ run_stack_diff(stack, translator_key, plan_results, origin_template, new_template, new_parameters)
217
221
  new_checksum = plan_results.checksum
218
222
  end
219
223
  scrub_plan(plan_results)
@@ -249,6 +253,7 @@ module Sfn
249
253
  # Run the stack diff and populate the result set
250
254
  #
251
255
  # @param stack [Miasma::Models::Orchestration::Stack] existing stack
256
+ # @param t_key [String] translator key
252
257
  # @param plan_result [Smash] plan data to populate
253
258
  # @param origin_template [Smash] template of existing stack
254
259
  # @param new_template [Smash] template to replace existing
@@ -256,7 +261,6 @@ module Sfn
256
261
  # @return [NilClass]
257
262
  def run_stack_diff(stack, t_key, plan_results, origin_template, new_template, new_parameters)
258
263
  translator = translator_for(t_key)
259
-
260
264
  new_parameters = new_parameters.dup
261
265
  if(stack.parameters)
262
266
  stack.parameters.each do |k,v|
@@ -266,50 +270,12 @@ module Sfn
266
270
  end
267
271
  end
268
272
  end
269
-
270
273
  new_parameters.merge!(get_global_parameters(stack))
271
-
272
274
  new_template_hash = new_template.to_smash
273
-
274
- o_nested_stacks = origin_template.fetch('Resources', {}).find_all do |s_name, s_val|
275
- is_stack?(s_val['Type'])
276
- end.map(&:first)
277
- n_nested_stacks = (new_template_hash['Resources'] || {}).find_all do |s_name, s_val|
278
- is_stack?(s_val['Type'])
279
- end.map(&:first)
280
- [o_nested_stacks + n_nested_stacks].flatten.compact.uniq.each do |n_name|
281
- o_stack = stack.nested_stacks(false).detect{|s| s.data[:logical_id] == n_name}
282
- n_exists = is_stack?(new_template_hash.get('Resources', n_name, 'Type'))
283
- n_template = new_template_hash.get('Resources', n_name, 'Properties', 'Stack')
284
- n_parameters = new_template_hash.fetch('Resources', n_name, 'Properties', 'Parameters', Smash.new)
285
- n_type = new_template_hash.fetch('Resources', n_name, 'Type',
286
- origin_template.get('Resources', n_name, 'Type')
287
- )
288
- resource = Smash.new(
289
- :name => n_name,
290
- :type => n_type,
291
- :properties => []
292
- )
293
- if(o_stack && n_template)
294
- n_parameters.keys.each do |n_key|
295
- n_parameters[n_key] = translator.dereference(n_parameters[n_key])
296
- end
297
- n_results = plan_stack(o_stack, n_template, n_parameters)
298
- unless(n_results[:outputs].empty?)
299
- n_results[:outputs].keys.each do |n_output|
300
- translator.flag_ref("#{n_name}_Outputs.#{n_output}")
301
- end
302
- end
303
- plan_results[:stacks][n_name] = n_results
304
- elsif(o_stack && (!n_template && !n_exists))
305
- plan_results[:removed][n_name] = resource
306
- elsif(n_template && !o_stack)
307
- plan_results[:added][n_name] = resource
308
- end
309
- end
310
-
311
275
  scrub_stack_properties(new_template_hash)
312
276
 
277
+ plan_nested_stacks(stack, translator, origin_template, new_template_hash, plan_results)
278
+
313
279
  update_template = dereference_template(
314
280
  t_key, new_template_hash, new_parameters,
315
281
  plan_results[:replace].keys + plan_results[:unavailable].keys
@@ -320,23 +286,72 @@ module Sfn
320
286
  end.each do |a_path, diff_items|
321
287
  register_diff(
322
288
  plan_results, a_path, diff_items, translator_for(t_key),
323
- :origin => origin_template,
324
- :update => update_template
289
+ Smash.new(
290
+ :origin => origin_template,
291
+ :update => update_template
292
+ )
325
293
  )
326
294
  end
327
295
  nil
328
296
  end
329
297
 
330
- # Register a diff item into the results set
298
+ # Extract nested stacks and generate plans
331
299
  #
332
- # @param results [Hash]
333
- # @param path [String]
334
- # @param diff [Array]
335
- # @param templates [Hash]
336
- # @option :templates [Hash] :origin
337
- # @option :templates [Hash] :update
338
- def register_diff(results, path, diff, translator, templates)
339
- diff_info = Smash.new.tap do |di|
300
+ # @param stack [Miasma::Orchestration::Models::Stack]
301
+ # @param translator [Translator]
302
+ # @param origin_template [Smash]
303
+ # @param new_template_hash [Smash]
304
+ # @param plan_results [Smash]
305
+ # @return [NilClass]
306
+ def plan_nested_stacks(stack, translator, origin_template, new_template_hash, plan_results)
307
+ origin_stacks = origin_template.fetch('Resources', {}).find_all do |s_name, s_val|
308
+ is_stack?(s_val['Type'])
309
+ end.map(&:first)
310
+ new_stacks = (new_template_hash['Resources'] || {}).find_all do |s_name, s_val|
311
+ is_stack?(s_val['Type'])
312
+ end.map(&:first)
313
+ [origin_stacks + new_stacks].flatten.compact.uniq.each do |stack_name|
314
+ original_stack = stack.nested_stacks(false).detect do |stk|
315
+ stk.data[:logical_id] == stack_name
316
+ end
317
+ new_stack_exists = is_stack?(new_template_hash.get('Resources', stack_name, 'Type'))
318
+ new_stack_template = new_template_hash.get('Resources', stack_name, 'Properties', 'Stack')
319
+ new_stack_parameters = new_stack_template.fetch('Parameters', Smash.new)
320
+ new_stack_type = new_template_hash.fetch('Resources', stack_name, 'Type',
321
+ origin_template.get('Resources', stack_name, 'Type')
322
+ )
323
+ resource = Smash.new(
324
+ :name => stack_name,
325
+ :type => new_stack_type,
326
+ :properties => []
327
+ )
328
+ if(original_stack && new_stack_template)
329
+ new_stack_parameters = Smash[
330
+ new_stack_parameters.map do |new_param_key, new_param_value|
331
+ [new_param_key, translator.dereference(new_param_value)]
332
+ end
333
+ ]
334
+ result = plan_stack(original_stack, new_stack_template, new_stack_parameters)
335
+ result[:outputs].keys.each do |modified_output|
336
+ translator.flag_ref("#{stack_name}_Outputs.#{modified_output}")
337
+ end
338
+ plan_results[:stacks][stack_name] = result
339
+ elsif(original_stack && (!new_stack_template && !new_stack_exists))
340
+ plan_results[:removed][stack_name] = resource
341
+ elsif(new_stack_template && !original_stack)
342
+ plan_results[:added][stack_name] = resource
343
+ end
344
+ end
345
+ nil
346
+ end
347
+
348
+ # Initialize the diff result hash
349
+ #
350
+ # @param diff [Array] Hashdiff result entry
351
+ # @param path [String] modification path within structure
352
+ # @return [Smash]
353
+ def diff_init(diff, path)
354
+ Smash.new.tap do |di|
340
355
  if(diff.size > 1)
341
356
  updated = diff.detect{|x| x.first == '+'}
342
357
  original = diff.detect{|x| x.first == '-'}
@@ -353,12 +368,24 @@ module Sfn
353
368
  end
354
369
  end
355
370
  end
371
+ end
372
+
373
+ # Register a diff item into the results set
374
+ #
375
+ # @param results [Hash]
376
+ # @param path [String]
377
+ # @param diff [Array]
378
+ # @param templates [Smash]
379
+ # @option :templates [Smash] :origin
380
+ # @option :templates [Smash] :update
381
+ def register_diff(results, path, diff, translator, templates)
382
+ diff_info = diff_init(diff, path)
356
383
  if(path.start_with?('Resources'))
357
384
  p_path = path.split('.')
358
385
  if(p_path.size == 2)
359
386
  diff = diff.first
360
387
  key = diff.first == '+' ? :added : :removed
361
- type = (key == :added ? templates[:update] : templates[:origin])['Resources'][p_path.last]['Type']
388
+ type = (key == :added ? templates[:update] : templates[:origin]).get('Resources', p_path.last, 'Type')
362
389
  results[key][p_path.last] = Smash.new(
363
390
  :name => p_path.last,
364
391
  :type => type,
@@ -375,9 +402,7 @@ module Sfn
375
402
  else
376
403
  property_name = p_path[3].to_s.sub(/\[\d+\]$/, '')
377
404
  end
378
- type = templates[:origin]['Resources'][resource_name]['Type']
379
- info = SfnAws.registry.fetch(type, {}).to_smash
380
- effect = info.fetch(:full_properties, property_name, :update_causes, :unknown).to_sym
405
+ type = templates.get(:origin, 'Resources', resource_name, 'Type')
381
406
  resource = Smash.new(
382
407
  :name => resource_name,
383
408
  :type => type,
@@ -386,16 +411,30 @@ module Sfn
386
411
  diff_info.merge(:property_name => property_name)
387
412
  ]
388
413
  )
389
- case effect
390
- when :replacement
391
- set_resource(:replace, results, resource_name, resource)
392
- when :interrupt
393
- set_resource(:interrupt, results, resource_name, resource)
394
- when :unavailable
395
- set_resource(:unavailable, results, resource_name, resource)
396
- when :none
397
- # \o/
398
- else
414
+ begin
415
+ r_info = SparkleFormation::Resources::Aws.resource_lookup(type)
416
+ r_property = r_info.property(property_name)
417
+ if(r_property)
418
+ effect = r_property.update_causes(
419
+ templates.get(:update, 'Resources', resource_name),
420
+ templates.get(:origin, 'Resources', resource_name)
421
+ )
422
+ else
423
+ raise KeyError.new 'Unknown property'
424
+ end
425
+ case effect.to_sym
426
+ when :replacement
427
+ set_resource(:replace, results, resource_name, resource)
428
+ when :interrupt
429
+ set_resource(:interrupt, results, resource_name, resource)
430
+ when :unavailable
431
+ set_resource(:unavailable, results, resource_name, resource)
432
+ when :none
433
+ # \o/
434
+ else
435
+ set_resource(:unknown, results, resource_name, resource)
436
+ end
437
+ rescue KeyError
399
438
  set_resource(:unknown, results, resource_name, resource)
400
439
  end
401
440
  elsif(p_path.include?('AWS::CloudFormation::Init'))
@@ -13,6 +13,7 @@ module Sfn
13
13
  def _to_json(thing)
14
14
  MultiJson.dump(thing)
15
15
  end
16
+ alias_method :dump_json, :_to_json
16
17
 
17
18
  # Load JSON data
18
19
  #
@@ -21,6 +22,7 @@ module Sfn
21
22
  def _from_json(thing)
22
23
  MultiJson.load(thing)
23
24
  end
25
+ alias_method :load_json, :_from_json
24
26
 
25
27
  # Format object into pretty JSON
26
28
  #
@@ -30,6 +32,7 @@ module Sfn
30
32
  thing = _from_json(thing) if thing.is_a?(String)
31
33
  MultiJson.dump(thing, :pretty => true)
32
34
  end
35
+ alias_method :format_json, :_format_json
33
36
 
34
37
  end
35
38
 
@@ -3,35 +3,32 @@ require 'sfn'
3
3
  module Sfn
4
4
  module Utils
5
5
  # Helper for scrubbing stack parameters
6
- class StackParameterScrubber
6
+ module StackParameterScrubber
7
7
 
8
- class << self
8
+ # Validate attributes within Parameter blocks
9
+ ALLOWED_PARAMETER_ATTRIBUTES = %w(
10
+ Type Default NoEcho AllowedValues AllowedPattern
11
+ MaxLength MinLength MaxValue MinValue Description
12
+ ConstraintDescription
13
+ )
9
14
 
10
- # Validate attributes within Parameter blocks
11
- ALLOWED_PARAMETER_ATTRIBUTES = %w(
12
- Type Default NoEcho AllowedValues AllowedPattern
13
- MaxLength MinLength MaxValue MinValue Description
14
- ConstraintDescription
15
- )
16
-
17
- # Clean the parameters of the template
18
- #
19
- # @param template [Hash]
20
- # @return [Hash] template
21
- def scrub!(template)
22
- parameters = template['Parameters']
23
- if(parameters)
24
- parameters.each do |name, options|
25
- options.delete_if do |attribute, value|
26
- !ALLOWED_PARAMETER_ATTRIBUTES.include?(attribute)
27
- end
15
+ # Clean the parameters of the template
16
+ #
17
+ # @param template [Hash]
18
+ # @return [Hash] template
19
+ def parameter_scrub!(template)
20
+ parameters = template['Parameters']
21
+ if(parameters)
22
+ parameters.each do |name, options|
23
+ options.delete_if do |attribute, value|
24
+ !ALLOWED_PARAMETER_ATTRIBUTES.include?(attribute)
28
25
  end
29
- template['Parameters'] = parameters
30
26
  end
31
- template
27
+ template['Parameters'] = parameters
32
28
  end
33
-
29
+ template
34
30
  end
31
+
35
32
  end
36
33
  end
37
34
  end