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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -22
- data/lib/sparkle_formation/aws.rb +2 -101
- data/lib/sparkle_formation/error.rb +2 -0
- data/lib/sparkle_formation/resources/aws.rb +40 -0
- data/lib/sparkle_formation/resources/aws_resources.json +5801 -0
- data/lib/sparkle_formation/resources.rb +114 -0
- data/lib/sparkle_formation/sparkle.rb +40 -45
- data/lib/sparkle_formation/sparkle_attribute/aws.rb +314 -0
- data/lib/sparkle_formation/sparkle_attribute.rb +44 -295
- data/lib/sparkle_formation/sparkle_formation.rb +68 -22
- data/lib/sparkle_formation/sparkle_struct.rb +9 -1
- data/lib/sparkle_formation/translation/heat.rb +7 -6
- data/lib/sparkle_formation/translation/rackspace.rb +9 -7
- data/lib/sparkle_formation/translation.rb +8 -7
- data/lib/sparkle_formation/utils.rb +42 -2
- data/lib/sparkle_formation/version.rb +2 -1
- data/lib/sparkle_formation.rb +2 -0
- data/sparkle_formation.gemspec +2 -1
- metadata +22 -5
- data/lib/sparkle_formation/aws/cfn_resources.rb +0 -4609
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'sparkle_formation'
|
2
|
+
|
3
|
+
class SparkleFormation
|
4
|
+
# Resources helper
|
5
|
+
class Resources
|
6
|
+
|
7
|
+
autoload :Aws, 'sparkle_formation/resources/aws'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
include SparkleFormation::Utils::AnimalStrings
|
12
|
+
# @!parse include SparkleFormation::Utils::AnimalStrings
|
13
|
+
|
14
|
+
# Register resource
|
15
|
+
#
|
16
|
+
# @param type [String] Orchestration resource type
|
17
|
+
# @param hash [Hash] metadata information
|
18
|
+
# @return [TrueClass]
|
19
|
+
def register(type, hash)
|
20
|
+
unless(class_variable_defined?(:@@registry))
|
21
|
+
@@registry = AttributeStruct.hashish.new
|
22
|
+
end
|
23
|
+
@@registry[type] = hash
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
# Resource information
|
28
|
+
#
|
29
|
+
# @param identifier [String, Symbol] resource identifier
|
30
|
+
# @param key [String, Symbol] specific data
|
31
|
+
# @return [Hashish]
|
32
|
+
def resource(identifier, key=nil)
|
33
|
+
res = lookup(identifier)
|
34
|
+
if(key && res)
|
35
|
+
res[key.to_sym]
|
36
|
+
else
|
37
|
+
res
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Register all discovered resources
|
42
|
+
#
|
43
|
+
# @param json_path_or_hash [String, Hashish] path to files or hash
|
44
|
+
# @return [TrueClass]
|
45
|
+
def load(json_path_or_hash)
|
46
|
+
if(json_path_or_hash.is_a?(String))
|
47
|
+
content = AttributeStruct.hashish.new(MultiJson.load(File.read(json_path_or_hash)))
|
48
|
+
else
|
49
|
+
content = json_path_or_hash
|
50
|
+
end
|
51
|
+
content.each do |type, hash|
|
52
|
+
register(type, hash)
|
53
|
+
end
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
# Load the builtin AWS resources
|
58
|
+
#
|
59
|
+
# @return [TrueClass]
|
60
|
+
def load!
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
# Discover registry key via part searching
|
65
|
+
#
|
66
|
+
# @param key [String, Symbol]
|
67
|
+
# @return [String, NilClass]
|
68
|
+
def registry_key(key)
|
69
|
+
o_key = key
|
70
|
+
key = key.to_s.tr('_', '')
|
71
|
+
snake_parts = nil
|
72
|
+
result = @@registry.keys.detect do |ref|
|
73
|
+
ref = ref.downcase
|
74
|
+
snake_parts = ref.split('::')
|
75
|
+
until(snake_parts.empty?)
|
76
|
+
break if snake_parts.join('') == key
|
77
|
+
snake_parts.shift
|
78
|
+
end
|
79
|
+
!snake_parts.empty?
|
80
|
+
end
|
81
|
+
if(result)
|
82
|
+
collisions = @@registry.keys.find_all do |ref|
|
83
|
+
split_ref = ref.downcase.split('::')
|
84
|
+
ref = split_ref.slice(split_ref.size - snake_parts.size, split_ref.size).join('')
|
85
|
+
key == ref
|
86
|
+
end
|
87
|
+
if(collisions.size > 1)
|
88
|
+
raise ArgumentError.new 'Ambiguous dynamic name returned multiple matches! ' \
|
89
|
+
"`#{o_key.inspect}` -> #{collisions.sort.join(', ')}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
result
|
93
|
+
end
|
94
|
+
|
95
|
+
# Registry information for given type
|
96
|
+
#
|
97
|
+
# @param key [String, Symbol]
|
98
|
+
# @return [Hashish, NilClass]
|
99
|
+
def lookup(key)
|
100
|
+
@@registry[registry_key(key)]
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [Hashish] currently loaded AWS registry
|
104
|
+
def registry
|
105
|
+
if(class_variable_defined?(:@@registry))
|
106
|
+
@@registry
|
107
|
+
else
|
108
|
+
@@registry = AttributeStruct.hashish.new
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'sparkle_formation'
|
2
2
|
|
3
|
+
# Unicorns and rainbows
|
3
4
|
class SparkleFormation
|
5
|
+
# Independent collection of SparkleFormation items
|
4
6
|
class Sparkle
|
5
7
|
|
6
8
|
class << self
|
@@ -17,16 +19,15 @@ class SparkleFormation
|
|
17
19
|
idx = caller.index do |item|
|
18
20
|
item.end_with?("`register!'")
|
19
21
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
22
|
+
idx = idx ? idx.next : 0
|
23
|
+
file = caller[idx].split(':', 2).first
|
24
|
+
path = File.join(File.dirname(file), 'sparkleformation')
|
25
|
+
unless(File.directory?(path))
|
26
|
+
path = nil
|
27
|
+
end
|
28
|
+
unless(name)
|
29
|
+
name = File.basename(caller[idx].split(':', 2).first)
|
30
|
+
name.sub!(File.extname(name), '')
|
30
31
|
end
|
31
32
|
end
|
32
33
|
unless(name)
|
@@ -61,6 +62,7 @@ class SparkleFormation
|
|
61
62
|
|
62
63
|
# Wrapper for evaluating sfn files to store within sparkle
|
63
64
|
# container and remove global application
|
65
|
+
# rubocop:disable Metrics/MethodLength
|
64
66
|
def eval_wrapper
|
65
67
|
klass = Class.new(BasicObject)
|
66
68
|
klass.class_eval(<<-EOS
|
@@ -214,7 +216,8 @@ class SparkleFormation
|
|
214
216
|
@raw_data = Smash.new(
|
215
217
|
:dynamic => [],
|
216
218
|
:component => [],
|
217
|
-
:registry => []
|
219
|
+
:registry => [],
|
220
|
+
:template => []
|
218
221
|
)
|
219
222
|
@wrapper = eval_wrapper.new
|
220
223
|
wrapper.part_data(raw_data)
|
@@ -245,35 +248,7 @@ class SparkleFormation
|
|
245
248
|
# @return [Smash<name:path>]
|
246
249
|
def templates
|
247
250
|
memoize(:templates) do
|
248
|
-
Smash.new
|
249
|
-
Dir.glob(File.join(root, '**', '**', '*.{json,rb}')) do |path|
|
250
|
-
slim_path = path.sub("#{root}/", '')
|
251
|
-
next if DIRS.include?(slim_path.split('/').first)
|
252
|
-
data = Smash.new(:template => [])
|
253
|
-
t_wrap = eval_wrapper.new
|
254
|
-
t_wrap.part_data(data)
|
255
|
-
if(slim_path.end_with?('.rb'))
|
256
|
-
begin
|
257
|
-
t_wrap.instance_eval(IO.read(path), path, 1)
|
258
|
-
rescue TypeError
|
259
|
-
end
|
260
|
-
end
|
261
|
-
data = data[:template].first || Smash.new
|
262
|
-
unless(data[:name])
|
263
|
-
data[:name] = slim_path.tr('/', '__').sub(/\.(rb|json)$/, '')
|
264
|
-
end
|
265
|
-
if(hash[data[:name]])
|
266
|
-
raise KeyError.new "Template name is already in use within pack! (`#{data[:name]}`)"
|
267
|
-
end
|
268
|
-
hash[data[:name]] = data.merge(
|
269
|
-
Smash.new(
|
270
|
-
:type => :template,
|
271
|
-
:path => path,
|
272
|
-
:serialized => !path.end_with?('.rb')
|
273
|
-
)
|
274
|
-
)
|
275
|
-
end
|
276
|
-
end
|
251
|
+
Smash.new
|
277
252
|
end
|
278
253
|
end
|
279
254
|
|
@@ -290,9 +265,9 @@ class SparkleFormation
|
|
290
265
|
result = send(TYPES[type])[name]
|
291
266
|
if(result.nil? && TYPES[type] == 'templates')
|
292
267
|
result = (
|
293
|
-
send(TYPES[type]).detect{|
|
268
|
+
send(TYPES[type]).detect{|_, v|
|
294
269
|
name = name.to_s
|
295
|
-
short_name = v[:path].sub(
|
270
|
+
short_name = v[:path].sub(%r{#{Regexp.escape(root)}/?}, '')
|
296
271
|
v[:path] == name ||
|
297
272
|
short_name == name ||
|
298
273
|
short_name.sub('.rb', '').gsub(File::SEPARATOR, '__').tr('-', '_') == name ||
|
@@ -323,7 +298,7 @@ class SparkleFormation
|
|
323
298
|
def locate_root
|
324
299
|
VALID_ROOT_DIRS.map do |part|
|
325
300
|
path = File.expand_path(File.join(Dir.pwd, part))
|
326
|
-
if(File.
|
301
|
+
if(File.exist?(path))
|
327
302
|
path
|
328
303
|
end
|
329
304
|
end.compact.first
|
@@ -332,8 +307,28 @@ class SparkleFormation
|
|
332
307
|
# Load all sparkle parts
|
333
308
|
def load_parts!
|
334
309
|
memoize(:load_parts) do
|
335
|
-
Dir.glob(File.join(root,
|
336
|
-
|
310
|
+
Dir.glob(File.join(root, '**', '**', '*.{json,rb}')).each do |file|
|
311
|
+
slim_path = file.sub("#{root}/", '')
|
312
|
+
if(file.end_with?('.rb'))
|
313
|
+
begin
|
314
|
+
wrapper.instance_eval(IO.read(file), file, 1)
|
315
|
+
rescue TypeError
|
316
|
+
end
|
317
|
+
end
|
318
|
+
if(file.end_with?('.json') || raw_data[:template].first)
|
319
|
+
data = raw_data[:template].pop || Smash.new
|
320
|
+
unless(data[:name])
|
321
|
+
data[:name] = slim_path.tr('/', '__').sub(/\.(rb|json)$/, '')
|
322
|
+
end
|
323
|
+
if(templates[data[:name]])
|
324
|
+
raise KeyError.new "Template name is already in use within pack! (`#{data[:name]}`)"
|
325
|
+
end
|
326
|
+
templates[data[:name]] = data.merge(
|
327
|
+
:type => :template,
|
328
|
+
:path => file,
|
329
|
+
:serialized => !file.end_with?('.rb')
|
330
|
+
)
|
331
|
+
end
|
337
332
|
end
|
338
333
|
raw_data.each do |key, items|
|
339
334
|
items.each do |item|
|
@@ -0,0 +1,314 @@
|
|
1
|
+
require 'sparkle_formation'
|
2
|
+
|
3
|
+
class SparkleFormation
|
4
|
+
|
5
|
+
# Provides template helper methods
|
6
|
+
module SparkleAttribute
|
7
|
+
|
8
|
+
# AWS specific helper implementations
|
9
|
+
module Aws
|
10
|
+
|
11
|
+
# Fn::Join generator
|
12
|
+
#
|
13
|
+
# @param args [Object]
|
14
|
+
# @return [Hash]
|
15
|
+
def _cf_join(*args)
|
16
|
+
options = args.detect{|i| i.is_a?(Hash) && i[:options]} || {:options => {}}
|
17
|
+
args.delete(options)
|
18
|
+
unless(args.size == 1)
|
19
|
+
args = [args]
|
20
|
+
end
|
21
|
+
{'Fn::Join' => [options[:options][:delimiter] || '', *args]}
|
22
|
+
end
|
23
|
+
alias_method :join!, :_cf_join
|
24
|
+
|
25
|
+
# Ref generator
|
26
|
+
#
|
27
|
+
# @param thing [String, Symbol] reference name
|
28
|
+
# @return [Hash]
|
29
|
+
# @note Symbol value will force key processing
|
30
|
+
def _cf_ref(thing)
|
31
|
+
__t_stringish(thing)
|
32
|
+
thing = _process_key(thing, :force) if thing.is_a?(Symbol)
|
33
|
+
{'Ref' => thing}
|
34
|
+
end
|
35
|
+
alias_method :_ref, :_cf_ref
|
36
|
+
alias_method :ref!, :_cf_ref
|
37
|
+
|
38
|
+
# Fn::FindInMap generator
|
39
|
+
#
|
40
|
+
# @param thing [String, Symbol] thing to find
|
41
|
+
# @param key [String, Symbol] thing to search
|
42
|
+
# @param suffix [Object] additional args
|
43
|
+
# @return [Hash]
|
44
|
+
def _cf_map(thing, key, *suffix)
|
45
|
+
__t_stringish(thing)
|
46
|
+
suffix = suffix.map do |item|
|
47
|
+
if(item.is_a?(Symbol))
|
48
|
+
_process_key(item, :force)
|
49
|
+
else
|
50
|
+
item
|
51
|
+
end
|
52
|
+
end
|
53
|
+
thing = _process_key(thing, :force) if thing.is_a?(Symbol)
|
54
|
+
if(key.is_a?(Symbol))
|
55
|
+
key = ref!(key)
|
56
|
+
end
|
57
|
+
{'Fn::FindInMap' => [thing, key, *suffix]}
|
58
|
+
end
|
59
|
+
alias_method :_cf_find_in_map, :_cf_map
|
60
|
+
alias_method :find_in_map!, :_cf_map
|
61
|
+
alias_method :map!, :_cf_map
|
62
|
+
|
63
|
+
# Fn::GetAtt generator
|
64
|
+
#
|
65
|
+
# @param [Object] pass through arguments
|
66
|
+
# @return [Hash]
|
67
|
+
def _cf_attr(*args)
|
68
|
+
__t_stringish(args.first)
|
69
|
+
args = args.map do |thing|
|
70
|
+
if(thing.is_a?(Symbol))
|
71
|
+
_process_key(thing, :force)
|
72
|
+
else
|
73
|
+
thing
|
74
|
+
end
|
75
|
+
end
|
76
|
+
{'Fn::GetAtt' => args}
|
77
|
+
end
|
78
|
+
alias_method :_cf_get_att, :_cf_attr
|
79
|
+
alias_method :get_att!, :_cf_attr
|
80
|
+
alias_method :attr!, :_cf_attr
|
81
|
+
|
82
|
+
# Fn::Base64 generator
|
83
|
+
#
|
84
|
+
# @param arg [Object] pass through
|
85
|
+
# @return [Hash]
|
86
|
+
def _cf_base64(arg)
|
87
|
+
{'Fn::Base64' => arg}
|
88
|
+
end
|
89
|
+
alias_method :base64!, :_cf_base64
|
90
|
+
|
91
|
+
# Fn::GetAZs generator
|
92
|
+
#
|
93
|
+
# @param region [String, Symbol] String will pass through. Symbol will be converted to ref
|
94
|
+
# @return [Hash]
|
95
|
+
def _cf_get_azs(region=nil)
|
96
|
+
region = case region
|
97
|
+
when Symbol
|
98
|
+
_cf_ref(region)
|
99
|
+
when NilClass
|
100
|
+
''
|
101
|
+
else
|
102
|
+
region
|
103
|
+
end
|
104
|
+
{'Fn::GetAZs' => region}
|
105
|
+
end
|
106
|
+
alias_method :get_azs!, :_cf_get_azs
|
107
|
+
alias_method :azs!, :_cf_get_azs
|
108
|
+
|
109
|
+
# Fn::Select generator
|
110
|
+
#
|
111
|
+
# @param index [String, Symbol, Integer] Symbol will be converted to ref
|
112
|
+
# @param item [Object, Symbol] Symbol will be converted to ref
|
113
|
+
# @return [Hash]
|
114
|
+
def _cf_select(index, item)
|
115
|
+
index = index.is_a?(Symbol) ? _cf_ref(index) : index
|
116
|
+
item = _cf_ref(item) if item.is_a?(Symbol)
|
117
|
+
{'Fn::Select' => [index, item]}
|
118
|
+
end
|
119
|
+
alias_method :select!, :_cf_select
|
120
|
+
|
121
|
+
# Condition generator
|
122
|
+
#
|
123
|
+
# @param name [String, Symbol] symbol will be processed
|
124
|
+
# @return [Hash]
|
125
|
+
def _condition(name)
|
126
|
+
__t_stringish(name)
|
127
|
+
{'Condition' => name.is_a?(Symbol) ? _process_key(name, :force) : name}
|
128
|
+
end
|
129
|
+
alias_method :condition!, :_condition
|
130
|
+
|
131
|
+
# Condition setter
|
132
|
+
#
|
133
|
+
# @param name [String, Symbol] condition name
|
134
|
+
# @return [SparkleStruct]
|
135
|
+
# @note this is used to set a {"Condition" => "Name"} into the
|
136
|
+
# current context, generally the top level of a resource
|
137
|
+
def _on_condition(name)
|
138
|
+
_set(*_condition(name).to_a.flatten)
|
139
|
+
end
|
140
|
+
alias_method :on_condition!, :_on_condition
|
141
|
+
|
142
|
+
# Fn::If generator
|
143
|
+
#
|
144
|
+
# @param cond [String, Symbol] symbol will be case processed
|
145
|
+
# @param true_value [Object]
|
146
|
+
# @param false_value [Object]
|
147
|
+
# @return [Hash]
|
148
|
+
def _if(cond, true_value, false_value)
|
149
|
+
cond = cond.is_a?(Symbol) ? _process_key(cond) : cond
|
150
|
+
{'Fn::If' => _array(cond, true_value, false_value)}
|
151
|
+
end
|
152
|
+
alias_method :if!, :_if
|
153
|
+
|
154
|
+
# Fn::And generator
|
155
|
+
#
|
156
|
+
# @param args [Object]
|
157
|
+
# @return [Hash]
|
158
|
+
# @note symbols will be processed and set as condition. strings
|
159
|
+
# will be set as condition directly. procs will be evaluated
|
160
|
+
def _and(*args)
|
161
|
+
{
|
162
|
+
'Fn::And' => _array(
|
163
|
+
*args.map{|v|
|
164
|
+
if(v.is_a?(Symbol) || v.is_a?(String))
|
165
|
+
_condition(v)
|
166
|
+
else
|
167
|
+
v
|
168
|
+
end
|
169
|
+
}
|
170
|
+
)
|
171
|
+
}
|
172
|
+
end
|
173
|
+
alias_method :and!, :_and
|
174
|
+
|
175
|
+
# Fn::Equals generator
|
176
|
+
#
|
177
|
+
# @param v1 [Object]
|
178
|
+
# @param v2 [Object]
|
179
|
+
# @return [Hash]
|
180
|
+
def _equals(v1, v2)
|
181
|
+
{'Fn::Equals' => _array(v1, v2)}
|
182
|
+
end
|
183
|
+
alias_method :equals!, :_equals
|
184
|
+
|
185
|
+
# Fn::Not generator
|
186
|
+
#
|
187
|
+
# @param arg [Object]
|
188
|
+
# @return [Hash]
|
189
|
+
def _not(arg)
|
190
|
+
if(arg.is_a?(String) || arg.is_a?(Symbol))
|
191
|
+
arg = _condition(arg)
|
192
|
+
else
|
193
|
+
arg = _array(arg).first
|
194
|
+
end
|
195
|
+
{'Fn::Not' => [arg]}
|
196
|
+
end
|
197
|
+
alias_method :not!, :_not
|
198
|
+
|
199
|
+
# Fn::Or generator
|
200
|
+
#
|
201
|
+
# @param v1 [Object]
|
202
|
+
# @param v2 [Object]
|
203
|
+
# @return [Hash]
|
204
|
+
def _or(*args)
|
205
|
+
{
|
206
|
+
'Fn::Or' => _array(
|
207
|
+
*args.map{|v|
|
208
|
+
if(v.is_a?(Symbol) || v.is_a?(String))
|
209
|
+
_condition(v)
|
210
|
+
else
|
211
|
+
v
|
212
|
+
end
|
213
|
+
}
|
214
|
+
)
|
215
|
+
}
|
216
|
+
end
|
217
|
+
alias_method :or!, :_or
|
218
|
+
|
219
|
+
# No value generator
|
220
|
+
#
|
221
|
+
# @return [String]
|
222
|
+
def _no_value
|
223
|
+
_ref('AWS::NoValue')
|
224
|
+
end
|
225
|
+
alias_method :no_value!, :_no_value
|
226
|
+
|
227
|
+
# Region generator
|
228
|
+
#
|
229
|
+
# @return [Hash]
|
230
|
+
def _region
|
231
|
+
_ref('AWS::Region')
|
232
|
+
end
|
233
|
+
alias_method :region!, :_region
|
234
|
+
|
235
|
+
# Notification ARNs generator
|
236
|
+
#
|
237
|
+
# @return [Hash]
|
238
|
+
def _notification_arns
|
239
|
+
_ref('AWS::NotificationARNs')
|
240
|
+
end
|
241
|
+
alias_method :notification_arns!, :_notification_arns
|
242
|
+
|
243
|
+
# Account ID generator
|
244
|
+
#
|
245
|
+
# @return [Hash]
|
246
|
+
def _account_id
|
247
|
+
_ref('AWS::AccountId')
|
248
|
+
end
|
249
|
+
alias_method :account_id!, :_account_id
|
250
|
+
|
251
|
+
# Stack ID generator
|
252
|
+
#
|
253
|
+
# @return [Hash]
|
254
|
+
def _stack_id
|
255
|
+
_ref('AWS::StackId')
|
256
|
+
end
|
257
|
+
alias_method :stack_id!, :_stack_id
|
258
|
+
|
259
|
+
# Stack name generator
|
260
|
+
#
|
261
|
+
# @return [Hash]
|
262
|
+
def _stack_name
|
263
|
+
_ref('AWS::StackName')
|
264
|
+
end
|
265
|
+
alias_method :stack_name!, :_stack_name
|
266
|
+
|
267
|
+
# Resource dependency generator
|
268
|
+
#
|
269
|
+
# @param [Symbol, String, Array<Symbol, String>] resource names
|
270
|
+
# @return [Array<String>]
|
271
|
+
def _depends_on(*args)
|
272
|
+
_set('DependsOn', [args].flatten.compact.map{|s| _process_key(s)})
|
273
|
+
end
|
274
|
+
alias_method :depends_on!, :_depends_on
|
275
|
+
|
276
|
+
# Reference output value from nested stack
|
277
|
+
#
|
278
|
+
# @param stack_name [String, Symbol] logical resource name of stack
|
279
|
+
# @apram output_name [String, Symbol] stack output name
|
280
|
+
def _stack_output(stack_name, output_name)
|
281
|
+
_cf_attr(_process_key(stack_name), "Outputs.#{_process_key(output_name)}")
|
282
|
+
end
|
283
|
+
alias_method :stack_output!, :_stack_output
|
284
|
+
|
285
|
+
# @return [TrueClass, FalseClass] resource can be tagged
|
286
|
+
def taggable?
|
287
|
+
if(self[:type])
|
288
|
+
resource = _self.lookup(self[:type].gsub('::', '_').downcase)
|
289
|
+
resource && resource[:properties].include?('Tags')
|
290
|
+
else
|
291
|
+
if(_parent)
|
292
|
+
_parent.taggable?
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Set tags on a resource
|
298
|
+
#
|
299
|
+
# @param hash [Hash] Key/value pair tags
|
300
|
+
# @return [SparkleStruct]
|
301
|
+
def _tags(hash)
|
302
|
+
_set('Tags',
|
303
|
+
hash.map{ |k, v|
|
304
|
+
key = k.is_a?(Symbol) ? _process_key(k, :force) : k
|
305
|
+
{'Key' => key, 'Value' => v}
|
306
|
+
}
|
307
|
+
)
|
308
|
+
end
|
309
|
+
alias_method :tags!, :_tags
|
310
|
+
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|
314
|
+
end
|