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,168 @@
1
+ require 'sparkle_formation'
2
+
3
+ class SparkleFormation
4
+ module Provider
5
+ # Azure specific implementation
6
+ module Azure
7
+
8
+ # @return [String] Type string for Azure Resource Manager stack resource
9
+ def stack_resource_type
10
+ 'Microsoft.Resources/deployments'
11
+ end
12
+
13
+ # Generate policy for stack
14
+ #
15
+ # @return [Hash]
16
+ def generate_policy
17
+ {}
18
+ end
19
+
20
+ # Apply deeply nested stacks. This is the new nesting approach and
21
+ # does not bubble parameters up to the root stack. Parameters are
22
+ # isolated to the stack resource itself and output mapping is
23
+ # automatically applied.
24
+ #
25
+ # @yieldparam stack [SparkleFormation] stack instance
26
+ # @yieldparam resource [AttributeStruct] the stack resource
27
+ # @yieldparam s_name [String] stack resource name
28
+ # @yieldreturn [Hash] key/values to be merged into resource properties
29
+ # @return [Hash] dumped stack
30
+ def apply_deep_nesting(*args, &block)
31
+ outputs = collect_outputs
32
+ nested_stacks(:with_resource).each do |stack, resource|
33
+ unless(stack.nested_stacks.empty?)
34
+ stack.apply_deep_nesting(*args)
35
+ end
36
+ stack.compile.parameters.keys!.each do |parameter_name|
37
+ if(output_name = output_matched?(parameter_name, outputs.keys))
38
+ next if outputs[output_name] == stack
39
+ stack_output = stack.make_output_available(output_name, outputs)
40
+ resource.properties.parameters._set(parameter_name).value stack_output
41
+ end
42
+ end
43
+ end
44
+ if(block_given?)
45
+ extract_templates(&block)
46
+ end
47
+ compile.dump!
48
+ end
49
+
50
+ # Apply shallow nesting. This style of nesting will bubble
51
+ # parameters up to the root stack. This type of nesting is the
52
+ # original and now deprecated, but remains for compat issues so any
53
+ # existing usage won't be automatically busted.
54
+ #
55
+ # @yieldparam resource_name [String] name of stack resource
56
+ # @yieldparam stack [SparkleFormation] nested stack
57
+ # @yieldreturn [String] Remote URL storage for template
58
+ # @return [Hash]
59
+ def apply_shallow_nesting(*args, &block)
60
+ parameters = compile.parameters
61
+ output_map = {}
62
+ nested_stacks(:with_resource, :with_name).each do |_stack, stack_resource, stack_name|
63
+ remap_nested_parameters(compile, parameters, stack_name, stack_resource, output_map)
64
+ end
65
+ extract_templates(&block)
66
+ if(args.include?(:bubble_outputs))
67
+ output_map.each do |o_name, o_val|
68
+ compile.outputs._set(o_name).value compile._stack_output(*o_val)
69
+ end
70
+ end
71
+ compile.dump!
72
+ end
73
+
74
+ # Extract output to make available for stack parameter usage at the
75
+ # current depth
76
+ #
77
+ # @param output_name [String] name of output
78
+ # @param outputs [Hash] listing of outputs
79
+ # @reutrn [Hash] reference to output value (used for setting parameter)
80
+ def make_output_available(output_name, outputs)
81
+ bubble_path = outputs[output_name].root_path - root_path
82
+ drip_path = root_path - outputs[output_name].root_path
83
+ bubble_path.each_slice(2) do |base_sparkle, ref_sparkle|
84
+ next unless ref_sparkle
85
+ base_sparkle.compile.outputs._set(output_name)._set(
86
+ :value, base_sparkle.compile._stack_output(
87
+ ref_sparkle.name, output_name
88
+ )
89
+ )
90
+ end
91
+ if(bubble_path.empty?)
92
+ if(drip_path.size == 1)
93
+ parent = drip_path.first.parent
94
+ if(parent && !parent.compile.parameters._set(output_name).nil?)
95
+ return compile.parameter!(output_name)
96
+ end
97
+ end
98
+ raise ArgumentError.new "Failed to detect available bubbling path for output `#{output_name}`. " <<
99
+ 'This may be due to a circular dependency! ' <<
100
+ "(Output Path: #{outputs[output_name].root_path.map(&:name).join(' > ')} " <<
101
+ "Requester Path: #{root_path.map(&:name).join(' > ')})"
102
+ end
103
+ result = compile._stack_output(bubble_path.first.name, output_name)
104
+ if(drip_path.size > 1)
105
+ parent = drip_path.first.parent
106
+ drip_path.unshift(parent) if parent
107
+ drip_path.each_slice(2) do |base_sparkle, ref_sparkle|
108
+ next unless ref_sparkle
109
+ base_sparkle.compile.resources[ref_sparkle.name].properties.parameters.value._set(output_name, result)
110
+ ref_sparkle.compile.parameters._set(output_name).type 'string' # TODO: <<<<------ type check and prop
111
+ result = compile._parameter(output_name)
112
+ end
113
+ end
114
+ result
115
+ end
116
+
117
+ # Extract parameters from nested stacks. Check for previous nested
118
+ # stack outputs that match parameter. If match, set parameter to use
119
+ # output. If no match, check container stack parameters for match.
120
+ # If match, set to use ref. If no match, add parameter to container
121
+ # stack parameters and set to use ref.
122
+ #
123
+ # @param template [Hash] template being processed
124
+ # @param parameters [Hash] top level parameter set being built
125
+ # @param stack_name [String] name of stack resource
126
+ # @param stack_resource [Hash] duplicate of stack resource contents
127
+ # @param output_map [Hash] mapping of output names to required stack output access
128
+ # @return [TrueClass]
129
+ # @note if parameter has includes `StackUnique` a new parameter will
130
+ # be added to container stack and it will not use outputs
131
+ def remap_nested_parameters(template, parameters, stack_name, stack_resource, output_map)
132
+ nested_template = stack_resource.properties.stack.compile
133
+ stack_parameters = nested_template.parameters
134
+ unless(stack_parameters.nil?)
135
+ stack_parameters._keys.each do |pname|
136
+ pval = stack_parameters[pname]
137
+ unless(pval.stack_unique.nil?)
138
+ check_name = [stack_name, pname].join
139
+ else
140
+ check_name = pname
141
+ end
142
+ if(!parameters._set(check_name).nil?)
143
+ template.resources._set(stack_name).properties.parameters._set(pname).value(
144
+ template._parameter(check_name)
145
+ )
146
+ elsif(output_map[check_name])
147
+ template.resources._set(stack_name).properties.parameters._set(pname).value(
148
+ template._stack_output(*output_map[check_name])
149
+ )
150
+ else
151
+ parameters._set(check_name, pval)
152
+ template.resources._set(stack_name).properties.parameters._set(pname).value(
153
+ template._parameter(check_name)
154
+ )
155
+ end
156
+ end
157
+ end
158
+ unless(nested_template.outputs.nil?)
159
+ nested_template.outputs.keys!.each do |oname|
160
+ output_map[oname] = [stack_name, oname]
161
+ end
162
+ end
163
+ true
164
+ end
165
+
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,163 @@
1
+ require 'sparkle_formation'
2
+
3
+ class SparkleFormation
4
+ module Provider
5
+ # Heat specific implementation
6
+ module Heat
7
+
8
+ # @return [String] Type string for OpenStack HEAT stack resource
9
+ def stack_resource_type
10
+ 'OS::Heat::Stack'
11
+ end
12
+
13
+ # Generate policy for stack
14
+ #
15
+ # @return [Hash]
16
+ def generate_policy
17
+ {}
18
+ end
19
+
20
+ # Apply deeply nested stacks. This is the new nesting approach and
21
+ # does not bubble parameters up to the root stack. Parameters are
22
+ # isolated to the stack resource itself and output mapping is
23
+ # automatically applied.
24
+ #
25
+ # @yieldparam stack [SparkleFormation] stack instance
26
+ # @yieldparam resource [AttributeStruct] the stack resource
27
+ # @yieldparam s_name [String] stack resource name
28
+ # @yieldreturn [Hash] key/values to be merged into resource properties
29
+ # @return [Hash] dumped stack
30
+ def apply_deep_nesting(*args, &block)
31
+ outputs = collect_outputs
32
+ nested_stacks(:with_resource).each do |stack, resource|
33
+ unless(stack.nested_stacks.empty?)
34
+ stack.apply_deep_nesting(*args)
35
+ end
36
+ stack.compile.parameters.keys!.each do |parameter_name|
37
+ if(output_name = output_matched?(parameter_name, outputs.keys))
38
+ next if outputs[output_name] == stack
39
+ stack_output = stack.make_output_available(output_name, outputs)
40
+ resource.properties.parameters._set(parameter_name, stack_output)
41
+ end
42
+ end
43
+ end
44
+ if(block_given?)
45
+ extract_templates(&block)
46
+ end
47
+ compile.dump!
48
+ end
49
+
50
+ # Apply shallow nesting. This style of nesting will bubble
51
+ # parameters up to the root stack. This type of nesting is the
52
+ # original and now deprecated, but remains for compat issues so any
53
+ # existing usage won't be automatically busted.
54
+ #
55
+ # @yieldparam resource_name [String] name of stack resource
56
+ # @yieldparam stack [SparkleFormation] nested stack
57
+ # @yieldreturn [String] Remote URL storage for template
58
+ # @return [Hash]
59
+ def apply_shallow_nesting(*args, &block)
60
+ parameters = compile.parameters
61
+ output_map = {}
62
+ nested_stacks(:with_resource, :with_name).each do |_stack, stack_resource, stack_name|
63
+ remap_nested_parameters(compile, parameters, stack_name, stack_resource, output_map)
64
+ end
65
+ extract_templates(&block)
66
+ if(args.include?(:bubble_outputs))
67
+ output_map.each do |o_name, o_val|
68
+ compile.outputs._set(o_name).value compile._stack_output(*o_val)
69
+ end
70
+ end
71
+ compile.dump!
72
+ end
73
+
74
+ # Extract output to make available for stack parameter usage at the
75
+ # current depth
76
+ #
77
+ # @param output_name [String] name of output
78
+ # @param outputs [Hash] listing of outputs
79
+ # @reutrn [Hash] reference to output value (used for setting parameter)
80
+ def make_output_available(output_name, outputs)
81
+ bubble_path = outputs[output_name].root_path - root_path
82
+ drip_path = root_path - outputs[output_name].root_path
83
+ bubble_path.each_slice(2) do |base_sparkle, ref_sparkle|
84
+ next unless ref_sparkle
85
+ base_sparkle.compile.outputs._set(output_name)._set(
86
+ :value, base_sparkle.compile._stack_output(
87
+ ref_sparkle.name, output_name
88
+ )
89
+ )
90
+ end
91
+ if(bubble_path.empty?)
92
+ if(drip_path.size == 1)
93
+ parent = drip_path.first.parent
94
+ if(parent && !parent.compile.parameters._set(output_name).nil?)
95
+ return compile.parameter!(output_name)
96
+ end
97
+ end
98
+ raise ArgumentError.new "Failed to detect available bubbling path for output `#{output_name}`. " <<
99
+ 'This may be due to a circular dependency! ' <<
100
+ "(Output Path: #{outputs[output_name].root_path.map(&:name).join(' > ')} " <<
101
+ "Requester Path: #{root_path.map(&:name).join(' > ')})"
102
+ end
103
+ result = compile._stack_output(bubble_path.first.name, output_name)
104
+ if(drip_path.size > 1)
105
+ parent = drip_path.first.parent
106
+ drip_path.unshift(parent) if parent
107
+ drip_path.each_slice(2) do |base_sparkle, ref_sparkle|
108
+ next unless ref_sparkle
109
+ base_sparkle.compile.resources[ref_sparkle.name].properties.parameters._set(output_name, result)
110
+ ref_sparkle.compile.parameters._set(output_name).type 'string' # TODO: <<<<------ type check and prop
111
+ result = compile._parameter(output_name)
112
+ end
113
+ end
114
+ result
115
+ end
116
+
117
+ # Extract parameters from nested stacks. Check for previous nested
118
+ # stack outputs that match parameter. If match, set parameter to use
119
+ # output. If no match, check container stack parameters for match.
120
+ # If match, set to use ref. If no match, add parameter to container
121
+ # stack parameters and set to use ref.
122
+ #
123
+ # @param template [Hash] template being processed
124
+ # @param parameters [Hash] top level parameter set being built
125
+ # @param stack_name [String] name of stack resource
126
+ # @param stack_resource [Hash] duplicate of stack resource contents
127
+ # @param output_map [Hash] mapping of output names to required stack output access
128
+ # @return [TrueClass]
129
+ # @note if parameter has includes `StackUnique` a new parameter will
130
+ # be added to container stack and it will not use outputs
131
+ def remap_nested_parameters(template, parameters, stack_name, stack_resource, output_map)
132
+ nested_template = stack_resource.properties.stack.compile
133
+ stack_parameters = nested_template.parameters
134
+ unless(stack_parameters.nil?)
135
+ stack_parameters._keys.each do |pname|
136
+ pval = stack_parameters[pname]
137
+ unless(pval.stack_unique.nil?)
138
+ check_name = [stack_name, pname].join
139
+ else
140
+ check_name = pname
141
+ end
142
+ if(!parameters._set(check_name).nil?)
143
+ template.resources._set(stack_name).properties.parameters._set(pname, template._parameter(check_name))
144
+ elsif(output_map[check_name])
145
+ template.resources._set(stack_name).properties.parameters._set(pname)
146
+ template._stack_output(*output_map[check_name])
147
+ else
148
+ parameters._set(check_name, pval)
149
+ template.resources._set(stack_name).properties.parameters._set(pname, template._parameter(check_name))
150
+ end
151
+ end
152
+ end
153
+ unless(nested_template.outputs.nil?)
154
+ nested_template.outputs.keys!.each do |oname|
155
+ output_map[oname] = [stack_name, oname]
156
+ end
157
+ end
158
+ true
159
+ end
160
+
161
+ end
162
+ end
163
+ end
@@ -5,12 +5,25 @@ class SparkleFormation
5
5
  class Resources
