Allow functions to calculate dependencies

Change-Id: Ic58f54612d3707abc8727a65a28b9c4502eae31a
This commit is contained in:
Zane Bitter 2014-04-29 13:07:29 -04:00
parent 95e4b7c548
commit ce9a99ab79
3 changed files with 65 additions and 0 deletions

View File

@ -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)

View File

@ -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 []

View File

@ -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))