Fix some map_replace issues

While testing I discovered a couple of corner cases not previously
handled:
- If you provide values/keys via a get_attr reference it's possible for
them to be None during validation
- If the input map has an unhashable value, it breaks the values replacement
so we need to tolerate a failure to lookup an unhashable key in the values
data.

Change-Id: I14d92056e0a07816a216aba752711887e8ac0aa5
This commit is contained in:
Steven Hardy 2016-07-21 18:53:16 +01:00
parent 849868d641
commit f019fb002e
3 changed files with 35 additions and 3 deletions

View File

@ -1329,6 +1329,10 @@ The keys/values mappings are optional, either or both may be specified.
Note that an error is raised if a replacement defined in "keys" results
in a collision with an existing keys in the input or output map.
Also note that while unhashable values (e.g lists) in the input map are valid,
they will be ignored by the values replacement, because no key can be defined
in the values mapping to define their replacement.
yaql
----
The ``yaql`` evaluates yaql expression on a given data.

View File

@ -542,8 +542,8 @@ class MapReplace(function.Function):
raise ValueError(_('Incorrect arguments to "%(fn_name)s" '
'should be: %(example)s') % self.fmt_data)
repl_keys = repl_map.get('keys', {})
repl_values = repl_map.get('values', {})
repl_keys = ensure_map(repl_map.get('keys', {}))
repl_values = ensure_map(repl_map.get('values', {}))
ret_map = {}
for k, v in six.iteritems(in_map):
key = repl_keys.get(k)
@ -559,7 +559,11 @@ class MapReplace(function.Function):
msg = _('key replacement %s collides with '
'a key in the output map')
raise ValueError(msg % key)
value = repl_values.get(v, v)
try:
value = repl_values.get(v, v)
except TypeError:
# If the value is unhashable, we get here
value = v
ret_map[key] = value
return ret_map

View File

@ -926,6 +926,30 @@ class HOTemplateTest(common.HeatTestCase):
self.assertEqual({'f1': 'b1', 'F2': 'b2'},
resolved)
def test_map_replace_none_values(self):
snippet = {'map_replace': [{'f1': 'b1', 'f2': 'b2'},
{'values': None}]}
tmpl = template.Template(hot_newton_tpl_empty)
resolved = self.resolve(snippet, tmpl)
self.assertEqual({'f1': 'b1', 'f2': 'b2'},
resolved)
def test_map_replace_none_keys(self):
snippet = {'map_replace': [{'f1': 'b1', 'f2': 'b2'},
{'keys': None}]}
tmpl = template.Template(hot_newton_tpl_empty)
resolved = self.resolve(snippet, tmpl)
self.assertEqual({'f1': 'b1', 'f2': 'b2'},
resolved)
def test_map_replace_unhashable_value(self):
snippet = {'map_replace': [{'f1': 'b1', 'f2': []},
{'values': {}}]}
tmpl = template.Template(hot_newton_tpl_empty)
resolved = self.resolve(snippet, tmpl)
self.assertEqual({'f1': 'b1', 'f2': []},
resolved)
def test_map_replace_keys_collide(self):
snippet = {'map_replace': [{'f1': 'b1', 'f2': 'b2'},
{'keys': {'f2': 'f1'}}]}