6
6
 
7
7
  autoload :Aws, 'sparkle_formation/resources/aws'
8
+ autoload :Azure, 'sparkle_formation/resources/azure'
9
+ autoload :Heat, 'sparkle_formation/resources/heat'
10
+ autoload :Rackspace, 'sparkle_formation/resources/rackspace'
11
+
12
+ # Characters to be removed from supplied key on matching
13
+ RESOURCE_TYPE_TR = '_'
14
+ # String to split for resource namespacing
15
+ RESOURCE_TYPE_NAMESPACE_SPLITTER = '::'
8
16
 
9
17
  class << self
10
18
 
11
19
  include SparkleFormation::Utils::AnimalStrings
12
20
  # @!parse include SparkleFormation::Utils::AnimalStrings
13
21
 
22
+ # @return [String] base registry key
23
+ def base_key
24
+ Bogo::Utility.snake(self.name.split('::').last) # rubocop:disable Style/RedundantSelf
25
+ end
26
+
14
27
  # Register resource
15
28
  #
16
29
  # @param type [String] Orchestration resource type
@@ -20,7 +33,8 @@ class SparkleFormation
20
33
  unless(class_variable_defined?(:@@registry))
21
34
  @@registry = AttributeStruct.hashish.new
22
35
  end
23
- @@registry[type] = hash
36
+ @@registry[base_key] ||= AttributeStruct.hashish.new
37
+ @@registry[base_key][type] = hash
24
38
  true
