Merge "Return error with path in function validate"

This commit is contained in:
Jenkins 2016-09-27 11:36:38 +00:00 committed by Gerrit Code Review
commit bc6fa5dedf
7 changed files with 63 additions and 44 deletions

View File

@ -18,6 +18,7 @@ import weakref
import six import six
from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
@ -198,16 +199,29 @@ def resolve(snippet):
return snippet return snippet
def validate(snippet): def validate(snippet, path=''):
if isinstance(snippet, Function): if isinstance(snippet, Function):
snippet.validate() try:
snippet.validate()
except AssertionError:
raise
except Exception as e:
path = '.'.join([path, snippet.fn_name])
raise exception.StackValidationFailed(
path=path, message=six.text_type(e))
elif isinstance(snippet, collections.Mapping): elif isinstance(snippet, collections.Mapping):
for v in six.itervalues(snippet): def mkpath(key):
validate(v) return '.'.join([path, key])
for k, v in six.iteritems(snippet):
validate(v, mkpath(k))
elif (not isinstance(snippet, six.string_types) and elif (not isinstance(snippet, six.string_types) and
isinstance(snippet, collections.Iterable)): isinstance(snippet, collections.Iterable)):
for v in snippet: def mkpath(indx):
validate(v) return '.'.join([path, '[%d]' % indx])
for i, v in enumerate(snippet):
validate(v, mkpath(i))
def dependencies(snippet, path=''): def dependencies(snippet, path=''):

View File

@ -25,9 +25,9 @@ class OutputDefinition(object):
self._resolved_value = None self._resolved_value = None
self._description = description self._description = description
def validate(self): def validate(self, path=''):
"""Validate the output value without resolving it.""" """Validate the output value without resolving it."""
function.validate(self._value) function.validate(self._value, path)
def dep_attrs(self, resource_name): def dep_attrs(self, resource_name):
"""Iterate over attributes of a given resource that this references. """Iterate over attributes of a given resource that this references.

View File

@ -1501,8 +1501,8 @@ class Resource(object):
self.stack.context, self.stack.context,
self.t.resource_type self.t.resource_type
) )
path = '.'.join([self.stack.t.RESOURCES, self.name])
function.validate(self.t) function.validate(self.t, path)
self.validate_deletion_policy(self.t.deletion_policy()) self.validate_deletion_policy(self.t.deletion_policy())
self.t.update_policy(self.update_policy_schema, self.t.update_policy(self.update_policy_schema,
self.context).validate() self.context).validate()

View File

@ -838,15 +838,13 @@ class Stack(collections.Mapping):
for op_name, output in six.iteritems(self.outputs): for op_name, output in six.iteritems(self.outputs):
try: try:
output.validate() path = '.'.join([self.t.OUTPUTS, op_name,
except exception.StackValidationFailed as ex: self.t.OUTPUT_VALUE])
output.validate(path)
except exception.StackValidationFailed:
raise raise
except AssertionError: except AssertionError:
raise raise
except Exception as ex:
raise exception.StackValidationFailed(
error='Validation error in output "%s"' % op_name,
message=six.text_type(ex))
def requires_deferred_auth(self): def requires_deferred_auth(self):
"""Determine whether to perform API requests with deferred auth. """Determine whether to perform API requests with deferred auth.

View File

@ -178,8 +178,9 @@ class ValidateTest(common.HeatTestCase):
def test_validate_func(self): def test_validate_func(self):
self.assertIsNone(function.validate(self.func)) self.assertIsNone(function.validate(self.func))
self.func = TestFunction(None, 'foo', ['bar']) self.func = TestFunction(None, 'foo', ['bar'])
ex = self.assertRaises(TypeError, function.validate, self.func) self.assertRaisesRegexp(exception.StackValidationFailed,
self.assertEqual('Need more arguments', six.text_type(ex)) '.foo: Need more arguments',
function.validate, self.func)
def test_validate_dict(self): def test_validate_dict(self):
snippet = {'foo': 'bar', 'blarg': self.func} snippet = {'foo': 'bar', 'blarg': self.func}
@ -187,8 +188,9 @@ class ValidateTest(common.HeatTestCase):
self.func = TestFunction(None, 'foo', ['bar']) self.func = TestFunction(None, 'foo', ['bar'])
snippet = {'foo': 'bar', 'blarg': self.func} snippet = {'foo': 'bar', 'blarg': self.func}
ex = self.assertRaises(TypeError, function.validate, snippet) self.assertRaisesRegexp(exception.StackValidationFailed,
self.assertEqual('Need more arguments', six.text_type(ex)) '.blarg.foo: Need more arguments',
function.validate, snippet)
def test_validate_list(self): def test_validate_list(self):
snippet = ['foo', 'bar', 'baz', 'blarg', self.func] snippet = ['foo', 'bar', 'baz', 'blarg', self.func]
@ -196,8 +198,9 @@ class ValidateTest(common.HeatTestCase):
self.func = TestFunction(None, 'foo', ['bar']) self.func = TestFunction(None, 'foo', ['bar'])
snippet = {'foo': 'bar', 'blarg': self.func} snippet = {'foo': 'bar', 'blarg': self.func}
ex = self.assertRaises(TypeError, function.validate, snippet) self.assertRaisesRegexp(exception.StackValidationFailed,
self.assertEqual('Need more arguments', six.text_type(ex)) '.blarg.foo: Need more arguments',
function.validate, snippet)
def test_validate_all(self): def test_validate_all(self):
snippet = ['foo', {'bar': ['baz', {'blarg': self.func}]}] snippet = ['foo', {'bar': ['baz', {'blarg': self.func}]}]
@ -205,8 +208,9 @@ class ValidateTest(common.HeatTestCase):
self.func = TestFunction(None, 'foo', ['bar']) self.func = TestFunction(None, 'foo', ['bar'])
snippet = {'foo': 'bar', 'blarg': self.func} snippet = {'foo': 'bar', 'blarg': self.func}
ex = self.assertRaises(TypeError, function.validate, snippet) self.assertRaisesRegexp(exception.StackValidationFailed,
self.assertEqual('Need more arguments', six.text_type(ex)) '.blarg.foo: Need more arguments',
function.validate, snippet)
class DependenciesTest(common.HeatTestCase): class DependenciesTest(common.HeatTestCase):

