sparkle_formation 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ require 'sparkle_formation'
2
+
3
+ class SparkleFormation
4
+
5
+ # Provides template helper methods
6
+ module SparkleAttribute
7
+
8
+ # Rackspace specific helper implementations
9
+ module Rackspace
10
+
11
+ # @!parse include SparkleFormation::SparkleAttribute::Heat
12
+
13
+ # Set customized struct behavior
14
+ def self.included(klass)
15
+ klass.include SparkleFormation::SparkleAttribute::Heat
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -224,7 +224,7 @@ class SparkleFormation
224
224
  unless(result)
225
225
  message = "Failed to locate requested dynamic block for insertion: #{dynamic_name} " \
226
226
  "(valid: #{struct._self.sparkle.dynamics.keys.sort.join(', ')})"
227
- if(struct._self.provider_resources.registry.keys.size > 1)
227
+ if(struct._self.provider_resources && struct._self.provider_resources.registry.keys.size > 1)
228
228
  t_name = struct._self.provider_resources.registry.keys.first
229
229
  message << "\nBuiltin dynamics pattern `#{t_name}` -> `:#{Bogo::Utility.snake(t_name.gsub('::', '_'))}`"
230
230
  end
@@ -266,7 +266,14 @@ class SparkleFormation
266
266
  nested_template.compile_state = options[:parameters]
267
267
  end
268
268
  struct.resources.set!(resource_name) do
269
- type DEFAULT_STACK_RESOURCE
269
+ type struct._self.stack_resource_type
270
+ end
271
+ unless(struct._self.sparkle.empty?)
272
+ struct._self.sparkle.size.times do |idx|
273
+ nested_template.sparkle.add_sparkle(
274
+ struct._self.sparkle.sparkle_at(idx)
275
+ )
276
+ end
270
277
  end
271
278
  struct.resources[resource_name].properties.stack nested_template
272
279
  if(block_given?)
@@ -380,7 +387,8 @@ class SparkleFormation
380
387
  }
381
388
  )
382
389
  )
383
- unless(options[:disable_aws_builtins])
390
+ self.provider = options.fetch(:provider, @parent ? @parent.provider : :aws)
391
+ if(provider == :aws || !options[:disable_aws_builtins])
384
392
  require 'sparkle_formation/aws'
385
393
  end
386
394
  @parameters = set_generation_parameters!(
@@ -388,21 +396,25 @@ class SparkleFormation
388
396
  options.fetch(:parameters, {})
389
397
  )
390
398
  )
391
- @stack_resource_types = (
392
- VALID_STACK_RESOURCES +
393
- options.fetch(:stack_resource_types, [])
394
- ).uniq
399
+ @stack_resource_types = [
400
+ stack_resource_type,
401
+ *options.fetch(:stack_resource_types, [])
402
+ ].compact.uniq
395
403
  @components = Smash.new
396
404
  @load_order = []
397
405
  @overrides = []
398
406
  @parent = options[:parent]
399
- @provider = options.fetch(:provider, @parent ? @parent.provider : :aws)
400
407
  if(block)
401
408
  load_block(block)
402
409
  end
403
410
  @compiled = nil
404
411
  end
405
412
 
413
+ # @return [String] provider stack resource type
414
+ def stack_resource_type
415
+ DEFAULT_STACK_RESOURCE
416
+ end
417
+
406
418
  # Set remote API target for template to allow loading of
407
419
  # provider specific helpers and data if available. Setting
408
420
  # to a false-y value will disable helpers loading
@@ -412,6 +424,11 @@ class SparkleFormation
412
424
  def provider=(val)
413
425
  if(val)
414
426
  @provider = Bogo::Utility.snake(val).to_sym
427
+ provider_klass = Bogo::Utility.camel(@provider.to_s)
428
+ if(Provider.const_defined?(provider_klass))
429
+ extend Provider.const_get(provider_klass)
430
+ end
431
+ @provider
415
432
  else
416
433
  @provider = nil
417
434
  end
@@ -558,6 +575,12 @@ class SparkleFormation
558
575
  compiled = struct_class.new
559
576
  compiled._set_self(self)
560
577
  compiled._struct_class = struct_class