25
39
  end
26
40
 
@@ -67,11 +81,13 @@ class SparkleFormation
67
81
  # @return [String, NilClass]
68
82
  def registry_key(key)
69
83
  o_key = key
70
- key = key.to_s.tr('_', '')
84
+ key = key.to_s.tr(self.const_get(:RESOURCE_TYPE_TR), '') # rubocop:disable Style/RedundantSelf
71
85
  snake_parts = nil
72
- result = @@registry.keys.detect do |ref|
86
+ result = @@registry[base_key].keys.detect do |ref|
73
87
  ref = ref.downcase
74
- snake_parts = ref.split('::')
88
+ snake_parts = ref.split(
89
+ self.const_get(:RESOURCE_TYPE_NAMESPACE_SPLITTER) # rubocop:disable Style/RedundantSelf
90
+ )
75
91
  until(snake_parts.empty?)
76
92
  break if snake_parts.join('') == key
77
93
  snake_parts.shift
@@ -79,8 +95,10 @@ class SparkleFormation
79
95
  !snake_parts.empty?
80
96
  end
81
97
  if(result)
82
- collisions = @@registry.keys.find_all do |ref|
83
- split_ref = ref.downcase.split('::')
98
+ collisions = @@registry[base_key].keys.find_all do |ref|
99
+ split_ref = ref.downcase.split(
100
+ self.const_get(:RESOURCE_TYPE_NAMESPACE_SPLITTER) # rubocop:disable Style/RedundantSelf
101
+ )
84
102
  ref = split_ref.slice(split_ref.size - snake_parts.size, split_ref.size).join('')
