sparkle_formation 1.1.14 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,276 +5,29 @@ class SparkleFormation
5
5
  # Provides template helper methods
6
6
  module SparkleAttribute
7
7
 
8
- # Fn::Join generator
9
- #
10
- # @param args [Object]
11
- # @return [Hash]
12
- def _cf_join(*args)
13
- options = args.detect{|i| i.is_a?(Hash) && i[:options]} || {:options => {}}
14
- args.delete(options)
15
- unless(args.size == 1)
16
- args = [args]
17
- end
18
- {'Fn::Join' => [options[:options][:delimiter] || '', *args]}
19
- end
20
- alias_method :join!, :_cf_join
21
-
22
- # Ref generator
23
- #
24
- # @param thing [String, Symbol] reference name
25
- # @return [Hash]
26
- # @note Symbol value will force key processing
27
- def _cf_ref(thing)
28
- thing = _process_key(thing, :force) if thing.is_a?(Symbol)
29
- {'Ref' => thing}
30
- end
31
- alias_method :_ref, :_cf_ref
32
- alias_method :ref!, :_cf_ref
8
+ autoload :Aws, 'sparkle_formation/sparkle_attribute/aws'
33
9
 
34
- # Fn::FindInMap generator
10
+ # Return current resource name
35
11
  #
36
- # @param thing [String, Symbol] thing to find
37
- # @param key [String, Symbol] thing to search
38
- # @param suffix [Object] additional args
39
- # @return [Hash]
40
- def _cf_map(thing, key, *suffix)
41
- suffix = suffix.map do |item|
42
- if(item.is_a?(Symbol))
43
- _process_key(item, :force)
12
+ # @return [String]
13
+ def _resource_name
14
+ result = nil
15
+ if(_parent)
16
+ if(_parent._parent == _root)
17
+ result = _parent._data.detect do |r_name, r_value|
18
+ r_value == self
19
+ end
20
+ result = result.first if result
44
21
  else
45
- item
22
+ result = _parent._resource_name
46
23
  end
47
24
  end
