diff --git a/doc/source/template_guide/hot_spec.rst b/doc/source/template_guide/hot_spec.rst index b645ef855b..a646a0f11e 100644 --- a/doc/source/template_guide/hot_spec.rst +++ b/doc/source/template_guide/hot_spec.rst @@ -96,9 +96,10 @@ Heat template version ~~~~~~~~~~~~~~~~~~~~~ The value of ``heat_template_version`` tells Heat not only the format of the -template but also features that will be validated and supported. -For example, Heat currently supports the following values for the -``heat_template_version`` key: +template but also features that will be validated and supported. Beginning with +the Newton release, the version can be either the date of the Heat release or +the code name of the Heat release. Heat currently supports the following values +for the ``heat_template_version`` key: 2013-05-23 ---------- @@ -203,14 +204,14 @@ For example, Heat currently supports the following values for the str_replace str_split -2016-10-14 ----------- - The key with value ``2016-10-14`` indicates that the YAML document is a HOT - template and it may contain features added and/or removed up until the - Newton release. This version adds the ``yaql`` function which +2016-10-14 | newton +------------------- + The key with value ``2016-10-14`` or ``newton`` indicates that the YAML + document is a HOT template and it may contain features added and/or removed + up until the Newton release. This version adds the ``yaql`` function which can be used for evaluation of complex expressions, and also adds ``equals`` - function which can be used to compare whether two values are equal. - The complete list of supported functions is:: + function which can be used to compare whether two values are equal. The + complete list of supported functions is:: digest get_attr diff --git a/heat/common/exception.py b/heat/common/exception.py index c29295888a..c4b8d9c82a 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -500,3 +500,8 @@ class NoActionRequired(Exception): class InvalidServiceVersion(HeatException): msg_fmt = _("Invalid service %(service)s version %(version)s") + + +class InvalidTemplateVersions(HeatException): + msg_fmt = _('A template version alias %(version)s was added for a ' + 'template class that has no official YYYY-MM-DD version.') diff --git a/heat/engine/service.py b/heat/engine/service.py index 1d0b7475ab..141c7dee32 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -1459,15 +1459,39 @@ class EngineService(service.Service): return result def list_template_versions(self, cnxt): + def find_version_class(versions, cls): + for version in versions: + if version['class'] is cls: + return version + mgr = templatem._get_template_extension_manager() _template_classes = [(name, mgr[name].plugin) for name in mgr.names()] versions = [] - for t in _template_classes: + for t in sorted(_template_classes): # Sort to ensure dates come first if issubclass(t[1], cfntemplate.CfnTemplate): - versions.append({'version': t[0], 'type': 'cfn'}) + type = 'cfn' else: - versions.append({'version': t[0], 'type': 'hot'}) + type = 'hot' + + # Official versions are in '%Y-%m-%d' format. Default + # version aliases are the Heat release code name + try: + datetime.datetime.strptime(t[0].split('.')[-1], '%Y-%m-%d') + versions.append({'version': t[0], 'type': type, + 'class': t[1], 'aliases': []}) + except ValueError: + version = find_version_class(versions, t[1]) + if version is not None: + version['aliases'].append(t[0]) + else: + raise exception.InvalidTemplateVersions(version=t[0]) + + # 'class' was just used to find the version that the alias + # maps to. Remove it so it will not show up in the output + for version in versions: + del version['class'] + return versions def list_template_functions(self, cnxt, template_version): diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index 194951aa7d..958721a095 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -934,11 +934,12 @@ class StackServiceTest(common.HeatTestCase): class DummyMgr(object): def names(self): - return ['a.b', 'c.d'] + return ['a.2012-12-12', 'c.newton', 'c.2016-10-14', + 'c.something'] def __getitem__(self, item): m = mock.MagicMock() - if item == 'a.b': + if item == 'a.2012-12-12': m.plugin = cfntemplate.CfnTemplate return m else: @@ -947,10 +948,30 @@ class StackServiceTest(common.HeatTestCase): templ_mock.return_value = DummyMgr() templates = self.eng.list_template_versions(self.ctx) - expected = [{'version': 'a.b', 'type': 'cfn'}, - {'version': 'c.d', 'type': 'hot'}] + expected = [{'version': 'a.2012-12-12', 'type': 'cfn', 'aliases': []}, + {'version': 'c.2016-10-14', + 'aliases': ['c.newton', 'c.something'], 'type': 'hot'}] self.assertEqual(expected, templates) + @mock.patch('heat.engine.template._get_template_extension_manager') + def test_list_template_versions_invalid_version(self, templ_mock): + + class DummyMgr(object): + def names(self): + return ['c.something'] + + def __getitem__(self, item): + m = mock.MagicMock() + if item == 'c.something': + m.plugin = cfntemplate.CfnTemplate + return m + + templ_mock.return_value = DummyMgr() + ret = self.assertRaises(exception.InvalidTemplateVersions, + self.eng.list_template_versions, self.ctx) + self.assertIn('A template version alias c.something was added', + six.text_type(ret)) + @mock.patch('heat.engine.template._get_template_extension_manager') def test_list_template_functions(self, templ_mock): diff --git a/heat/tests/test_template.py b/heat/tests/test_template.py index 34cae03c35..4199235869 100644 --- a/heat/tests/test_template.py +++ b/heat/tests/test_template.py @@ -522,7 +522,7 @@ class TemplateTest(common.HeatTestCase): template.Template, invalid_hot_version_tmp) valid_versions = ['2013-05-23', '2014-10-16', '2015-04-30', '2015-10-15', '2016-04-08', - '2016-10-14'] + '2016-10-14', 'newton'] ex_error_msg = ('The template version is invalid: ' '"heat_template_version: 2012-12-12". ' '"heat_template_version" should be one of: %s' diff --git a/heat_integrationtests/functional/test_templates.py b/heat_integrationtests/functional/test_templates.py index 82a1af4f72..9d36391c60 100644 --- a/heat_integrationtests/functional/test_templates.py +++ b/heat_integrationtests/functional/test_templates.py @@ -54,7 +54,7 @@ class TemplateAPITest(functional_base.FunctionalTestsBase): supported_template_versions = ["2013-05-23", "2014-10-16", "2015-04-30", "2015-10-15", "2012-12-12", "2010-09-09", - "2016-04-08", "2016-10-14"] + "2016-04-08", "2016-10-14", "newton"] for template in template_versions: self.assertIn(template.version.split(".")[1], supported_template_versions) diff --git a/setup.cfg b/setup.cfg index 054f264f87..031bdbe829 100644 --- a/setup.cfg +++ b/setup.cfg @@ -141,15 +141,16 @@ heat.event_sinks = zaqar-queue = heat.engine.clients.os.zaqar:ZaqarEventSink heat.templates = + AWSTemplateFormatVersion.2010-09-09 = heat.engine.cfn.template:CfnTemplate + HeatTemplateFormatVersion.2012-12-12 = heat.engine.cfn.template:HeatTemplate heat_template_version.2013-05-23 = heat.engine.hot.template:HOTemplate20130523 heat_template_version.2014-10-16 = heat.engine.hot.template:HOTemplate20141016 heat_template_version.2015-04-30 = heat.engine.hot.template:HOTemplate20150430 heat_template_version.2015-10-15 = heat.engine.hot.template:HOTemplate20151015 heat_template_version.2016-04-08 = heat.engine.hot.template:HOTemplate20160408 heat_template_version.2016-10-14 = heat.engine.hot.template:HOTemplate20161014 - HeatTemplateFormatVersion.2012-12-12 = heat.engine.cfn.template:HeatTemplate + heat_template_version.newton = heat.engine.hot.template:HOTemplate20161014 HeatTemplateFormatVersion.2016-10-14 = heat.engine.cfn.template:HeatTemplate20161014 - AWSTemplateFormatVersion.2010-09-09 = heat.engine.cfn.template:CfnTemplate [global] setup-hooks =