578
+ if(struct_class.const_defined?(:CAMEL_KEYS))
579
+ compiled._camel_keys = struct_class.const_get(:CAMEL_KEYS)
580
+ end
581
+ if(struct_class.const_defined?(:CAMEL_STYLE))
582
+ compiled._camel_style = struct_class.const_get(:CAMEL_STYLE)
583
+ end
561
584
  if(compile_state)
562
585
  compiled.set_state!(compile_state)
563
586
  end
@@ -587,71 +610,65 @@ class SparkleFormation
587
610
 
588
611
  # @return [Array<SparkleFormation>]
589
612
  def nested_stacks(*args)
590
- compile.resources.keys!.map do |key|
591
- if(stack_resource_type?(compile.resources[key].type))
592
- result = [compile.resources[key].properties.stack]
593
- if(args.include?(:with_resource))
594
- result.push(compile[:resources][key])
595
- end
596
- if(args.include?(:with_name))
597
- result.push(key)
613
+ if(compile[:resources])
614
+ compile.resources.keys!.map do |key|
615
+ if(stack_resource_type?(compile.resources[key].type))
616
+ result = [compile.resources[key].properties.stack]
617
+ if(args.include?(:with_resource))
618
+ result.push(compile[:resources][key])
619
+ end
620
+ if(args.include?(:with_name))
621
+ result.push(key)
622
+ end
623
+ result.size == 1 ? result.first : result
598
624
  end
599
- result.size == 1 ? result.first : result
600
- end
601
- end.compact
625
+ end.compact
626
+ else
627
+ []
628
+ end
602
629
  end
603
630
 
604
631
  # @return [TrueClass, FalseClass] includes nested stacks
605
632
  def nested?(stack_hash=nil)
606
- stack_hash = compile.dump! unless stack_hash
607
- !!stack_hash.fetch('Resources', {}).detect do |_r_name, resource|
608
- stack_resource_type?(resource['Type'])
633
+ if(stack_hash)
634
+ raise Error::Deprecated.new "Hash parameter no longer valid for this method (`#{self.class}##{__callee__}`)"
635
+ end
636
+ unless(compile.resources.nil?)
637
+ compile.resources._data.any? do |r_name, r_value|
638
+ stack_resource_type?(r_value.type)
639
+ end
609
640
  end
610
641
  end
611
642
 
612
643
  # @return [TrueClass, FalseClass] includes _only_ nested stacks
613
644
  def isolated_nests?(stack_hash=nil)
614
- stack_hash = compile.dump! unless stack_hash
615
- stack_hash.fetch('Resources', {}).all? do |_name, resource|
616
- stack_resource_type?(resource['Type'])
645
+ if(stack_hash)
646
+ raise Error::Deprecated.new "Hash parameter no longer valid for this method (`#{self.class}##{__callee__}`)"
647
+ end
648
+ unless(compile.resources.nil?)
649
+ compile.resources._data.all? do |r_name, r_value|
650
+ stack_resource_type?(r_value.type)
651
+ end
617
652
  end
618
653
  end
619
654
 
620
655
  # @return [TrueClass, FalseClass] policies defined
621
656
  def includes_policies?(stack_hash=nil)
622
- stack_hash = compile.dump! unless stack_hash
623
- stack_hash.fetch('Resources', {}).any? do |_name, resource|
624
- resource.key?('Policy')
657
+ if(stack_hash)
658
+ raise Error::Deprecated.new "Hash parameter no longer valid for this method (`#{self.class}##{__callee__}`)"
659
+ end
660
+ unless(compile.resources.nil?)
661
+ compile.resources._data.any? do |r_name, r_value|
662
+ !r_value.policy.nil?
663
+ end
625
664
  end
626
665
  end
627
666
 
628
667
  # Generate policy for stack
629
668
  #
630
669
  # @return [Hash]
631
- # @todo this is very AWS specific, so make this easy for swapping
632
670
  def generate_policy
633
- statements = []
634
- compile.resources.keys!.each do |r_name|
635
- r_object = compile.resources[r_name]
636
- if(r_object['Policy'])
637
- r_object['Policy'].keys!.each do |effect|
638
- statements.push(
639
- 'Effect' => effect.to_s.capitalize,
640
- 'Action' => [r_object['Policy'][effect]].flatten.compact.map{|i| "Update:#{i}"},
641
- 'Resource' => "LogicalResourceId/#{r_name}",
642
- 'Principal' => '*'
643
- )
644
- end
645
- r_object.delete!('Policy')
646
- end
647
- end
648
- statements.push(
649
- 'Effect' => 'Allow',
650
- 'Action' => 'Update:*',
651
- 'Resource' => '*',
652
- 'Principal' => '*'
653
- )
654
- Smash.new('Statement' => statements)
671
+ Smash.new
655
672
  end