48
- thing = _process_key(thing, :force) if thing.is_a?(Symbol)
49
- if(key.is_a?(Symbol))
50
- key = ref!(key)
51
- end
52
- {'Fn::FindInMap' => [thing, key, *suffix]}
53
- end
54
- alias_method :_cf_find_in_map, :_cf_map
55
- alias_method :find_in_map!, :_cf_map
56
- alias_method :map!, :_cf_map
57
-
58
- # Fn::GetAtt generator
59
- #
60
- # @param [Object] pass through arguments
61
- # @return [Hash]
62
- def _cf_attr(*args)
63
- args = args.map do |thing|
64
- if(thing.is_a?(Symbol))
65
- _process_key(thing, :force)
66
- else
67
- thing
68
- end
69
-
70
- end
71
- {'Fn::GetAtt' => args}
72
- end
73
- alias_method :_cf_get_att, :_cf_attr
74
- alias_method :get_att!, :_cf_attr
75
- alias_method :attr!, :_cf_attr
76
-
77
- # Fn::Base64 generator
78
- #
79
- # @param arg [Object] pass through
80
- # @return [Hash]
81
- def _cf_base64(arg)
82
- {'Fn::Base64' => arg}
83
- end
84
- alias_method :base64!, :_cf_base64
85
-
86
- # Fn::GetAZs generator
87
- #
88
- # @param region [String, Symbol] String will pass through. Symbol will be converted to ref
89
- # @return [Hash]
90
- def _cf_get_azs(region=nil)
91
- region = case region
92
- when Symbol
93
- _cf_ref(region)
94
- when NilClass
95
- ''
96
- else
97
- region
98
- end
99
- {'Fn::GetAZs' => region}
100
- end
101
- alias_method :get_azs!, :_cf_get_azs
102
- alias_method :azs!, :_cf_get_azs
103
-
104
- # Fn::Select generator
105
- #
106
- # @param index [String, Symbol, Integer] Symbol will be converted to ref
107
- # @param item [Object, Symbol] Symbol will be converted to ref
108
- # @return [Hash]
109
- def _cf_select(index, item)
110
- index = index.is_a?(Symbol) ? _cf_ref(index) : index
111
- item = _cf_ref(item) if item.is_a?(Symbol)
112
- {'Fn::Select' => [index, item]}
113
- end
114
- alias_method :select!, :_cf_select
115
-
116
- # Condition generator
117
- #
118
- # @param name [String, Symbol] symbol will be processed
119
- # @return [Hash]
120
- def _condition(name)
121
- {'Condition' => name.is_a?(Symbol) ? _process_key(name) : name}
122
- end
123
- alias_method :condition!, :_condition
124
-
125
- # Condition setter
126
- #
127
- # @param name [String, Symbol] condition name
128
- # @return [SparkleStruct]
129
- # @note this is used to set a {"Condition" => "Name"} into the
130
- # current context, generally the top level of a resource
131
- def _on_condition(name)
132
- _set(*_condition(name).to_a.flatten)
133
- end
134
- alias_method :on_condition!, :_on_condition
135
-
136
- # Fn::If generator
137
- #
138
- # @param cond [String, Symbol] symbol will be case processed
139
- # @param true_value [Object]
140
- # @param false_value [Object]
141
- # @return [Hash]
142
- def _if(cond, true_value, false_value)
143
- cond = cond.is_a?(Symbol) ? _process_key(cond) : cond
144
- {'Fn::If' => _array(cond, true_value, false_value)}
145
- end
146
- alias_method :if!, :_if
147
-
148
- # Fn::And generator
149
- #
150
- # @param args [Object]
151
- # @return [Hash]
152
- # @note symbols will be processed and set as condition. strings
153
- # will be set as condition directly. procs will be evaluated
154
- def _and(*args)
155
- {
156
- 'Fn::And' => _array(
157
- *args.map{|v|
158
- if(v.is_a?(Symbol) || v.is_a?(String))
159
- _condition(v)
160
- else
161
- v
162
- end
163
- }
164
- )
165
- }
166
- end
167
- alias_method :and!, :_and
168
-
169
- # Fn::Equals generator
170
- #
171
- # @param v1 [Object]
172
- # @param v2 [Object]
173
- # @return [Hash]
174
- def _equals(v1, v2)
175
- {'Fn::Equals' => _array(v1, v2)}
176
- end
177
- alias_method :equals!, :_equals
178
-
179
- # Fn::Not generator
180
- #
181
- # @param arg [Object]
182
- # @return [Hash]
183
- def _not(arg)
184
- if(arg.is_a?(String) || arg.is_a?(Symbol))
185
- arg = _condition(arg)
186
- else
187
- arg = _array(arg).first
25
+ unless(result)
26
+ ::Kernel.raise NameError.new 'Failed to determine current resource name! (Check call location)'
188
27
  end
189
- {'Fn::Not' => [arg]}
190
- end
191
- alias_method :not!, :_not
192
-
193
- # Fn::Or generator
194
- #
195
- # @param v1 [Object]
196
- # @param v2 [Object]
197
- # @return [Hash]
198
- def _or(*args)
199
- {
200
- 'Fn::Or' => _array(
201
- *args.map{|v|
202
- if(v.is_a?(Symbol) || v.is_a?(String))
203
- _condition(v)
204
- else
205
- v
206
- end
207
- }
208
- )
209
- }
210
- end
211
- alias_method :or!, :_or
212
-
213
- # No value generator
214
- #
215
- # @return [String]
216
- def _no_value
217
- _ref('AWS::NoValue')
218
- end
219
- alias_method :no_value!, :_no_value
220
-
221
- # Region generator
222
- #
223
- # @return [Hash]
224
- def _region
225
- _ref('AWS::Region')
226
- end
227
- alias_method :region!, :_region
228
-
229
- # Notification ARNs generator
230
- #
231
- # @return [Hash]
232
- def _notification_arns
233
- _ref('AWS::NotificationARNs')
234
- end
235
- alias_method :notification_arns!, :_notification_arns
236
-
237
- # Account ID generator
238
- #
239
- # @return [Hash]
240
- def _account_id
241
- _ref('AWS::AccountId')
242
- end
243
- alias_method :account_id!, :_account_id
244
-
245
- # Stack ID generator
246
- #
247
- # @return [Hash]
248
- def _stack_id
249
- _ref('AWS::StackId')
28
+ result
250
29
  end
