Merge "JSON size violation gives a bad error with nested templates"
This commit is contained in:
commit
714627b39b
|
@ -98,6 +98,8 @@ class InstantiationData(object):
|
||||||
adopt_data = self.data[rpc_api.PARAM_ADOPT_STACK_DATA]
|
adopt_data = self.data[rpc_api.PARAM_ADOPT_STACK_DATA]
|
||||||
try:
|
try:
|
||||||
adopt_data = template_format.simple_parse(adopt_data)
|
adopt_data = template_format.simple_parse(adopt_data)
|
||||||
|
template_format.validate_template_limit(
|
||||||
|
six.text_type(adopt_data['template']))
|
||||||
return adopt_data['template']
|
return adopt_data['template']
|
||||||
except (ValueError, KeyError) as ex:
|
except (ValueError, KeyError) as ex:
|
||||||
err_reason = _('Invalid adopt data: %s') % ex
|
err_reason = _('Invalid adopt data: %s') % ex
|
||||||
|
@ -105,7 +107,10 @@ class InstantiationData(object):
|
||||||
elif self.PARAM_TEMPLATE in self.data:
|
elif self.PARAM_TEMPLATE in self.data:
|
||||||
template_data = self.data[self.PARAM_TEMPLATE]
|
template_data = self.data[self.PARAM_TEMPLATE]
|
||||||
if isinstance(template_data, dict):
|
if isinstance(template_data, dict):
|
||||||
|
template_format.validate_template_limit(six.text_type(
|
||||||
|
template_data))
|
||||||
return template_data
|
return template_data
|
||||||
|
|
||||||
elif self.PARAM_TEMPLATE_URL in self.data:
|
elif self.PARAM_TEMPLATE_URL in self.data:
|
||||||
url = self.data[self.PARAM_TEMPLATE_URL]
|
url = self.data[self.PARAM_TEMPLATE_URL]
|
||||||
LOG.debug('TemplateUrl %s' % url)
|
LOG.debug('TemplateUrl %s' % url)
|
||||||
|
|
|
@ -22,8 +22,6 @@ import yaml
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
|
|
||||||
cfg.CONF.import_opt('max_template_size', 'heat.common.config')
|
|
||||||
|
|
||||||
if hasattr(yaml, 'CSafeLoader'):
|
if hasattr(yaml, 'CSafeLoader'):
|
||||||
yaml_loader = yaml.CSafeLoader
|
yaml_loader = yaml.CSafeLoader
|
||||||
else:
|
else:
|
||||||
|
@ -39,6 +37,7 @@ def _construct_yaml_str(self, node):
|
||||||
# Override the default string handling function
|
# Override the default string handling function
|
||||||
# to always return unicode objects
|
# to always return unicode objects
|
||||||
return self.construct_scalar(node)
|
return self.construct_scalar(node)
|
||||||
|
|
||||||
yaml_loader.add_constructor(u'tag:yaml.org,2002:str', _construct_yaml_str)
|
yaml_loader.add_constructor(u'tag:yaml.org,2002:str', _construct_yaml_str)
|
||||||
# Unquoted dates like 2013-05-23 in yaml files get loaded as objects of type
|
# Unquoted dates like 2013-05-23 in yaml files get loaded as objects of type
|
||||||
# datetime.data which causes problems in API layer when being processed by
|
# datetime.data which causes problems in API layer when being processed by
|
||||||
|
@ -75,16 +74,31 @@ def simple_parse(tmpl_str):
|
||||||
return tpl
|
return tpl
|
||||||
|
|
||||||
|
|
||||||
|
def validate_template_limit(contain_str):
|
||||||
|
"""Validate limit for the template.
|
||||||
|
|
||||||
|
Check if the contain exceeds allowed size range.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(contain_str) > cfg.CONF.max_template_size:
|
||||||
|
msg = _("Template size (%(actual_len)s bytes) exceeds maximum "
|
||||||
|
"allowed size (%(limit)s bytes)."
|
||||||
|
) % {'actual_len': len(contain_str),
|
||||||
|
'limit': cfg.CONF.max_template_size}
|
||||||
|
raise exception.RequestLimitExceeded(message=msg)
|
||||||
|
|
||||||
|
|
||||||
def parse(tmpl_str):
|
def parse(tmpl_str):
|
||||||
"""Takes a string and returns a dict containing the parsed structure.
|
"""Takes a string and returns a dict containing the parsed structure.
|
||||||
|
|
||||||
This includes determination of whether the string is using the
|
This includes determination of whether the string is using the
|
||||||
JSON or YAML format.
|
JSON or YAML format.
|
||||||
"""
|
"""
|
||||||
if len(tmpl_str) > cfg.CONF.max_template_size:
|
|
||||||
msg = (_('Template exceeds maximum allowed size (%s bytes)') %
|
# TODO(ricolin): Move this validation to api side.
|
||||||
cfg.CONF.max_template_size)
|
# Validate nested stack template.
|
||||||
raise exception.RequestLimitExceeded(message=msg)
|
validate_template_limit(six.text_type(tmpl_str))
|
||||||
|
|
||||||
tpl = simple_parse(tmpl_str)
|
tpl = simple_parse(tmpl_str)
|
||||||
# Looking for supported version keys in the loaded template
|
# Looking for supported version keys in the loaded template
|
||||||
if not ('HeatTemplateFormatVersion' in tpl
|
if not ('HeatTemplateFormatVersion' in tpl
|
||||||
|
|
|
@ -130,6 +130,20 @@ blarg: wibble
|
||||||
data = stacks.InstantiationData(body)
|
data = stacks.InstantiationData(body)
|
||||||
self.assertRaises(webob.exc.HTTPBadRequest, data.template)
|
self.assertRaises(webob.exc.HTTPBadRequest, data.template)
|
||||||
|
|
||||||
|
def test_template_exceeds_max_template_size(self):
|
||||||
|
cfg.CONF.set_override('max_template_size', 10)
|
||||||
|
template = json.dumps(['a'] * cfg.CONF.max_template_size)
|
||||||
|
body = {'template': template}
|
||||||
|
data = stacks.InstantiationData(body)
|
||||||
|
error = self.assertRaises(heat_exc.RequestLimitExceeded,
|
||||||
|
data.template)
|
||||||
|
|
||||||
|
msg = ('Request limit exceeded: Template size (%(actual_len)s '
|
||||||
|
'bytes) exceeds maximum allowed size (%(limit)s bytes).') % {
|
||||||
|
'actual_len': len(str(template)),
|
||||||
|
'limit': cfg.CONF.max_template_size}
|
||||||
|
self.assertEqual(msg, six.text_type(error))
|
||||||
|
|
||||||
def test_parameters(self):
|
def test_parameters(self):
|
||||||
params = {'foo': 'bar', 'blarg': 'wibble'}
|
params = {'foo': 'bar', 'blarg': 'wibble'}
|
||||||
body = {'parameters': params,
|
body = {'parameters': params,
|
||||||
|
|
|
@ -97,7 +97,7 @@ class YamlMinimalTest(common.HeatTestCase):
|
||||||
|
|
||||||
def test_long_yaml(self):
|
def test_long_yaml(self):
|
||||||
template = {'HeatTemplateFormatVersion': '2012-12-12'}
|
template = {'HeatTemplateFormatVersion': '2012-12-12'}
|
||||||
config.cfg.CONF.set_override('max_template_size', 1024)
|
config.cfg.CONF.set_override('max_template_size', 10)
|
||||||
template['Resources'] = ['a'] * int(
|
template['Resources'] = ['a'] * int(
|
||||||
config.cfg.CONF.max_template_size / 3)
|
config.cfg.CONF.max_template_size / 3)
|
||||||
limit = config.cfg.CONF.max_template_size
|
limit = config.cfg.CONF.max_template_size
|
||||||
|
@ -105,8 +105,10 @@ class YamlMinimalTest(common.HeatTestCase):
|
||||||
self.assertTrue(len(long_yaml) > limit)
|
self.assertTrue(len(long_yaml) > limit)
|
||||||
ex = self.assertRaises(exception.RequestLimitExceeded,
|
ex = self.assertRaises(exception.RequestLimitExceeded,
|
||||||
template_format.parse, long_yaml)
|
template_format.parse, long_yaml)
|
||||||
msg = ('Request limit exceeded: Template exceeds maximum allowed size '
|
msg = ('Request limit exceeded: Template size (%(actual_len)s '
|
||||||
'(1024 bytes)')
|
'bytes) exceeds maximum allowed size (%(limit)s bytes).') % {
|
||||||
|
'actual_len': len(str(long_yaml)),
|
||||||
|
'limit': config.cfg.CONF.max_template_size}
|
||||||
self.assertEqual(msg, six.text_type(ex))
|
self.assertEqual(msg, six.text_type(ex))
|
||||||
|
|
||||||
def test_parse_no_version_format(self):
|
def test_parse_no_version_format(self):
|
||||||
|
|
Loading…
Reference in New Issue