View File

@ -1120,7 +1120,10 @@ class HOTemplateTest(common.HeatTestCase):
'data': {'var1': [1, 2, 3, 4]}}} 'data': {'var1': [1, 2, 3, 4]}}}
tmpl = template.Template(hot_newton_tpl_empty) tmpl = template.Template(hot_newton_tpl_empty)
yaql = tmpl.parse(None, snippet) yaql = tmpl.parse(None, snippet)
self.assertRaises(ValueError, function.validate, yaql) regxp = ('.yaql: Bad expression Parse error: unexpected end '
'of statement.')
self.assertRaisesRegexp(exception.StackValidationFailed, regxp,
function.validate, yaql)
def test_yaql_data_as_function(self): def test_yaql_data_as_function(self):
snippet = {'yaql': {'expression': '$.data.var1.len()', snippet = {'yaql': {'expression': '$.data.var1.len()',
@ -1383,7 +1386,10 @@ conditions:
snippet = {'repeat': {'template': 'this is %var%', snippet = {'repeat': {'template': 'this is %var%',
'for_each': '%var%'}} 'for_each': '%var%'}}
repeat = tmpl.parse(None, snippet) repeat = tmpl.parse(None, snippet)
self.assertRaises(TypeError, function.validate, repeat) regxp = ('.repeat: The "for_each" argument to "repeat" '
'must contain a map')
self.assertRaisesRegexp(exception.StackValidationFailed, regxp,
function.validate, repeat)
def test_digest(self): def test_digest(self):
snippet = {'digest': ['md5', 'foobar']} snippet = {'digest': ['md5', 'foobar']}
@ -1626,10 +1632,11 @@ conditions:
snippet = {'Fn::GetAZs': ''} snippet = {'Fn::GetAZs': ''}
stack = parser.Stack(utils.dummy_context(), 'test_stack', stack = parser.Stack(utils.dummy_context(), 'test_stack',
template.Template(hot_juno_tpl_empty)) template.Template(hot_juno_tpl_empty))
error = self.assertRaises(exception.InvalidTemplateVersion, regxp = '.Fn::GetAZs: The template version is invalid'
function.validate, self.assertRaisesRegexp(exception.StackValidationFailed,
stack.t.parse(stack, snippet)) regxp,
self.assertIn(next(iter(snippet)), six.text_type(error)) function.validate,
stack.t.parse(stack, snippet))
def test_add_resource(self): def test_add_resource(self):
hot_tpl = template_format.parse(''' hot_tpl = template_format.parse('''

View File

@ -1846,13 +1846,11 @@ class StackTest(common.HeatTestCase):
self.stack = stack.Stack(self.ctx, 'stack_with_correct_outputs', self.stack = stack.Stack(self.ctx, 'stack_with_correct_outputs',
template.Template(tmpl)) template.Template(tmpl))
ex = self.assertRaises(exception.StackValidationFailed, self.assertRaisesRegexp(
self.stack.validate) exception.StackValidationFailed,
('Outputs.Resource_attr.Value.Fn::GetAtt: The Referenced '
self.assertEqual('Validation error in output "Resource_attr": ' 'Attribute \(AResource Bar\) is incorrect.'),
'The Referenced Attribute ' self.stack.validate)
'(AResource Bar) is incorrect.',
six.text_type(ex))
def test_incorrect_outputs_cfn_incorrect_reference(self): def test_incorrect_outputs_cfn_incorrect_reference(self):
tmpl = template_format.parse(""" tmpl = template_format.parse("""
@ -2212,13 +2210,11 @@ class StackTest(common.HeatTestCase):
self.stack = stack.Stack(self.ctx, 'stack_with_correct_outputs', self.stack = stack.Stack(self.ctx, 'stack_with_correct_outputs',
template.Template(tmpl)) template.Template(tmpl))
ex = self.assertRaises(exception.StackValidationFailed, self.assertRaisesRegexp(
self.stack.validate) exception.StackValidationFailed,
('outputs.resource_attr.value.get_attr: The Referenced Attribute '
self.assertEqual('Validation error in output "resource_attr": ' '\(AResource Bar\) is incorrect.'),
'The Referenced Attribute ' self.stack.validate)
'(AResource Bar) is incorrect.',
six.text_type(ex))
def test_snapshot_save_called_first(self): def test_snapshot_save_called_first(self):
def snapshotting_called_first(stack, action, status, reason): def snapshotting_called_first(stack, action, status, reason):