656
673
 
657
674
  # Apply nesting logic to stack
@@ -678,22 +695,6 @@ class SparkleFormation
678
695
  # @yieldreturn [Hash] key/values to be merged into resource properties
679
696
  # @return [Hash] dumped stack
680
697
  def apply_deep_nesting(*args, &block)
681
- outputs = collect_outputs
682
- nested_stacks(:with_resource).each do |stack, resource|
683
- unless(stack.nested_stacks.empty?)
684
- stack.apply_deep_nesting(*args)
685
- end
686
- stack.compile.parameters.keys!.each do |parameter_name|
687
- if(output_name = output_matched?(parameter_name, outputs.keys))
688
- next if outputs[output_name] == stack
689
- stack_output = stack.make_output_available(output_name, outputs)
690
- resource.properties.parameters.set!(parameter_name, stack_output)
691
- end
692
- end
693
- end
694
- if(block_given?)
695
- extract_templates(&block)
696
- end
697
698
  compile.dump!
698
699
  end
699
700
 
@@ -716,40 +717,7 @@ class SparkleFormation
716
717
  # @param outputs [Hash] listing of outputs
717
718
  # @reutrn [Hash] reference to output value (used for setting parameter)
718
719
  def make_output_available(output_name, outputs)
719
- bubble_path = outputs[output_name].root_path - root_path
720
- drip_path = root_path - outputs[output_name].root_path
721
- bubble_path.each_slice(2) do |base_sparkle, ref_sparkle|
722
- next unless ref_sparkle
723
- base_sparkle.compile.outputs.set!(output_name).set!(
724
- :value, base_sparkle.compile.attr!(
725
- ref_sparkle.name, "Outputs.#{output_name}"
726
- )
727
- )
728
- end
729
- if(bubble_path.empty?)
730
- if(drip_path.size == 1)
731
- parent = drip_path.first.parent
732
- if(parent && parent.compile.parameters.data![output_name])
733
- return compile.ref!(output_name)
734
- end
735
- end
736
- raise ArgumentError.new "Failed to detect available bubbling path for output `#{output_name}`. " <<
737
- 'This may be due to a circular dependency! ' <<
738
- "(Output Path: #{outputs[output_name].root_path.map(&:name).join(' > ')} " <<
739
- "Requester Path: #{root_path.map(&:name).join(' > ')})"
740
- end
741
- result = compile.attr!(bubble_path.first.name, "Outputs.#{output_name}")
742
- if(drip_path.size > 1)
743
- parent = drip_path.first.parent
744
- drip_path.unshift(parent) if parent
745
- drip_path.each_slice(2) do |base_sparkle, ref_sparkle|
746
- next unless ref_sparkle
747
- base_sparkle.compile.resources[ref_sparkle.name].properties.parameters.set!(output_name, result)
748
- ref_sparkle.compile.parameters.set!(output_name){ type 'String' } # TODO: <<<<------ type check and prop
749
- result = compile.ref!(output_name)
750
- end
751
- end
752
- result
720
+ {}
753
721
  end
754
722
 
755
723
  # Extract and process nested stacks
@@ -785,32 +753,13 @@ class SparkleFormation
785
753
  # @yieldreturn [String] Remote URL storage for template
786
754
  # @return [Hash]
787
755
  def apply_shallow_nesting(*args, &block)
788
- parameters = compile[:parameters] ? compile[:parameters]._dump : {}
789
- output_map = {}
790
- nested_stacks(:with_resource, :with_name).each do |_stack, stack_resource, stack_name|
791
- remap_nested_parameters(compile, parameters, stack_name, stack_resource, output_map)
792
- end
793
- extract_templates(&block)
794
- compile.parameters parameters
795
- if(args.include?(:bubble_outputs))
796
- outputs_hash = Hash[
797
- output_map do |name, value|
798
- [name, {'Value' => {'Fn::GetAtt' => value}}]
799
- end
800
- ]
801
- if(compile.outputs)
802
- compile._merge(compile._klass_new(outputs_hash))
803
- else
804
- compile.outputs output_hash
805
- end
806
- end
807
756
  compile.dump!