251
- alias_method :stack_id!, :_stack_id
252
-
253
- # Stack name generator
254
- #
255
- # @return [Hash]
256
- def _stack_name
257
- _ref('AWS::StackName')
258
- end
259
- alias_method :stack_name!, :_stack_name
260
-
261
- # Resource dependency generator
262
- #
263
- # @param [Symbol, String, Array<Symbol, String>] resource names
264
- # @return [Array<String>]
265
- def _depends_on(*args)
266
- _set('DependsOn', [args].flatten.compact.map{|s| _process_key(s)})
267
- end
268
- alias_method :depends_on!, :_depends_on
269
-
270
- # Reference output value from nested stack
271
- #
272
- # @param stack_name [String, Symbol] logical resource name of stack
273
- # @apram output_name [String, Symbol] stack output name
274
- def _stack_output(stack_name, output_name)
275
- _cf_attr(_process_key(stack_name), "Outputs.#{_process_key(output_name)}")
276
- end
277
- alias_method :stack_output!, :_stack_output
30
+ alias_method :resource_name!, :_resource_name
278
31
 
279
32
  # Execute system command
280
33
  #
@@ -300,39 +53,13 @@ class SparkleFormation
300
53
  end
301
54
  alias_method :raise!, :_raise
302
55
 
303
- # @return [TrueClass, FalseClass] resource can be tagged
304
- def taggable?
305
- if(defined?(SfnAws))
306
- if(self[:type])
307
- resource = SfnAws.lookup(self[:type].gsub('::', '_').downcase)
308
- resource && resource[:properties].include?('Tags')
309
- else
310
- if(self._parent)
311
- self._parent.taggable?
312
- end
313
- end
314
- end
315
- end
316
-
317
- # @return [TrueClass, FalseClass]
318
- def rhel?
319
- !!@platform[:rhel]
320
- end
321
-
322
- # @return [TrueClass, FalseClass]
323
- def debian?
324
- !!@platform[:debian]
325
- end
326
-
327
- # Set the destination platform
328
- #
329
- # @param plat [String, Symbol] one of :rhel or :debian
330
- # @return [TrueClass]
331
- def _platform=(plat)
332
- @platform || __hashish
333
- @platform.clear
334
- @platform[plat.to_sym] = true
56
+ # Lookup a method definition on self
57
+ # usually used as `puts! method!(:foo).source_location`
58
+ # @return [Method]
59
+ def _method(*args)
60
+ ::Kernel.instance_method(:method).bind(self).call(*args)
335
61
  end
62
+ alias_method :method!, :_method
336
63
 
337
64
  # Dynamic insertion helper method
338
65
  #
@@ -361,5 +88,27 @@ class SparkleFormation
361
88
  SparkleFormation.nest(template, self, *args, &block)
362
89
  end
363
90
 
91
+ # TODO: Deprecate or re-imagine
92
+
93
+ # @return [TrueClass, FalseClass]
94
+ def rhel?
95
+ !!@platform[:rhel]
96
+ end
97
+
98
+ # @return [TrueClass, FalseClass]
99
+ def debian?
100
+ !!@platform[:debian]
101
+ end
102
+
103
+ # Set the destination platform
104
+ #
105
+ # @param plat [String, Symbol] one of :rhel or :debian
106
+ # @return [TrueClass]
107
+ def _platform=(plat)
108
+ @platform || __hashish
109
+ @platform.clear
110
+ @platform[plat.to_sym] = true
111
+ end
112
+
364
113
  end
365
114
  end
@@ -22,6 +22,8 @@ class SparkleFormation
22
22
 
23
23
  class << self
24
24
 
25
+ include SparkleFormation::Utils::TypeCheckers
26
+
25
27
  # @return [Hashish] loaded dynamics
26
28
  def dynamics
27
29
  @dynamics ||= SparkleStruct.hashish.new
