Allow paths as lists in function.validate()

Previously when calling function.validate() we passed the path to the
function in the template (used for debugging purposes) as a string. This
change allows us to pass it as a list of path components, as we do
elsewhere, so that higher-level code that catches StackValidationFailed
can deal with its components separately.

Change-Id: I017aa6f7511b8478ef8273522ab8087684ae71c6
This commit is contained in:
Zane Bitter 2017-11-16 14:18:11 -05:00
parent 38f1975cf7
commit cb80df4f4a
3 changed files with 28 additions and 27 deletions

View File

@ -267,29 +267,30 @@ def resolve(snippet):
return snippet return snippet
def validate(snippet, path=''): def validate(snippet, path=None):
if path is None:
path = []
elif isinstance(path, six.string_types):
path = [path]
if isinstance(snippet, Function): if isinstance(snippet, Function):
try: try:
snippet.validate() snippet.validate()
except AssertionError: except AssertionError:
raise raise
except Exception as e: except Exception as e:
path = '.'.join([path, snippet.fn_name])
raise exception.StackValidationFailed( raise exception.StackValidationFailed(
path=path, message=six.text_type(e)) path=path + [snippet.fn_name],
message=six.text_type(e))
elif isinstance(snippet, collections.Mapping): elif isinstance(snippet, collections.Mapping):
def mkpath(key):
return '.'.join([path, key])
for k, v in six.iteritems(snippet): for k, v in six.iteritems(snippet):
validate(v, mkpath(k)) validate(v, path + [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)):
def mkpath(indx): basepath = list(path)
return '.'.join([path, '[%d]' % indx]) parent = basepath.pop() if basepath else ''
for i, v in enumerate(snippet): for i, v in enumerate(snippet):
validate(v, mkpath(i)) validate(v, basepath + ['%s[%d]' % (parent, i)])
def dependencies(snippet, path=''): def dependencies(snippet, path=''):

View File

@ -180,7 +180,7 @@ class ValidateTest(common.HeatTestCase):
self.assertIsNone(function.validate(self.func)) self.assertIsNone(function.validate(self.func))
self.func = TestFunction(None, 'foo', ['bar']) self.func = TestFunction(None, 'foo', ['bar'])
self.assertRaisesRegex(exception.StackValidationFailed, self.assertRaisesRegex(exception.StackValidationFailed,
'.foo: Need more arguments', 'foo: Need more arguments',
function.validate, self.func) function.validate, self.func)
def test_validate_dict(self): def test_validate_dict(self):
@ -190,7 +190,7 @@ 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}
self.assertRaisesRegex(exception.StackValidationFailed, self.assertRaisesRegex(exception.StackValidationFailed,
'.blarg.foo: Need more arguments', 'blarg.foo: Need more arguments',
function.validate, snippet) function.validate, snippet)
def test_validate_list(self): def test_validate_list(self):
@ -200,7 +200,7 @@ 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}
self.assertRaisesRegex(exception.StackValidationFailed, self.assertRaisesRegex(exception.StackValidationFailed,
'.blarg.foo: Need more arguments', 'blarg.foo: Need more arguments',
function.validate, snippet) function.validate, snippet)
def test_validate_all(self): def test_validate_all(self):
@ -210,7 +210,7 @@ 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}
self.assertRaisesRegex(exception.StackValidationFailed, self.assertRaisesRegex(exception.StackValidationFailed,
'.blarg.foo: Need more arguments', 'blarg.foo: Need more arguments',
function.validate, snippet) function.validate, snippet)

View File