808
757
  end
809
758
 
810
759
  # @return [Smash<output_name:SparkleFormation>]
811
760
  def collect_outputs(*args)
812
761
  if(args.include?(:force) || root?)
813
- if(compile.outputs)
762
+ unless(compile.outputs.nil?)
814
763
  outputs = Smash[
815
764
  compile.outputs.keys!.zip(
816
765
  [self] * compile.outputs.keys!.size
@@ -843,39 +792,6 @@ class SparkleFormation
843
792
  # @note if parameter has includes `StackUnique` a new parameter will
844
793
  # be added to container stack and it will not use outputs
845
794
  def remap_nested_parameters(template, parameters, stack_name, stack_resource, output_map)
846
- stack_parameters = stack_resource.properties.stack.compile.parameters
847
- unless(stack_parameters.nil?)
848
- stack_parameters._dump.each do |pname, pval|
849
- if(pval['StackUnique'])
850
- check_name = [stack_name, pname].join
851
- else
852
- check_name = pname
853
- end
854
- if(parameters.keys.include?(check_name))
855
- if(parameters[check_name]['Type'] == 'CommaDelimitedList')
856
- new_val = {'Fn::Join' => [',', {'Ref' => check_name}]}
857
- else
858
- new_val = {'Ref' => check_name}
859
- end
860
- template.resources.set!(stack_name).properties.parameters.set!(pname, new_val)
861
- elsif(output_map[check_name])
862
- template.resources.set!(stack_name).properties.parameters.set!(pname, 'Fn::GetAtt' => output_map[check_name])
863
- else
864
- if(pval['Type'] == 'CommaDelimitedList')
865
- new_val = {'Fn::Join' => [',', {'Ref' => check_name}]}
866
- else
867
- new_val = {'Ref' => check_name}
868
- end
869
- template.resources.set!(stack_name).properties.parameters.set!(pname, new_val)
870
- parameters[check_name] = pval
871
- end
872
- end
873
- end
874
- unless(stack_resource.properties.stack.compile.outputs.nil?)
875
- stack_resource.properties.stack.compile.outputs.keys!.each do |oname|
876
- output_map[oname] = [stack_name, "Outputs.#{oname}"]
877
- end
878
- end
879
795
  true
880
796
  end
881
797
 
@@ -45,6 +45,40 @@ class SparkleFormation
45
45
  end
46
46
  end
47
47
 
48
+ # Process value in search for FunctionStruct objects. If found replace with
49
+ # the root item of the structure
50
+ #
51
+ # @param item [Object]
52
+ # @return [Object]
53
+ def function_bubbler(item)
54
+ if(item.is_a?(::Enumerable))
55
+ if(item.respond_to?(:keys))
56
+ item.class[
57
+ *item.map do |entry|
58
+ function_bubbler(entry)
59
+ end.flatten(1)
60
+ ]
61
+ else
62
+ item.class[
63
+ *item.map do |entry|
64
+ function_bubbler(entry)
65
+ end
66
+ ]
67
+ end
68
+ elsif(item.is_a?(::SparkleFormation::FunctionStruct))
69
+ item._root
70
+ else
71
+ item
72
+ end
73
+ end
74
+
75
+ # Override to inspect result value and fetch root if value is a
76
+ # FunctionStruct
77
+ def method_missing(sym, *args, &block)
78
+ result = super(*[sym, *args], &block)
79
+ @table[_process_key(sym)] = function_bubbler(result)
80
+ end
81
+
48
82
  # @return [Class]
49
83
  def _klass
50
84
  _struct_class || ::SparkleFormation::SparkleStruct
@@ -76,7 +110,11 @@ class SparkleFormation
76
110
  result = super
77
111
  if(@self && result.nil?)
78
112
  if(_self.parameters.keys.map(&:to_s).include?(arg.to_s))
79
- ::Kernel.raise ::ArgumentError.new "No value provided for compile time parameter: `#{arg}`!"
113
+ unless(_self.parameters[arg.to_sym].key?(:default))
114
+ ::Kernel.raise ::ArgumentError.new "No value provided for compile time parameter: `#{arg}`!"
115
+ else
116
+ result = _self.parameters[arg.to_sym][:default]
117
+ end
80
118
  end
81
119
  end
82
120
  result
@@ -84,5 +122,4 @@ class SparkleFormation
84
122
  alias_method :state!, :_state
85
123
 
86
124
  end
87
-
88
125
  end
@@ -1,5 +1,5 @@
1
1
  # Unicorns and rainbows
2
2
  class SparkleFormation
3
3
  # Current library version
4
- VERSION = Gem::Version.new('1.2.0')
4
+ VERSION = Gem::Version.new('2.0.0')
5
5
  end
@@ -10,12 +10,16 @@ Gem::Specification.new do |s|
10
10
  s.description = 'Ruby DSL for programmatic orchestration API template generation'
11
11
  s.license = 'Apache-2.0'
12
12
  s.require_path = 'lib'
13
- s.add_dependency 'attribute_struct', '>= 0.2.27', '< 0.4'
14
- s.add_dependency 'multi_json'
15
- s.add_dependency 'bogo'
13
+ s.required_ruby_version = '>= 2.0'
14
+ s.add_runtime_dependency 'attribute_struct', '>= 0.3.0', '< 0.5'
15
+ s.add_runtime_dependency 'multi_json'
16
+ s.add_runtime_dependency 'bogo'
16
17
  s.add_development_dependency 'minitest'
17
18
  s.add_development_dependency 'rake'
18
19
  s.add_development_dependency 'rubocop'
20
+ s.add_development_dependency 'yard'
21
+ s.add_development_dependency 'redcarpet', '~> 2.0'
22
+ s.add_development_dependency 'github-markup'
19
23
  s.executables << 'generate_sparkle_docs'
20
24
  s.files = Dir['{lib,docs}/**/*'] + %w(sparkle_formation.gemspec README.md CHANGELOG.md LICENSE)
21
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparkle_formation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-02 00:00:00.000000000 Z
11
+ date: 2016-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: attribute_struct
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.27
19
+ version: 0.3.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '0.4'
22
+ version: '0.5'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 0.2.27
29
+ version: 0.3.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '0.4'
32
+ version: '0.5'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: multi_json
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -100,6 +100,48 @@ dependencies:
100
100
  - - ">="
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: yard
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: redcarpet
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '2.0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '2.0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: github-markup
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
103
145
  description: Ruby DSL for programmatic orchestration API template generation
104
146
  email: chrisroberts.code@gmail.com
105
147
  executables:
@@ -132,12 +174,26 @@ files:
132
174
  - lib/sparkle_formation.rb
133
175
  - lib/sparkle_formation/aws.rb
134
176
  - lib/sparkle_formation/error.rb
177
+ - lib/sparkle_formation/function_struct.rb
178
+ - lib/sparkle_formation/provider.rb
179
+ - lib/sparkle_formation/provider/aws.rb
180
+ - lib/sparkle_formation/provider/azure.rb
181
+ - lib/sparkle_formation/provider/heat.rb
135
182
  - lib/sparkle_formation/resources.rb
136
183
  - lib/sparkle_formation/resources/aws.rb
137
184
  - lib/sparkle_formation/resources/aws_resources.json
185
+ - lib/sparkle_formation/resources/azure.rb
186
+ - lib/sparkle_formation/resources/azure_resources.json
187
+ - lib/sparkle_formation/resources/heat.rb
188
+ - lib/sparkle_formation/resources/heat_resources.json
189
+ - lib/sparkle_formation/resources/rackspace.rb
190
+ - lib/sparkle_formation/resources/rackspace_resources.json
138
191
  - lib/sparkle_formation/sparkle.rb
139
192
  - lib/sparkle_formation/sparkle_attribute.rb
140
193
  - lib/sparkle_formation/sparkle_attribute/aws.rb
194
+ - lib/sparkle_formation/sparkle_attribute/azure.rb
195
+ - lib/sparkle_formation/sparkle_attribute/heat.rb
196
+ - lib/sparkle_formation/sparkle_attribute/rackspace.rb
141
197
  - lib/sparkle_formation/sparkle_collection.rb
142
198
  - lib/sparkle_formation/sparkle_formation.rb
143
199
  - lib/sparkle_formation/sparkle_struct.rb
@@ -159,7 +215,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
215
  requirements:
160
216
  - - ">="
161
217
  - !ruby/object:Gem::Version
162
- version: '0'
218
+ version: '2.0'
163
219
  required_rubygems_version: !ruby/object:Gem::Requirement
164
220
  requirements:
165
221
  - - ">="