@@ -96,7 +98,7 @@ class SparkleFormation
96
98
  container = Sparkle.new(:root => spath)
97
99
  path = container.get(:template, path)[:path]
98
100
  end
99
- formation = self.instance_eval(IO.read(path), path, 1)
101
+ formation = instance_eval(IO.read(path), path, 1)
100
102
  if(args.delete(:sparkle))
101
103
  formation
102
104
  else
@@ -124,7 +126,7 @@ class SparkleFormation
124
126
  # @param path [String] path to component
125
127
  # @return [SparkleStruct] resulting struct
126
128
  def load_component(path)
127
- self.instance_eval(IO.read(path), path, 1)
129
+ instance_eval(IO.read(path), path, 1)
128
130
  @_struct
129
131
  end
130
132
 
@@ -137,7 +139,7 @@ class SparkleFormation
137
139
  Dir.glob(File.join(directory, '*.rb')).each do |dyn|
138
140
  dyn = File.expand_path(dyn)
139
141
  next if @loaded_dynamics.include?(dyn)
140
- self.instance_eval(IO.read(dyn), dyn, 1)
142
+ instance_eval(IO.read(dyn), dyn, 1)
141
143
  @loaded_dynamics << dyn
142
144
  end
143
145
  @loaded_dynamics.uniq!
@@ -194,7 +196,7 @@ class SparkleFormation
194
196
  # @param args [Object] parameters for dynamic
195
197
  # @return [SparkleStruct]
196
198
  def registry(registry_name, struct, *args)
197
- result = false
199
+ __t_stringish(registry_name)
198
200
  reg = struct._self.sparkle.get(:registry, registry_name)
199
201
  struct.instance_exec(*args, &reg[:block])
200
202
  end
@@ -206,6 +208,7 @@ class SparkleFormation
206
208
  # @param args [Object] parameters for dynamic
207
209
  # @return [SparkleStruct]
208
210
  def insert(dynamic_name, struct, *args, &block)
211
+ __t_stringish(dynamic_name)
209
212
  result = false
210
213
  begin
211
214
  dyn = struct._self.sparkle.get(:dynamic, dynamic_name)
@@ -219,7 +222,13 @@ class SparkleFormation
219
222
  result = builtin_insert(dynamic_name, struct, *args, &block)
220
223
  end
221
224
  unless(result)
222
- raise "Failed to locate requested dynamic block for insertion: #{dynamic_name} (valid: #{struct._self.sparkle.dynamics.keys.sort.join(', ')})"
225
+ message = "Failed to locate requested dynamic block for insertion: #{dynamic_name} " \
226
+ "(valid: #{struct._self.sparkle.dynamics.keys.sort.join(', ')})"
227
+ if(struct._self.provider_resources.registry.keys.size > 1)
228
+ t_name = struct._self.provider_resources.registry.keys.first
229
+ message << "\nBuiltin dynamics pattern `#{t_name}` -> `:#{Bogo::Utility.snake(t_name.gsub('::', '_'))}`"
230
+ end
231
+ raise message
223
232
  end
224
233
  result
225
234
  end
@@ -239,6 +248,9 @@ class SparkleFormation
239
248
  else
240
249
  options = {}
241
250
  end
251
+ [template, *args].compact.each do |item|
252
+ __t_stringish(item)
253
+ end
242
254
  to_nest = struct._self.sparkle.get(:template, template)
243
255
  resource_name = template.to_s.gsub('__', '_')
244
256
  unless(args.empty?)
@@ -247,7 +259,7 @@ class SparkleFormation
247
259
  args.map{|a| Bogo::Utility.snake(a)}.join('_')
248
260
  ].flatten.compact.join('_').to_sym
249
261
  end
250
- nested_template = self.compile(to_nest[:path], :sparkle)
262
+ nested_template = compile(to_nest[:path], :sparkle)
251
263
  nested_template.parent = struct._self
252
264
  nested_template.name = Bogo::Utility.camel(resource_name)
253
265
  if(options[:parameters])
@@ -270,17 +282,17 @@ class SparkleFormation
270
282
  # @param args [Object] parameters for dynamic
