Fix translating for props with get_param value

If some map or list type properties specified with
json-type or commadelimitedlist parameters, error
raised in case of wrong properties data parsing.
Fix this case by adding check if data is GetParam
instance, resolve it. Other function can be safely
replaced without resolve.

Change-Id: I0c9a6af29b56b629cbdad2acb868c3033e38b5ef
Closes-bug: #1494364
(cherry picked from commit aea59ecdac)
This commit is contained in:
Peter Razumovsky 2015-09-14 17:43:49 +03:00 committed by Steve Baker
parent 1ea044aa86
commit 6c3fa4e25d
2 changed files with 207 additions and 17 deletions

View File

@ -18,8 +18,10 @@ import six
from heat.common import exception
from heat.common.i18n import _
from heat.engine.cfn import functions as cfn_funcs
from heat.engine import constraints as constr
from heat.engine import function
from heat.engine.hot import functions as hot_funcs
from heat.engine.hot import parameters as hot_param
from heat.engine import parameters
from heat.engine import support
@ -659,17 +661,20 @@ class TranslationRule(object):
raise ValueError(_('value must be list type when rule is Add.'))
def execute_rule(self):
(source_key, source_data) = self.get_data_from_source_path(
self.source_path)
if self.value_path:
(value_key, value_data) = self.get_data_from_source_path(
self.value_path)
value = (value_data[value_key]
if value_data and value_data.get(value_key)
else self.value)
else:
(value_key, value_data) = None, None
value = self.value
try:
(source_key, source_data) = self.get_data_from_source_path(
self.source_path)
if self.value_path:
(value_key, value_data) = self.get_data_from_source_path(
self.value_path)
value = (value_data[value_key]
if value_data and value_data.get(value_key)
else self.value)
else:
(value_key, value_data) = None, None
value = self.value
except AttributeError:
return
if (source_data is None or (self.rule != self.DELETE and
(value is None and
@ -728,16 +733,37 @@ class TranslationRule(object):
for k, s in schemata.items())
return props
def resolve_param(param):
"""Check whether if given item is param and resolve, if it is."""
# NOTE(prazumovsky): If property uses removed in HOT function,
# we should not translate it for correct validating and raising
# validation error.
if isinstance(param, hot_funcs.Removed):
raise AttributeError(_('Property uses removed function.'))
if isinstance(param, (hot_funcs.GetParam, cfn_funcs.ParamRef)):
return function.resolve(param)
elif isinstance(param, list):
return [resolve_param(param_item) for param_item in param]
else:
return param
source_key = path[0]
data = self.properties.data
props = self.properties.props
for key in path:
if isinstance(data, list):
source_key = key
elif data.get(key) is not None and isinstance(data.get(key),
(list, dict)):
data = data.get(key)
props = get_props(props, key)
elif data.get(key) is not None:
# NOTE(prazumovsky): There's no need to resolve other functions
# because we can translate all function to another path. But if
# list or map type property equals to get_param function, need
# to resolve it for correct translating.
data[key] = resolve_param(data[key])
if isinstance(data[key], (dict, list)):
data = data[key]
props = get_props(props, key)
else:
source_key = key
elif data.get(key) is None:
if (self.rule == TranslationRule.DELETE or
(self.rule == TranslationRule.REPLACE and
@ -752,6 +778,4 @@ class TranslationRule(object):
continue
data = data.get(key)
props = get_props(props, key)
else:
source_key = key
return source_key, data

View File

@ -18,6 +18,7 @@ import six
from heat.common import exception
from heat.engine.cfn import functions as cfn_funcs
from heat.engine import constraints
from heat.engine.hot import functions as hot_funcs
from heat.engine.hot import parameters as hot_param
from heat.engine import parameters
from heat.engine import plugin_manager
@ -2289,3 +2290,168 @@ class TestTranslationRule(common.HeatTestCase):
rule.execute_rule()
self.assertIsNone(props.get('far'))
def test_property_json_param_correct_translation(self):
"""Test case when property with sub-schema takes json param."""
schema = {
'far': properties.Schema(properties.Schema.MAP,
schema={
'bar': properties.Schema(
properties.Schema.STRING,
),
'dar': properties.Schema(
properties.Schema.STRING
)
})
}
class DummyStack(dict):
@property
def parameters(self):
return mock.Mock()
param = hot_funcs.GetParam(DummyStack(json_far='json_far'),
'get_param',
'json_far')
param.parameters = {
'json_far': parameters.JsonParam(
'json_far',
{'Type': 'Json'},
'{"dar": "rad"}').value()}
data = {'far': param}
props = properties.Properties(schema, data)
rule = properties.TranslationRule(props,
properties.TranslationRule.REPLACE,
['far', 'bar'],
value_path=['far', 'dar'])
rule.execute_rule()
self.assertEqual('rad', props.get('far').get('bar'))
def test_property_json_param_to_list_correct_translation(self):
"""Test case when list property with sub-schema takes json param."""
schema = {
'far': properties.Schema(properties.Schema.LIST,
schema=properties.Schema(
properties.Schema.MAP,
schema={
'bar': properties.Schema(
properties.Schema.STRING,
),
'dar': properties.Schema(
properties.Schema.STRING
)
}
))
}
class DummyStack(dict):
@property
def parameters(self):
return mock.Mock()
param = hot_funcs.GetParam(DummyStack(json_far='json_far'),
'get_param',
'json_far')
param.parameters = {
'json_far': parameters.JsonParam(
'json_far',
{'Type': 'Json'},
'{"dar": "rad"}').value()}
data = {'far': [param]}
props = properties.Properties(schema, data)
rule = properties.TranslationRule(props,
properties.TranslationRule.REPLACE,
['far', 'bar'],
value_name='dar')
rule.execute_rule()
self.assertEqual([{'dar': None, 'bar': 'rad'}], props.get('far'))
def test_property_commadelimitedlist_param_correct_translation(self):
"""Test when property with sub-schema takes comma_delimited_list."""
schema = {
'far': properties.Schema(
properties.Schema.LIST,
schema=properties.Schema(
properties.Schema.STRING,
)
),
'boo': properties.Schema(
properties.Schema.STRING
)}
class DummyStack(dict):
@property
def parameters(self):
return mock.Mock()
param = hot_funcs.GetParam(DummyStack(list_far='list_far'),
'get_param',
'list_far')
param.parameters = {
'list_far': parameters.CommaDelimitedListParam(
'list_far',
{'Type': 'CommaDelimitedList'},
"white,roses").value()}
data = {'far': param, 'boo': 'chrysanthemums'}
props = properties.Properties(schema, data)
rule = properties.TranslationRule(props,
properties.TranslationRule.ADD,
['far'],
[props.get('boo')])
rule.execute_rule()
self.assertEqual(['white', 'roses', 'chrysanthemums'],
props.get('far'))
def test_property_no_translation_removed_function(self):
"""Test case when list property with sub-schema takes json param."""
schema = {
'far': properties.Schema(properties.Schema.LIST,
schema=properties.Schema(
properties.Schema.MAP,
schema={
'bar': properties.Schema(
properties.Schema.STRING,
),
'dar': properties.Schema(
properties.Schema.STRING
)
}
))
}
class DummyStack(dict):
@property
def parameters(self):
return mock.Mock()
param = hot_funcs.Removed(DummyStack(json_far='json_far'),
'Ref',
'json_far')
param.parameters = {
'json_far': parameters.JsonParam(
'json_far',
{'Type': 'Json'},
'{"dar": "rad"}').value()}
data = {'far': [param]}
props = properties.Properties(schema, data)
rule = properties.TranslationRule(props,
properties.TranslationRule.REPLACE,
['far', 'bar'],
value_name='dar')
rule.execute_rule()
self.assertEqual([param], props.data.get('far'))