85
103
  key == ref
86
104
  end
@@ -97,16 +115,15 @@ class SparkleFormation
97
115
  # @param key [String, Symbol]
98
116
  # @return [Hashish, NilClass]
99
117
  def lookup(key)
100
- @@registry[registry_key(key)]
118
+ @@registry[base_key][registry_key(key)]
101
119
  end
102
120
 
103
121
  # @return [Hashish] currently loaded AWS registry
104
122
  def registry
105
- if(class_variable_defined?(:@@registry))
106
- @@registry
107
- else
123
+ unless(class_variable_defined?(:@@registry))
108
124
  @@registry = AttributeStruct.hashish.new
109
125
  end
126
+ @@registry[base_key]
110
127
  end
111
128
 
112
129
  end
@@ -0,0 +1,42 @@
1
+ require 'sparkle_formation'
2
+
3
+ class SparkleFormation
4
+
5
+ # Resources helper
6
+ class Resources
7
+
8
+ # Azure specific resources collection
9
+ class Azure < Resources
10
+
11
+ # String to split for resource namespacing
12
+ RESOURCE_TYPE_NAMESPACE_SPLITTER = '/'
13
+
14
+ class << self
15
+
16
+ include Bogo::Memoization
17
+
18
+ # Load the builtin AWS resources
19
+ #
20
+ # @return [TrueClass]
21
+ def load!
22
+ memoize(:azure_resources, :global) do
23
+ load(
24
+ File.join(
25
+ File.dirname(__FILE__),
26
+ 'azure_resources.json'
27
+ )
28
+ )
29
+ true
30
+ end
31
+ end
32
+
33
+ # Auto load data when included
34
+ def included(_klass)
35
+ load!
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
42
+ end