Merge "Improve validation during template.parse"
This commit is contained in:
commit
e660cea228
|
@ -109,7 +109,8 @@ class CfnTemplate(template.Template):
|
|||
try:
|
||||
|
||||
for name, snippet in resources.items():
|
||||
data = self.parse(stack, snippet)
|
||||
path = '.'.join([self.RESOURCES, name])
|
||||
data = self.parse(stack, snippet, path)
|
||||
|
||||
if not self.validate_resource_key_type(self.RES_TYPE,
|
||||
six.string_types,
|
||||
|
|
|
@ -197,7 +197,8 @@ class HOTemplate20130523(template.Template):
|
|||
|
||||
try:
|
||||
for name, snippet in resources.items():
|
||||
data = self.parse(stack, snippet)
|
||||
path = '.'.join([self.RESOURCES, name])
|
||||
data = self.parse(stack, snippet, path)
|
||||
|
||||
if not self.validate_resource_key_type(self.RES_TYPE,
|
||||
six.string_types,
|
||||
|
|
|
@ -229,7 +229,8 @@ class Stack(collections.Mapping):
|
|||
self._set_param_stackid()
|
||||
|
||||
if resolve_data:
|
||||
self.outputs = self.resolve_static_data(self.t[self.t.OUTPUTS])
|
||||
self.outputs = self.resolve_static_data(
|
||||
self.t[self.t.OUTPUTS], path=self.t.OUTPUTS)
|
||||
else:
|
||||
self.outputs = {}
|
||||
|
||||
|
@ -1459,7 +1460,8 @@ class Stack(collections.Mapping):
|
|||
previous_template_id = self.t.id
|
||||
self.t = newstack.t
|
||||
template_outputs = self.t[self.t.OUTPUTS]
|
||||
self.outputs = self.resolve_static_data(template_outputs)
|
||||
self.outputs = self.resolve_static_data(
|
||||
template_outputs, path=self.t.OUTPUTS)
|
||||
finally:
|
||||
if should_rollback:
|
||||
# Already handled in rollback task
|
||||
|
@ -1916,14 +1918,8 @@ class Stack(collections.Mapping):
|
|||
'tags': self.tags,
|
||||
}
|
||||
|
||||
def resolve_static_data(self, snippet):
|
||||
try:
|
||||
return self.t.parse(self, snippet)
|
||||
except AssertionError:
|
||||
raise
|
||||
except Exception as ex:
|
||||
raise exception.StackValidationFailed(
|
||||
message=encodeutils.safe_decode(six.text_type(ex)))
|
||||
def resolve_static_data(self, snippet, path=''):
|
||||
return self.t.parse(self, snippet, path=path)
|
||||
|
||||
def reset_resource_attributes(self):
|
||||
# nothing is cached if no resources exist
|
||||
|
|
|
@ -257,8 +257,8 @@ class Template(collections.Mapping):
|
|||
if self.RESOURCES in self.t:
|
||||
self.t.update({self.RESOURCES: {}})
|
||||
|
||||
def parse(self, stack, snippet):
|
||||
return parse(self.functions, stack, snippet)
|
||||
def parse(self, stack, snippet, path=''):
|
||||
return parse(self.functions, stack, snippet, path)
|
||||
|
||||
def validate(self):
|
||||
"""Validate the template.
|
||||
|
@ -326,18 +326,33 @@ class Template(collections.Mapping):
|
|||
return cls(tmpl)
|
||||
|
||||
|
||||
def parse(functions, stack, snippet):
|
||||
def parse(functions, stack, snippet, path=''):
|
||||
recurse = functools.partial(parse, functions, stack)
|
||||
|
||||
if isinstance(snippet, collections.Mapping):
|
||||
def mkpath(key):
|
||||
return '.'.join([path, six.text_type(key)])
|
||||
|
||||
if len(snippet) == 1:
|
||||
fn_name, args = next(six.iteritems(snippet))
|
||||
Func = functions.get(fn_name)
|
||||
if Func is not None:
|
||||
return Func(stack, fn_name, recurse(args))
|
||||
return dict((k, recurse(v)) for k, v in six.iteritems(snippet))
|
||||
try:
|
||||
path = '.'.join([path, fn_name])
|
||||
return Func(stack, fn_name, recurse(args, path))
|
||||
except (ValueError, TypeError, KeyError) as e:
|
||||
raise exception.StackValidationFailed(
|
||||
path=path,
|
||||
message=six.text_type(e))
|
||||
|
||||
return dict((k, recurse(v, mkpath(k)))
|
||||
for k, v in six.iteritems(snippet))
|
||||
elif (not isinstance(snippet, six.string_types) and
|
||||
isinstance(snippet, collections.Iterable)):
|
||||
return [recurse(v) for v in snippet]
|
||||
|
||||
def mkpath(idx):
|
||||
return ''.join([path, '[%d]' % idx])
|
||||
|
||||
return [recurse(v, mkpath(i)) for i, v in enumerate(snippet)]
|
||||
else:
|
||||
return snippet
|
||||
|
|
|
@ -627,7 +627,8 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
|
||||
tmpl = template.Template(hot_tpl_empty)
|
||||
|
||||
self.assertRaises(TypeError, self.resolve, snippet, tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
|
||||
def test_str_replace_invalid_param_keys(self):
|
||||
"""Test str_replace function parameter keys.
|
||||
|
@ -641,12 +642,14 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
|
||||
tmpl = template.Template(hot_tpl_empty)
|
||||
|
||||
self.assertRaises(KeyError, self.resolve, snippet, tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
|
||||
snippet = {'str_replace': {'tmpl': 'Template var1 string var2',
|
||||
'parms': {'var1': 'foo', 'var2': 'bar'}}}
|
||||
|
||||
self.assertRaises(KeyError, self.resolve, snippet, tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
|
||||
def test_str_replace_invalid_param_types(self):
|
||||
"""Test str_replace function parameter values.
|
||||
|
@ -665,8 +668,10 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
snippet = {'str_replace': {'template': 'Template var1 string var2',
|
||||
'params': ['var1', 'foo', 'var2', 'bar']}}
|
||||
|
||||
ex = self.assertRaises(TypeError, self.resolve, snippet, tmpl)
|
||||
self.assertIn('parameters must be a mapping', six.text_type(ex))
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
self.assertIn('.str_replace: "str_replace" parameters must be a'
|
||||
' mapping', six.text_type(ex))
|
||||
|
||||
def test_str_replace_invalid_param_type_init(self):
|
||||
"""Test str_replace function parameter values.
|
||||
|
@ -824,32 +829,42 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
def test_join_invalid(self):
|
||||
snippet = {'list_join': 'bad'}
|
||||
l_tmpl = template.Template(hot_liberty_tpl_empty)
|
||||
exc = self.assertRaises(TypeError, self.resolve, snippet, l_tmpl)
|
||||
self.assertIn('Incorrect arguments', six.text_type(exc))
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, l_tmpl)
|
||||
self.assertIn('.list_join: Incorrect arguments to "list_join"',
|
||||
six.text_type(exc))
|
||||
|
||||
k_tmpl = template.Template(hot_kilo_tpl_empty)
|
||||
exc1 = self.assertRaises(TypeError, self.resolve, snippet, k_tmpl)
|
||||
self.assertIn('Incorrect arguments', six.text_type(exc1))
|
||||
exc1 = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, k_tmpl)
|
||||
self.assertIn('.list_join: Incorrect arguments to "list_join"',
|
||||
six.text_type(exc1))
|
||||
|
||||
def test_join_int_invalid(self):
|
||||
snippet = {'list_join': 5}
|
||||
l_tmpl = template.Template(hot_liberty_tpl_empty)
|
||||
exc = self.assertRaises(TypeError, self.resolve, snippet, l_tmpl)
|
||||
self.assertIn('Incorrect arguments', six.text_type(exc))
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, l_tmpl)
|
||||
self.assertIn('.list_join: Incorrect arguments', six.text_type(exc))
|
||||
|
||||
k_tmpl = template.Template(hot_kilo_tpl_empty)
|
||||
exc1 = self.assertRaises(TypeError, self.resolve, snippet, k_tmpl)
|
||||
self.assertIn('Incorrect arguments', six.text_type(exc1))
|
||||
exc1 = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, k_tmpl)
|
||||
self.assertIn('.list_join: Incorrect arguments', six.text_type(exc1))
|
||||
|
||||
def test_join_invalid_value(self):
|
||||
snippet = {'list_join': [',']}
|
||||
l_tmpl = template.Template(hot_liberty_tpl_empty)
|
||||
exc = self.assertRaises(ValueError, self.resolve, snippet, l_tmpl)
|
||||
self.assertIn('Incorrect arguments', six.text_type(exc))
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, l_tmpl)
|
||||
self.assertIn('.list_join: Incorrect arguments to "list_join"',
|
||||
six.text_type(exc))
|
||||
|
||||
k_tmpl = template.Template(hot_kilo_tpl_empty)
|
||||
exc1 = self.assertRaises(ValueError, self.resolve, snippet, k_tmpl)
|
||||
self.assertIn('Incorrect arguments', six.text_type(exc1))
|
||||
exc1 = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, k_tmpl)
|
||||
self.assertIn('.list_join: Incorrect arguments to "list_join"',
|
||||
six.text_type(exc1))
|
||||
|
||||
def test_join_invalid_multiple(self):
|
||||
snippet = {'list_join': [',', 'bad', ['foo']]}
|
||||
|
@ -907,19 +922,22 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
'data': 'mustbeamap',
|
||||
'bogus': ""}}
|
||||
tmpl = template.Template(hot_newton_tpl_empty)
|
||||
self.assertRaises(KeyError, self.resolve, snippet, tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
|
||||
def test_yaql_invalid_syntax(self):
|
||||
snippet = {'yaql': {'wrong': 'wrong_expr',
|
||||
'wrong_data': 'mustbeamap'}}
|
||||
tmpl = template.Template(hot_newton_tpl_empty)
|
||||
self.assertRaises(KeyError, self.resolve, snippet, tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
|
||||
def test_yaql_non_map_args(self):
|
||||
snippet = {'yaql': 'invalid'}
|
||||
tmpl = template.Template(hot_newton_tpl_empty)
|
||||
msg = 'Arguments to "yaql" must be a map.'
|
||||
self.assertRaisesRegexp(TypeError, msg, self.resolve, snippet, tmpl)
|
||||
msg = '.yaql: Arguments to "yaql" must be a map.'
|
||||
self.assertRaisesRegexp(exception.StackValidationFailed,
|
||||
msg, self.resolve, snippet, tmpl)
|
||||
|
||||
def test_yaql_invalid_expression(self):
|
||||
snippet = {'yaql': {'expression': 'invalid(',
|
||||
|
@ -968,13 +986,15 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
tmpl = template.Template(hot_newton_tpl_empty)
|
||||
|
||||
snippet = {'equals': ['test', 'prod', 'invalid']}
|
||||
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
|
||||
self.assertIn('Arguments to "equals" must be of the form: '
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
self.assertIn('.equals: Arguments to "equals" must be of the form: '
|
||||
'[value_1, value_2]', six.text_type(exc))
|
||||
|
||||
snippet = {'equals': "invalid condition"}
|
||||
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
|
||||
self.assertIn('Arguments to "equals" must be of the form: '
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
self.assertIn('.equals: Arguments to "equals" must be of the form: '
|
||||
'[value_1, value_2]', six.text_type(exc))
|
||||
|
||||
def test_repeat(self):
|
||||
|
@ -1054,12 +1074,14 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
|
||||
# missing for_each
|
||||
snippet = {'repeat': {'template': 'this is %var%'}}
|
||||
self.assertRaises(KeyError, self.resolve, snippet, tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
|
||||
# misspelled for_each
|
||||
snippet = {'repeat': {'template': 'this is %var%',
|
||||
'foreach': {'%var%': ['a', 'b', 'c']}}}
|
||||
self.assertRaises(KeyError, self.resolve, snippet, tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
|
||||
# value given to for_each entry is not a list
|
||||
snippet = {'repeat': {'template': 'this is %var%',
|
||||
|
@ -1069,7 +1091,8 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
# misspelled template
|
||||
snippet = {'repeat': {'templte': 'this is %var%',
|
||||
'for_each': {'%var%': ['a', 'b', 'c']}}}
|
||||
self.assertRaises(KeyError, self.resolve, snippet, tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
|
||||
def test_repeat_bad_arg_type(self):
|
||||
tmpl = template.Template(hot_kilo_tpl_empty)
|
||||
|
@ -1285,7 +1308,7 @@ class HOTemplateTest(common.HeatTestCase):
|
|||
snippet = {'resource_facade': 'wibble'}
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_stack',
|
||||
template.Template(hot_tpl_empty))
|
||||
error = self.assertRaises(ValueError,
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve,
|
||||
snippet,
|
||||
stack.t, stack)
|
||||
|
@ -2470,8 +2493,9 @@ class TestGetAttAllAttributes(common.HeatTestCase):
|
|||
('test_get_attr_all_attributes_str', dict(
|
||||
hot_tpl=hot_tpl_generic_resource_all_attrs,
|
||||
snippet={'Value': {'get_attr': 'resource1'}},
|
||||
expected='Argument to "get_attr" must be a list',
|
||||
raises=TypeError
|
||||
expected='.Value.get_attr: Argument to "get_attr" must be a '
|
||||
'list',
|
||||
raises=exception.StackValidationFailed
|
||||
)),
|
||||
('test_get_attr_all_attributes_invalid_resource_list', dict(
|
||||
hot_tpl=hot_tpl_generic_resource_all_attrs,
|
||||
|
@ -2483,23 +2507,24 @@ class TestGetAttAllAttributes(common.HeatTestCase):
|
|||
('test_get_attr_all_attributes_invalid_type', dict(
|
||||
hot_tpl=hot_tpl_generic_resource_all_attrs,
|
||||
snippet={'Value': {'get_attr': {'resource1': 'attr1'}}},
|
||||
raises=TypeError,
|
||||
expected='Argument to "get_attr" must be a list'
|
||||
raises=exception.StackValidationFailed,
|
||||
expected='.Value.get_attr: Argument to "get_attr" must be a '
|
||||
'list'
|
||||
)),
|
||||
('test_get_attr_all_attributes_invalid_arg_str', dict(
|
||||
hot_tpl=hot_tpl_generic_resource_all_attrs,
|
||||
snippet={'Value': {'get_attr': ''}},
|
||||
raises=ValueError,
|
||||
expected='Arguments to "get_attr" can be of the next '
|
||||
'forms: [resource_name] or '
|
||||
raises=exception.StackValidationFailed,
|
||||
expected='.Value.get_attr: Arguments to "get_attr" can be of '
|
||||
'the next forms: [resource_name] or '
|
||||
'[resource_name, attribute, (path), ...]'
|
||||
)),
|
||||
('test_get_attr_all_attributes_invalid_arg_list', dict(
|
||||
hot_tpl=hot_tpl_generic_resource_all_attrs,
|
||||
snippet={'Value': {'get_attr': []}},
|
||||
raises=ValueError,
|
||||
expected='Arguments to "get_attr" can be of the next '
|
||||
'forms: [resource_name] or '
|
||||
raises=exception.StackValidationFailed,
|
||||
expected='.Value.get_attr: Arguments to "get_attr" can be of '
|
||||
'the next forms: [resource_name] or '
|
||||
'[resource_name, attribute, (path), ...]'
|
||||
)),
|
||||
('test_get_attr_all_attributes_standard', dict(
|
||||
|
|
|
@ -668,7 +668,8 @@ class TemplateTest(common.HeatTestCase):
|
|||
{'Fn::FindInMap': ["ReallyShortList"]})
|
||||
|
||||
for find in finds:
|
||||
self.assertRaises(KeyError, self.resolve, find, tmpl, stk)
|
||||
self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, find, tmpl, stk)
|
||||
|
||||
def test_param_refs(self):
|
||||
env = environment.Environment({'foo': 'bar', 'blarg': 'wibble'})
|
||||
|
@ -796,14 +797,16 @@ class TemplateTest(common.HeatTestCase):
|
|||
tmpl = template.Template(empty_template20161014)
|
||||
|
||||
snippet = {'Fn::Equals': ['test', 'prod', 'invalid']}
|
||||
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
|
||||
self.assertIn('Arguments to "Fn::Equals" must be of the form: '
|
||||
'[value_1, value_2]', six.text_type(exc))
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
self.assertIn('.Fn::Equals: Arguments to "Fn::Equals" must be of '
|
||||
'the form: [value_1, value_2]', six.text_type(exc))
|
||||
# test invalid type
|
||||
snippet = {'Fn::Equals': {"equal": False}}
|
||||
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
|
||||
self.assertIn('Arguments to "Fn::Equals" must be of the form: '
|
||||
'[value_1, value_2]', six.text_type(exc))
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, tmpl)
|
||||
self.assertIn('.Fn::Equals: Arguments to "Fn::Equals" must be of '
|
||||
'the form: [value_1, value_2]', six.text_type(exc))
|
||||
|
||||
def test_join(self):
|
||||
tmpl = template.Template(empty_template)
|
||||
|
@ -961,7 +964,7 @@ class TemplateTest(common.HeatTestCase):
|
|||
snippet = {'Fn::ResourceFacade': 'wibble'}
|
||||
stk = stack.Stack(self.ctx, 'test_stack',
|
||||
template.Template(empty_template))
|
||||
error = self.assertRaises(ValueError,
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
self.resolve, snippet, stk.t, stk)
|
||||
self.assertIn(next(iter(snippet)), six.text_type(error))
|
||||
|
||||
|
@ -1114,22 +1117,22 @@ class TemplateFnErrorTest(common.HeatTestCase):
|
|||
dict(expect=ValueError,
|
||||
snippet={"Fn::Select": ["not", "no json"]})),
|
||||
('select_wrong_num_args_1',
|
||||
dict(expect=ValueError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Select": []})),
|
||||
('select_wrong_num_args_2',
|
||||
dict(expect=ValueError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Select": ["4"]})),
|
||||
('select_wrong_num_args_3',
|
||||
dict(expect=ValueError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Select": ["foo", {"foo": "bar"}, ""]})),
|
||||
('select_wrong_num_args_4',
|
||||
dict(expect=TypeError,
|
||||
snippet={'Fn::Select': [['f'], {'f': 'food'}]})),
|
||||
('split_no_delim',
|
||||
dict(expect=ValueError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Split": ["foo, bar, achoo"]})),
|
||||
('split_no_list',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Split": "foo, bar, achoo"})),
|
||||
('base64_list',
|
||||
dict(expect=TypeError,
|
||||
|
@ -1143,18 +1146,18 @@ class TemplateFnErrorTest(common.HeatTestCase):
|
|||
{'$var1': 'foo', '%var2%': ['bar']},
|
||||
'$var1 is %var2%']})),
|
||||
('replace_list_mapping',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Replace": [
|
||||
['var1', 'foo', 'var2', 'bar'],
|
||||
'$var1 is ${var2}']})),
|
||||
('replace_dict',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Replace": {}})),
|
||||
('replace_missing_template',
|
||||
dict(expect=ValueError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Replace": [['var1', 'foo', 'var2', 'bar']]})),
|
||||
('replace_none_template',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Replace": [['var2', 'bar'], None]})),
|
||||
('replace_list_string',
|
||||
dict(expect=TypeError,
|
||||
|
@ -1168,46 +1171,46 @@ class TemplateFnErrorTest(common.HeatTestCase):
|
|||
dict(expect=TypeError,
|
||||
snippet={"Fn::Join": [" ", {"foo": "bar"}]})),
|
||||
('join_wrong_num_args_1',
|
||||
dict(expect=ValueError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": []})),
|
||||
('join_wrong_num_args_2',
|
||||
dict(expect=ValueError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": [" "]})),
|
||||
('join_wrong_num_args_3',
|
||||
dict(expect=ValueError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": [" ", {"foo": "bar"}, ""]})),
|
||||
('join_string_nodelim',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": "o"})),
|
||||
('join_string_nodelim_1',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": "oh"})),
|
||||
('join_string_nodelim_2',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": "ohh"})),
|
||||
('join_dict_nodelim1',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": {"foo": "bar"}})),
|
||||
('join_dict_nodelim2',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": {"foo": "bar", "blarg": "wibble"}})),
|
||||
('join_dict_nodelim3',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::Join": {"foo": "bar", "blarg": "wibble",
|
||||
"baz": "quux"}})),
|
||||
('member_list2map_no_key_or_val',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::MemberListToMap": [
|
||||
'Key', ['.member.2.Key=metric',
|
||||
'.member.2.Value=cpu',
|
||||
'.member.5.Key=size',
|
||||
'.member.5.Value=56']]})),
|
||||
('member_list2map_no_list',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::MemberListToMap": [
|
||||
'Key', '.member.2.Key=metric']})),
|
||||
('member_list2map_not_string',
|
||||
dict(expect=TypeError,
|
||||
dict(expect=exception.StackValidationFailed,
|
||||
snippet={"Fn::MemberListToMap": [
|
||||
'Name', ['Value'], ['.member.0.Name=metric',
|
||||
'.member.0.Value=cpu',
|
||||
|
|
|
@ -1624,7 +1624,8 @@ class ValidateTest(common.HeatTestCase):
|
|||
template = tmpl.Template(t)
|
||||
err = self.assertRaises(exception.StackValidationFailed,
|
||||
parser.Stack, self.ctx, 'test_stack', template)
|
||||
error_message = ('Arguments to "get_attr" must be of the form '
|
||||
error_message = ('outputs.string.value.get_attr: Arguments to '
|
||||
'"get_attr" must be of the form '
|
||||
'[resource_name, attribute, (path), ...]')
|
||||
self.assertEqual(error_message, six.text_type(err))
|
||||
|
||||
|
|
Loading…
Reference in New Issue