diff --git a/heat/engine/hot/functions.py b/heat/engine/hot/functions.py index 3f7cb7b7c1..66025bafb6 100644 --- a/heat/engine/hot/functions.py +++ b/heat/engine/hot/functions.py @@ -878,15 +878,19 @@ class Repeat(function.Function): def result(self): for_each = function.resolve(self._for_each) - if not all(self._valid_arg(l) for l in for_each.values()): + keys, lists = six.moves.zip(*for_each.items()) + + # use empty list for references(None) else validation will fail + values = [[] if value is None else value for value in lists] + + if not all(self._valid_arg(l) for l in values): raise TypeError(_('The values of the "for_each" argument to ' '"%s" must be lists') % self.fn_name) template = function.resolve(self._template) - keys, lists = six.moves.zip(*for_each.items()) return [self._do_replacement(keys, replacements, template) - for replacements in itertools.product(*lists)] + for replacements in itertools.product(*values)] class RepeatWithMap(Repeat): diff --git a/heat/tests/test_hot.py b/heat/tests/test_hot.py index 62ef710ca6..faa29d3048 100644 --- a/heat/tests/test_hot.py +++ b/heat/tests/test_hot.py @@ -956,6 +956,14 @@ class HOTemplateTest(common.HeatTestCase): self.assertEqual('role1', resolved['role1']) self.assertEqual('role2', resolved['role2']) + def test_merge_containing_repeat_with_none(self): + snippet = {'map_merge': {'repeat': {'template': {'ROLE': 'ROLE'}, + 'for_each': {'ROLE': None}}}} + tmpl = template.Template(hot_mitaka_tpl_empty) + resolved = self.resolve(snippet, tmpl) + + self.assertEqual({}, resolved) + def test_map_replace(self): snippet = {'map_replace': [{'f1': 'b1', 'f2': 'b2'}, {'keys': {'f1': 'F1'},