diff --git a/etc/heat/heat.conf.sample b/etc/heat/heat.conf.sample index 8a31cd2074..3f6e29ea81 100644 --- a/etc/heat/heat.conf.sample +++ b/etc/heat/heat.conf.sample @@ -70,6 +70,9 @@ # Keystone role for heat template-defined users (string value) #heat_stack_user_role=heat_stack_user +# Maximum raw byte size of any template. (integer value) +#max_template_size=524288 + # # Options defined in heat.common.crypt diff --git a/heat/common/config.py b/heat/common/config.py index 8feeacb30d..6f584fc668 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -81,7 +81,10 @@ service_opts = [ help='Instance connection to cfn/cw API validate certs if ssl'), cfg.StrOpt('heat_stack_user_role', default="heat_stack_user", - help='Keystone role for heat template-defined users')] + help='Keystone role for heat template-defined users'), + cfg.IntOpt('max_template_size', + default=524288, + help='Maximum raw byte size of any template.')] db_opts = [ cfg.StrOpt('sql_connection', diff --git a/heat/common/exception.py b/heat/common/exception.py index fe3c78b42b..9645a05d39 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -297,3 +297,7 @@ class HTTPExceptionDisguise(Exception): def __init__(self, exception): self.exc = exception self.tb = sys.exc_info()[2] + + +class TemplateTooBig(OpenstackException): + message = _('Template exceeds maximum allowed size.') diff --git a/heat/common/template_format.py b/heat/common/template_format.py index d6bd8e814e..a4064edf35 100644 --- a/heat/common/template_format.py +++ b/heat/common/template_format.py @@ -17,6 +17,12 @@ import re import yaml import json +from oslo.config import cfg + +from heat.common import exception + +cfg.CONF.import_opt('max_template_size', 'heat.common.config') + HEAT_VERSIONS = (u'2012-12-12',) CFN_VERSIONS = (u'2010-09-09',) @@ -43,6 +49,8 @@ def parse(tmpl_str, add_template_sections=True): This includes determination of whether the string is using the JSON or YAML format. ''' + if len(tmpl_str) > cfg.CONF.max_template_size: + raise exception.TemplateTooBig() if tmpl_str.startswith('{'): tpl = json.loads(tmpl_str) else: diff --git a/heat/tests/test_template_format.py b/heat/tests/test_template_format.py index 83843a1fd2..da98752f41 100644 --- a/heat/tests/test_template_format.py +++ b/heat/tests/test_template_format.py @@ -14,8 +14,11 @@ from testtools import skipIf import os +import yaml from heat.engine import clients +from heat.common import config +from heat.common import exception from heat.common import template_format from heat.tests.common import HeatTestCase from heat.tests import utils @@ -89,6 +92,15 @@ Outputs: {} tpl2 = template_format.parse(yaml2) self.assertEqual(tpl1, tpl2) + def test_long_yaml(self): + template = {'HeatTemplateVersion': '2012-12-12'} + template['Resources'] = ['a'] * (config.cfg.CONF.max_template_size / 3) + limit = config.cfg.CONF.max_template_size + long_yaml = yaml.safe_dump(template) + self.assertTrue(len(long_yaml) > limit) + self.assertRaises(exception.TemplateTooBig, template_format.parse, + long_yaml) + class YamlEnvironmentTest(HeatTestCase):