sfn 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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