271
283
  # @return [SparkleStruct]
272
284
  def builtin_insert(dynamic_name, struct, *args, &block)
273
- if(defined?(SfnAws) && lookup_key = SfnAws.registry_key(dynamic_name))
285
+ if(struct._self.provider_resources && lookup_key = struct._self.provider_resources.registry_key(dynamic_name))
274
286
  _name, _config = *args
275
287
  _config ||= {}
276
- return unless _name
288
+ __t_stringish(_name)
289
+ __t_hashish(_config)
277
290
  resource_name = "#{_name}_#{_config.delete(:resource_name_suffix) || dynamic_name}".to_sym
278
- struct.resources.set!(resource_name)
279
- new_resource = struct.resources[resource_name]
291
+ new_resource = struct.resources.set!(resource_name)
280
292
  new_resource.type lookup_key
281
293
  properties = new_resource.properties
282
294
  config_keys = _config.keys.zip(_config.keys.map{|k| snake(k).to_s.tr('_', '')})
283
- SfnAws.resource(dynamic_name, :properties).each do |prop_name|
295
+ struct._self.provider_resources.resource(dynamic_name, :properties).each do |prop_name|
284
296
  key = (config_keys.detect{|k| k.last == snake(prop_name).to_s.tr('_', '')} || []).first
285
297
  value = _config[key] if key
286
298
  if(value)
@@ -336,6 +348,10 @@ class SparkleFormation
336
348
  attr_reader :stack_resource_types
337
349
  # @return [Hash] state hash for compile time parameters
338
350
  attr_accessor :compile_state
351
+ # @return [Symbol] target provider
352
+ attr_reader :provider
353
+ # @return [Class] Provider resources
354
+ attr_reader :provider_resources
339
355
 
340
356
  # Create new instance
341
357
  #
@@ -366,7 +382,6 @@ class SparkleFormation
366
382
  )
367
383
  unless(options[:disable_aws_builtins])
368
384
  require 'sparkle_formation/aws'
369
- SfnAws.load!
370
385
  end