@ -864,7 +864,7 @@ class HOTemplateTest(common.HeatTestCase):
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, tmpl) self.resolve, snippet, tmpl)
self.assertIn('.str_replace: "str_replace" parameters must be a' self.assertIn('str_replace: "str_replace" parameters must be a'
' mapping', six.text_type(ex)) ' mapping', six.text_type(ex))
def test_str_replace_invalid_param_type_init(self): def test_str_replace_invalid_param_type_init(self):
@ -1027,13 +1027,13 @@ class HOTemplateTest(common.HeatTestCase):
l_tmpl = template.Template(hot_liberty_tpl_empty) l_tmpl = template.Template(hot_liberty_tpl_empty)
exc = self.assertRaises(exception.StackValidationFailed, exc = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, l_tmpl) self.resolve, snippet, l_tmpl)
self.assertIn('.list_join: Incorrect arguments to "list_join"', self.assertIn('list_join: Incorrect arguments to "list_join"',
six.text_type(exc)) six.text_type(exc))
k_tmpl = template.Template(hot_kilo_tpl_empty) k_tmpl = template.Template(hot_kilo_tpl_empty)
exc1 = self.assertRaises(exception.StackValidationFailed, exc1 = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, k_tmpl) self.resolve, snippet, k_tmpl)
self.assertIn('.list_join: Incorrect arguments to "list_join"', self.assertIn('list_join: Incorrect arguments to "list_join"',
six.text_type(exc1)) six.text_type(exc1))
def test_join_int_invalid(self): def test_join_int_invalid(self):
@ -1041,25 +1041,25 @@ class HOTemplateTest(common.HeatTestCase):
l_tmpl = template.Template(hot_liberty_tpl_empty) l_tmpl = template.Template(hot_liberty_tpl_empty)
exc = self.assertRaises(exception.StackValidationFailed, exc = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, l_tmpl) self.resolve, snippet, l_tmpl)
self.assertIn('.list_join: Incorrect arguments', six.text_type(exc)) self.assertIn('list_join: Incorrect arguments', six.text_type(exc))
k_tmpl = template.Template(hot_kilo_tpl_empty) k_tmpl = template.Template(hot_kilo_tpl_empty)
exc1 = self.assertRaises(exception.StackValidationFailed, exc1 = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, k_tmpl) self.resolve, snippet, k_tmpl)
self.assertIn('.list_join: Incorrect arguments', six.text_type(exc1)) self.assertIn('list_join: Incorrect arguments', six.text_type(exc1))
def test_join_invalid_value(self): def test_join_invalid_value(self):
snippet = {'list_join': [',']} snippet = {'list_join': [',']}
l_tmpl = template.Template(hot_liberty_tpl_empty) l_tmpl = template.Template(hot_liberty_tpl_empty)
exc = self.assertRaises(exception.StackValidationFailed, exc = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, l_tmpl) self.resolve, snippet, l_tmpl)
self.assertIn('.list_join: Incorrect arguments to "list_join"', self.assertIn('list_join: Incorrect arguments to "list_join"',
six.text_type(exc)) six.text_type(exc))
k_tmpl = template.Template(hot_kilo_tpl_empty) k_tmpl = template.Template(hot_kilo_tpl_empty)
exc1 = self.assertRaises(exception.StackValidationFailed, exc1 = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, k_tmpl) self.resolve, snippet, k_tmpl)
self.assertIn('.list_join: Incorrect arguments to "list_join"', self.assertIn('list_join: Incorrect arguments to "list_join"',
six.text_type(exc1)) six.text_type(exc1))
def test_join_invalid_multiple(self): def test_join_invalid_multiple(self):
@ -1269,7 +1269,7 @@ class HOTemplateTest(common.HeatTestCase):
def test_yaql_non_map_args(self): def test_yaql_non_map_args(self):
snippet = {'yaql': 'invalid'} snippet = {'yaql': 'invalid'}
tmpl = template.Template(hot_newton_tpl_empty) tmpl = template.Template(hot_newton_tpl_empty)
msg = '.yaql: Arguments to "yaql" must be a map.' msg = 'yaql: Arguments to "yaql" must be a map.'
self.assertRaisesRegex(exception.StackValidationFailed, self.assertRaisesRegex(exception.StackValidationFailed,
msg, self.resolve, snippet, tmpl) msg, self.resolve, snippet, tmpl)
@ -1278,7 +1278,7 @@ 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)
regxp = ('.yaql: Bad expression Parse error: unexpected end ' regxp = ('yaql: Bad expression Parse error: unexpected end '
'of statement.') 'of statement.')
self.assertRaisesRegex(exception.StackValidationFailed, regxp, self.assertRaisesRegex(exception.StackValidationFailed, regxp,
function.validate, yaql) function.validate, yaql)
@ -1363,7 +1363,7 @@ class HOTemplateTest(common.HeatTestCase):
exc = self.assertRaises(exception.StackValidationFailed, exc = self.assertRaises(exception.StackValidationFailed,
self.resolve_condition, snippet, tmpl) self.resolve_condition, snippet, tmpl)
error_msg = ('.equals: Arguments to "equals" must be ' error_msg = ('equals: Arguments to "equals" must be '
'of the form: [value_1, value_2]') 'of the form: [value_1, value_2]')
self.assertIn(error_msg, six.text_type(exc)) self.assertIn(error_msg, six.text_type(exc))
@ -1715,7 +1715,7 @@ resources:
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)
regxp = ('.repeat: The "for_each" argument to "repeat" ' regxp = ('repeat: The "for_each" argument to "repeat" '
'must contain a map') 'must contain a map')
self.assertRaisesRegex(exception.StackValidationFailed, regxp, self.assertRaisesRegex(exception.StackValidationFailed, regxp,
function.validate, repeat) function.validate, repeat)
@ -1968,7 +1968,7 @@ resources:
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))
regxp = '.Fn::GetAZs: The template version is invalid' regxp = 'Fn::GetAZs: The template version is invalid'
self.assertRaisesRegex(exception.StackValidationFailed, self.assertRaisesRegex(exception.StackValidationFailed,
regxp, regxp,
function.validate, function.validate,