Allow functions to calculate dependencies
Change-Id: Ic58f54612d3707abc8727a65a28b9c4502eae31a
This commit is contained in:
parent
95e4b7c548
commit
ce9a99ab79
@ -12,6 +12,7 @@
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import itertools
|
||||
import json
|
||||
|
||||
import six
|
||||
@ -108,6 +109,10 @@ class ResourceRef(function.Function):
|
||||
raise exception.InvalidTemplateReference(resource=resource_name,
|
||||
key=path)
|
||||
|
||||
def dependencies(self, path):
|
||||
return itertools.chain(super(ResourceRef, self).dependencies(path),
|
||||
[self._resource(path)])
|
||||
|
||||
def result(self):
|
||||
return self._resource().FnGetRefId()
|
||||
|
||||
@ -164,6 +169,10 @@ class GetAtt(function.Function):
|
||||
raise exception.InvalidTemplateReference(resource=resource_name,
|
||||
key=path)
|
||||
|
||||
def dependencies(self, path):
|
||||
return itertools.chain(super(GetAtt, self).dependencies(path),
|
||||
[self._resource(path)])
|
||||
|
||||
def result(self):
|
||||
attribute = function.resolve(self._attribute)
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
import abc
|
||||
import collections
|
||||
import itertools
|
||||
|
||||
|
||||
class Function(object):
|
||||
@ -54,6 +55,9 @@ class Function(object):
|
||||
"""
|
||||
return {self.fn_name: self.args}
|
||||
|
||||
def dependencies(self, path):
|
||||
return dependencies(self.args, '.'.join([path, self.fn_name]))
|
||||
|
||||
def __reduce__(self):
|
||||
"""
|
||||
Return a representation of the function suitable for pickling.
|
||||
@ -130,3 +134,35 @@ def validate(snippet):
|
||||
isinstance(snippet, collections.Iterable)):
|
||||
for v in snippet:
|
||||
validate(v)
|
||||
|
||||
|
||||
def dependencies(snippet, path=''):
|
||||
"""
|
||||
Return an iterator over Resource dependencies in a template snippet.
|
||||
|
||||
The snippet should be already parsed to insert Function objects where
|
||||
appropriate.
|
||||
"""
|
||||
|
||||
if isinstance(snippet, Function):
|
||||
return snippet.dependencies(path)
|
||||
|
||||
elif isinstance(snippet, collections.Mapping):
|
||||
def mkpath(key):
|
||||
return '.'.join([path, unicode(key)])
|
||||
|
||||
deps = (dependencies(value,
|
||||
mkpath(key)) for key, value in snippet.items())
|
||||
return itertools.chain.from_iterable(deps)
|
||||
|
||||
elif (not isinstance(snippet, basestring) and
|
||||
isinstance(snippet, collections.Iterable)):
|
||||
def mkpath(idx):
|
||||
return ''.join([path, '[%d]' % idx])
|
||||
|
||||
deps = (dependencies(value,
|
||||
mkpath(i)) for i, value in enumerate(snippet))
|
||||
return itertools.chain.from_iterable(deps)
|
||||
|
||||
else:
|
||||
return []
|
||||
|
@ -22,6 +22,9 @@ class TestFunction(function.Function):
|
||||
if len(self.args) < 2:
|
||||
raise Exception(_('Need more arguments'))
|
||||
|
||||
def dependencies(self, path):
|
||||
return ['foo', 'bar']
|
||||
|
||||
def result(self):
|
||||
return 'wibble'
|
||||
|
||||
@ -122,3 +125,20 @@ class ValidateTest(HeatTestCase):
|
||||
snippet = {'foo': 'bar', 'blarg': self.func}
|
||||
ex = self.assertRaises(Exception, function.validate, snippet)
|
||||
self.assertEqual('Need more arguments', str(ex))
|
||||
|
||||
|
||||
class DependenciesTest(HeatTestCase):
|
||||
func = TestFunction(None, 'test', None)
|
||||
|
||||
scenarios = [
|
||||
('function', dict(snippet=func)),
|
||||
('nested_map', dict(snippet={'wibble': func})),
|
||||
('nested_list', dict(snippet=['wibble', func])),
|
||||
('deep_nested', dict(snippet=[{'wibble': ['wibble', func]}])),
|
||||
]
|
||||
|
||||
def test_dependencies(self):
|
||||
deps = list(function.dependencies(self.snippet))
|
||||
self.assertIn('foo', deps)
|
||||
self.assertIn('bar', deps)
|
||||
self.assertEqual(2, len(deps))
|
||||
|
Loading…
Reference in New Issue
Block a user