371
386
  @parameters = set_generation_parameters!(
372
387
  options.fetch(:compile_time_parameters,
@@ -381,12 +396,27 @@ class SparkleFormation
381
396
  @load_order = []
382
397
  @overrides = []
383
398
  @parent = options[:parent]
399
+ @provider = options.fetch(:provider, @parent ? @parent.provider : :aws)
384
400
  if(block)
385
401
  load_block(block)
386
402
  end
387
403
  @compiled = nil
388
404
  end
389
405
 
406
+ # Set remote API target for template to allow loading of
407
+ # provider specific helpers and data if available. Setting
408
+ # to a false-y value will disable helpers loading
409
+ #
410
+ # @param val [String, Symbol, NilClass, FalseClass] remote provider
411
+ # @return [Symbol, NilClass]
412
+ def provider=(val)
413
+ if(val)
414
+ @provider = Bogo::Utility.snake(val).to_sym
415
+ else
416
+ @provider = nil
417
+ end
418
+ end
419
+
390
420
  # Check if type is a registered stack type
391
421
  #
392
422
  # @param type [String]
@@ -508,14 +538,26 @@ class SparkleFormation
508
538
  # @option args [Hash] :state local state parameters
509
539
  # @return [SparkleStruct]
510
540
  def compile(args={})
511
- if(args.has_key?(:state))
541
+ if(args.key?(:state))
512
542
  @compile_state = args[:state]
513
543
  unmemoize(:compile)
514
544
  end
515
545
  memoize(:compile) do
516
546
  set_compile_time_parameters!
517
- compiled = SparkleStruct.new
547
+ if(provider && SparkleAttribute.const_defined?(camel(provider)))
548
+ const = SparkleAttribute.const_get(camel(provider))
549
+ struct_class = Class.new(SparkleStruct)
550
+ struct_class.include(const)
551
+ else
552
+ struct_class = SparkleStruct
553
+ end
554
+ if(Resources.const_defined?(camel(provider)))
555
+ @provider_resources = Resources.const_get(camel(provider))
556
+ provider_resources.load!
557
+ end
558
+ compiled = struct_class.new
518
559
  compiled._set_self(self)
560
+ compiled._struct_class = struct_class
519
561
  if(compile_state)
520
562
  compiled.set_state!(compile_state)
521
563
  end
@@ -562,7 +604,7 @@ class SparkleFormation
562
604
  # @return [TrueClass, FalseClass] includes nested stacks
563
605
  def nested?(stack_hash=nil)
564
606
  stack_hash = compile.dump! unless stack_hash
565
- !!stack_hash.fetch('Resources', {}).detect do |r_name, resource|
607
+ !!stack_hash.fetch('Resources', {}).detect do |_r_name, resource|
566
608
  stack_resource_type?(resource['Type'])
567
609
  end
568
610
  end
@@ -570,7 +612,7 @@ class SparkleFormation
570
612
  # @return [TrueClass, FalseClass] includes _only_ nested stacks
571
613
  def isolated_nests?(stack_hash=nil)
572
614
  stack_hash = compile.dump! unless stack_hash
573
- stack_hash.fetch('Resources', {}).all? do |name, resource|
615
+ stack_hash.fetch('Resources', {}).all? do |_name, resource|
574
616
  stack_resource_type?(resource['Type'])
575
617
  end
576
618
  end
@@ -578,8 +620,8 @@ class SparkleFormation
578
620
  # @return [TrueClass, FalseClass] policies defined
579
621
  def includes_policies?(stack_hash=nil)
580
622
  stack_hash = compile.dump! unless stack_hash
581
- stack_hash.fetch('Resources', {}).any? do |name, resource|
582
- resource.has_key?('Policy')
623
+ stack_hash.fetch('Resources', {}).any? do |_name, resource|
624
+ resource.key?('Policy')
583
625
  end
584
626
  end
585
627
 
@@ -678,7 +720,11 @@ class SparkleFormation
678
720
  drip_path = root_path - outputs[output_name].root_path
679
721
  bubble_path.each_slice(2) do |base_sparkle, ref_sparkle|
680
722
  next unless ref_sparkle
681
- base_sparkle.compile.outputs.set!(output_name).set!(:value, base_sparkle.compile.attr!(ref_sparkle.name, "Outputs.#{output_name}"))
723
+ base_sparkle.compile.outputs.set!(output_name).set!(
724
+ :value, base_sparkle.compile.attr!(
725
+ ref_sparkle.name, "Outputs.#{output_name}"
726
+ )
727
+ )
682
728
  end
683
729
  if(bubble_path.empty?)
684
730
  if(drip_path.size == 1)
@@ -699,7 +745,7 @@ class SparkleFormation
699
745
  drip_path.each_slice(2) do |base_sparkle, ref_sparkle|
700
746
  next unless ref_sparkle
701
747
  base_sparkle.compile.resources[ref_sparkle.name].properties.parameters.set!(output_name, result)
702
- ref_sparkle.compile.parameters.set!(output_name){ type 'String' } # TODO <<<<------ type check and prop
748
+ ref_sparkle.compile.parameters.set!(output_name){ type 'String' } # TODO: <<<<------ type check and prop
703
749
  result = compile.ref!(output_name)
704
750
  end
705
751
  end
@@ -741,7 +787,7 @@ class SparkleFormation
741
787
  def apply_shallow_nesting(*args, &block)
742
788
  parameters = compile[:parameters] ? compile[:parameters]._dump : {}
743
789
  output_map = {}
744
- nested_stacks(:with_resource, :with_name).each do |stack, stack_resource, stack_name|
790
+ nested_stacks(:with_resource, :with_name).each do |_stack, stack_resource, stack_name|
745
791
  remap_nested_parameters(compile, parameters, stack_name, stack_resource, output_map)
746
792
  end
747
793
  extract_templates(&block)
@@ -835,7 +881,7 @@ class SparkleFormation
835
881
 
836
882
  # @return [Hash] dumped hash
837
883
  def dump
838
- MultiJson.load(self.to_json)
884
+ MultiJson.load(to_json)
839
885
  end
840
886
 
841
887
  # @return [String] dumped hash JSON