sparkle_formation 1.1.14 → 1.2.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.
@@ -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