Improve validation for some functions

Move validation code to __init__ method for these list of
functions: Removed, Repeat, Yaql, so now validation error will
be found during template.parse() and returned with a path
to the function in template. Don't change validation for
GetAttr, because it resolves some arguments and we need to parse
template before we can use resolve().
Also, remove check for Removed function in translation, because
now we can't have Removed in properties.

Change-Id: I4f2e1ebee37e82badcdca9110cadc649aa01be8c
This commit is contained in:
Oleksii Chuprykov 2016-06-21 18:32:11 +03:00
parent e660cea228
commit 36bf170f34
5 changed files with 19 additions and 65 deletions

View File

@ -518,10 +518,9 @@ class Removed(function.Function):
Check the HOT guide for an equivalent native function.
"""
def validate(self):
def __init__(self, stack, fn_name, args):
exp = (_("The function %s is not supported in this version of HOT.") %
self.fn_name)
fn_name)
raise exception.InvalidTemplateVersion(explanation=exp)
def result(self):
@ -561,10 +560,9 @@ class Repeat(function.Function):
for_each:
%var%: ['a', 'b', 'c']''')
raise KeyError(_('"repeat" syntax should be %s') % example)
self.validate_args()
def validate(self):
super(Repeat, self).validate()
def validate_args(self):
if not isinstance(self._for_each, function.Function):
if not isinstance(self._for_each, collections.Mapping):
raise TypeError(_('The "for_each" argument to "%s" must '
@ -762,6 +760,7 @@ class Yaql(function.Function):
var1: [3, 2, 1]''') % self.fn_name
raise KeyError(_('"%(name)s" syntax should be %(example)s') % {
'name': self.fn_name, 'example': example})
self.validate_args()
def validate_expression(self, expression):
try:
@ -769,8 +768,7 @@ class Yaql(function.Function):
except exceptions.YaqlException as yex:
raise ValueError(_('Bad expression %s.') % yex)
def validate(self):
super(Yaql, self).validate()
def validate_args(self):
if not isinstance(self._data,
(collections.Mapping, function.Function)):
raise TypeError(_('The "data" argument to "%s" must contain '

View File

@ -340,7 +340,8 @@ def parse(functions, stack, snippet, path=''):
try:
path = '.'.join([path, fn_name])
return Func(stack, fn_name, recurse(args, path))
except (ValueError, TypeError, KeyError) as e:
except (ValueError, TypeError, KeyError,
exception.InvalidTemplateVersion) as e:
raise exception.StackValidationFailed(
path=path,
message=six.text_type(e))

View File

@ -185,8 +185,6 @@ class TranslationRule(object):
# 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)):
try:
return function.resolve(param)

View File

@ -914,8 +914,9 @@ class HOTemplateTest(common.HeatTestCase):
snippet = {'yaql': {'expression': '$.data.var1.sum()',
'data': 'mustbeamap'}}
tmpl = template.Template(hot_newton_tpl_empty)
msg = 'The "data" argument to "yaql" must contain a map.'
self.assertRaisesRegexp(TypeError, msg, self.resolve, snippet, tmpl)
msg = '.yaql: The "data" argument to "yaql" must contain a map.'
self.assertRaisesRegexp(exception.StackValidationFailed,
msg, self.resolve, snippet, tmpl)
def test_yaql_bogus_keys(self):
snippet = {'yaql': {'expression': '1 + 3',
@ -943,8 +944,8 @@ class HOTemplateTest(common.HeatTestCase):
snippet = {'yaql': {'expression': 'invalid(',
'data': {'var1': [1, 2, 3, 4]}}}
tmpl = template.Template(hot_newton_tpl_empty)
yaql = tmpl.parse(None, snippet)
self.assertRaises(ValueError, function.validate, yaql)
self.assertRaises(exception.StackValidationFailed,
tmpl.parse, None, snippet)
def test_yaql_data_as_function(self):
snippet = {'yaql': {'expression': '$.data.var1.len()',
@ -1086,7 +1087,8 @@ class HOTemplateTest(common.HeatTestCase):
# value given to for_each entry is not a list
snippet = {'repeat': {'template': 'this is %var%',
'for_each': {'%var%': 'a'}}}
self.assertRaises(TypeError, self.resolve, snippet, tmpl)
self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, tmpl)
# misspelled template
snippet = {'repeat': {'templte': 'this is %var%',
@ -1100,8 +1102,8 @@ class HOTemplateTest(common.HeatTestCase):
# for_each is not a map
snippet = {'repeat': {'template': 'this is %var%',
'for_each': '%var%'}}
repeat = tmpl.parse(None, snippet)
self.assertRaises(TypeError, function.validate, repeat)
self.assertRaises(exception.StackValidationFailed,
tmpl.parse, None, snippet)
def test_digest(self):
snippet = {'digest': ['md5', 'foobar']}
@ -1334,9 +1336,8 @@ class HOTemplateTest(common.HeatTestCase):
snippet = {'Fn::GetAZs': ''}
stack = parser.Stack(utils.dummy_context(), 'test_stack',
template.Template(hot_juno_tpl_empty))
error = self.assertRaises(exception.InvalidTemplateVersion,
function.validate,
stack.t.parse(stack, snippet))
error = self.assertRaises(exception.StackValidationFailed,
stack.t.parse, stack, snippet)
self.assertIn(next(iter(snippet)), six.text_type(error))
def test_add_resource(self):

View File

@ -807,50 +807,6 @@ class TestTranslationRule(common.HeatTestCase):
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 = translation.TranslationRule(
props,
translation.TranslationRule.REPLACE,
['far', 'bar'],
value_name='dar')
rule.execute_rule()
self.assertEqual([param], props.data.get('far'))
def test_property_no_translation_if_user_parameter_missing(self):
"""Test translation in the case of missing parameter"""
schema = {