Support 'conditions' section for templates
This changes: 1. Support 'Conditions' for AWSTemplateFormatVersion: 2010-09-09 2. Support 'conditions' for heat_template_version: 2016-10-14 3. There is no need to add a new HeatTemplateFormatVersion template, because we decide to support conditions in AWSTemplateFormatVersion and heat_template_version: 2016-10-14, so remove the HeatTemplateFormatVersion.2016-10-14 4. Move the definition of function 'equals' to hot/functions.py 5. Mark 'equals' as condition function which supported in conditions. Change-Id: I2e7bdfa1c2052e75f35f2bd0003cdc170188d8b8 Blueprint: support-conditions-function
This commit is contained in:
parent
6c9f33dade
commit
70c4ab3fcf
@ -43,7 +43,7 @@ HOT templates are defined in YAML and follow the structure outlined below.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
heat_template_version: 2015-04-30
|
||||
heat_template_version: 2016-10-14
|
||||
|
||||
description:
|
||||
# a description of the template
|
||||
@ -60,6 +60,9 @@ HOT templates are defined in YAML and follow the structure outlined below.
|
||||
outputs:
|
||||
# declaration of output parameters
|
||||
|
||||
conditions:
|
||||
# declaration of conditions
|
||||
|
||||
heat_template_version
|
||||
This key with value ``2013-05-23`` (or a later date) indicates that the
|
||||
YAML document is a HOT template of the specified version.
|
||||
@ -89,6 +92,15 @@ outputs
|
||||
once the template has been instantiated. This section is optional and can
|
||||
be omitted when no output values are required.
|
||||
|
||||
conditions
|
||||
This optional section includes statements which can be used to restrict
|
||||
when a resource is created or when a property is defined. They can be
|
||||
associated with resources and resource properties in the
|
||||
``resources`` section, also can be associated with outputs in the
|
||||
``outputs`` sections of a template.
|
||||
|
||||
Note: Support for this section is added in the Newton version.
|
||||
|
||||
|
||||
.. _hot_spec_template_version:
|
||||
|
||||
@ -209,10 +221,9 @@ for the ``heat_template_version`` key:
|
||||
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, and
|
||||
the ``map_replace`` function that can do key/value replacements on a mapping.
|
||||
The complete list of supported functions is::
|
||||
can be used for evaluation of complex expressions, and the ``map_replace``
|
||||
function that can do key/value replacements on a mapping. The complete list
|
||||
of supported functions is::
|
||||
|
||||
digest
|
||||
get_attr
|
||||
@ -227,7 +238,13 @@ for the ``heat_template_version`` key:
|
||||
str_replace
|
||||
str_split
|
||||
yaql
|
||||
|
||||
This version also adds ``equals`` condition function which can be used
|
||||
to compare whether two values are equal. The complete list of supported
|
||||
condition functions is::
|
||||
|
||||
equals
|
||||
get_param
|
||||
|
||||
.. _hot_spec_parameter_groups:
|
||||
|
||||
@ -756,6 +773,48 @@ be defined as an output parameter
|
||||
value: { get_attr: [my_instance, first_address] }
|
||||
|
||||
|
||||
Conditions section
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
The ``conditions`` section defines one or more conditions which are evaluated
|
||||
based on input parameter values provided when a user creates or updates a
|
||||
stack. The condition can be associated with resources, resource properties and
|
||||
outputs. For example, based on the result of a condition, user can
|
||||
conditionally create resources, user can conditionally set different values
|
||||
of properties, and user can conditionally give outputs of a stack.
|
||||
|
||||
The ``conditions`` section is defined with the following syntax
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
conditions:
|
||||
<condition name1>: {expression1}
|
||||
<condition name2>: {expression2}
|
||||
...
|
||||
|
||||
condition name
|
||||
The condition name, which must be unique within the ``conditions``
|
||||
section of a template.
|
||||
|
||||
expression
|
||||
The expression which is expected to return True or False. Usually,
|
||||
the condition functions can be used as expression to define conditions::
|
||||
|
||||
equals
|
||||
get_param
|
||||
|
||||
Note: In condition functions, you can reference a value from an input
|
||||
parameter, but you cannot reference resource or its attribute.
|
||||
|
||||
An example of conditions section definition
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
conditions:
|
||||
cd1: True
|
||||
cd2: {get_param: param1}
|
||||
cd3: {equals: [{get_param: param2}, "yes"]}
|
||||
|
||||
|
||||
.. _hot_spec_intrinsic_functions:
|
||||
|
||||
Intrinsic functions
|
||||
|
@ -129,6 +129,10 @@ class InvalidTemplateSection(HeatException):
|
||||
msg_fmt = _("The template section is invalid: %(section)s")
|
||||
|
||||
|
||||
class InvalidConditionFunction(HeatException):
|
||||
msg_fmt = _("The function is not supported in condition: %(func)s")
|
||||
|
||||
|
||||
class ImmutableParameterModified(HeatException):
|
||||
msg_fmt = _("The following parameters are immutable and may not be "
|
||||
"updated: %(keys)s")
|
||||
|
@ -48,36 +48,6 @@ class FindInMap(function.Function):
|
||||
return mapping[key][value]
|
||||
|
||||
|
||||
class Equals(function.Function):
|
||||
"""A function for comparing whether two values are equal.
|
||||
|
||||
Takes the form::
|
||||
|
||||
{ "Fn::Equals" : ["value_1", "value_2"] }
|
||||
|
||||
The value to be any type that you want to compare. Returns true
|
||||
if the two values are equal or false if they aren't.
|
||||
"""
|
||||
|
||||
def __init__(self, stack, fn_name, args):
|
||||
super(Equals, self).__init__(stack, fn_name, args)
|
||||
try:
|
||||
if (not self.args or
|
||||
not isinstance(self.args, list)):
|
||||
raise ValueError()
|
||||
self.value1, self.value2 = self.args
|
||||
except ValueError:
|
||||
msg = _('Arguments to "%s" must be of the form: '
|
||||
'[value_1, value_2]')
|
||||
raise ValueError(msg % self.fn_name)
|
||||
|
||||
def result(self):
|
||||
resolved_v1 = function.resolve(self.value1)
|
||||
resolved_v2 = function.resolve(self.value2)
|
||||
|
||||
return resolved_v1 == resolved_v2
|
||||
|
||||
|
||||
class GetAZs(function.Function):
|
||||
"""A function for retrieving the availability zones.
|
||||
|
||||
|
@ -19,20 +19,21 @@ from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
from heat.engine.cfn import functions as cfn_funcs
|
||||
from heat.engine import function
|
||||
from heat.engine.hot import functions as hot_funcs
|
||||
from heat.engine import parameters
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.engine import template
|
||||
|
||||
|
||||
class CfnTemplate(template.Template):
|
||||
"""A stack template."""
|
||||
class CfnTemplateBase(template.Template):
|
||||
"""The base implementation of cfn template."""
|
||||
|
||||
SECTIONS = (
|
||||
VERSION, ALTERNATE_VERSION,
|
||||
DESCRIPTION, MAPPINGS, PARAMETERS, RESOURCES, OUTPUTS
|
||||
DESCRIPTION, MAPPINGS, PARAMETERS, RESOURCES, OUTPUTS,
|
||||
) = (
|
||||
'AWSTemplateFormatVersion', 'HeatTemplateFormatVersion',
|
||||
'Description', 'Mappings', 'Parameters', 'Resources', 'Outputs'
|
||||
'Description', 'Mappings', 'Parameters', 'Resources', 'Outputs',
|
||||
)
|
||||
|
||||
OUTPUT_KEYS = (
|
||||
@ -206,7 +207,26 @@ class CfnTemplate(template.Template):
|
||||
self.t[self.RESOURCES][name] = cfn_tmpl
|
||||
|
||||
|
||||
class HeatTemplate(CfnTemplate):
|
||||
class CfnTemplate(CfnTemplateBase):
|
||||
|
||||
CONDITIONS = 'Conditions'
|
||||
SECTIONS = CfnTemplateBase.SECTIONS + (CONDITIONS,)
|
||||
|
||||
condition_functions = {
|
||||
'Fn::Equals': hot_funcs.Equals,
|
||||
'Ref': cfn_funcs.ParamRef,
|
||||
'Fn::FindInMap': cfn_funcs.FindInMap,
|
||||
}
|
||||
|
||||
def __init__(self, tmpl, template_id=None, files=None, env=None):
|
||||
super(CfnTemplate, self).__init__(tmpl, template_id, files, env)
|
||||
|
||||
self._parser_condition_functions = dict(
|
||||
(n, function.Invalid) for n in self.functions)
|
||||
self._parser_condition_functions.update(self.condition_functions)
|
||||
|
||||
|
||||
class HeatTemplate(CfnTemplateBase):
|
||||
functions = {
|
||||
'Fn::FindInMap': cfn_funcs.FindInMap,
|
||||
'Fn::GetAZs': cfn_funcs.GetAZs,
|
||||
@ -220,21 +240,3 @@ class HeatTemplate(CfnTemplate):
|
||||
'Fn::MemberListToMap': cfn_funcs.MemberListToMap,
|
||||
'Fn::ResourceFacade': cfn_funcs.ResourceFacade,
|
||||
}
|
||||
|
||||
|
||||
class HeatTemplate20161014(HeatTemplate):
|
||||
functions = {
|
||||
'Fn::FindInMap': cfn_funcs.FindInMap,
|
||||
'Fn::GetAZs': cfn_funcs.GetAZs,
|
||||
'Ref': cfn_funcs.Ref,
|
||||
'Fn::GetAtt': cfn_funcs.GetAtt,
|
||||
'Fn::Select': cfn_funcs.Select,
|
||||
'Fn::Join': cfn_funcs.Join,
|
||||
'Fn::Split': cfn_funcs.Split,
|
||||
'Fn::Replace': cfn_funcs.Replace,
|
||||
'Fn::Base64': cfn_funcs.Base64,
|
||||
'Fn::MemberListToMap': cfn_funcs.MemberListToMap,
|
||||
'Fn::ResourceFacade': cfn_funcs.ResourceFacade,
|
||||
# supports Fn::Equals in Newton
|
||||
'Fn::Equals': cfn_funcs.Equals,
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ import weakref
|
||||
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Function(object):
|
||||
@ -203,3 +205,17 @@ def dep_attrs(snippet, resource_name):
|
||||
attrs = (dep_attrs(value, resource_name) for value in snippet)
|
||||
return itertools.chain.from_iterable(attrs)
|
||||
return []
|
||||
|
||||
|
||||
class Invalid(Function):
|
||||
"""A function for checking condition functions and to force failures.
|
||||
|
||||
This function is used to force failures for functions that are not
|
||||
supported in condition definition.
|
||||
"""
|
||||
|
||||
def __init__(self, stack, fn_name, args):
|
||||
raise exception.InvalidConditionFunction(func=fn_name)
|
||||
|
||||
def result(self):
|
||||
return super(Invalid, self).result()
|
||||
|
@ -872,3 +872,33 @@ class Yaql(function.Function):
|
||||
self._expression = function.resolve(self._expression)
|
||||
self.validate_expression(self._expression)
|
||||
return self.parser(self._expression).evaluate(context=self.context)
|
||||
|
||||
|
||||
class Equals(function.Function):
|
||||
"""A function for comparing whether two values are equal.
|
||||
|
||||
Takes the form::
|
||||
|
||||
{ "equals" : ["value_1", "value_2"] }
|
||||
|
||||
The value can be any type that you want to compare. Returns true
|
||||
if the two values are equal or false if they aren't.
|
||||
"""
|
||||
|
||||
def __init__(self, stack, fn_name, args):
|
||||
super(Equals, self).__init__(stack, fn_name, args)
|
||||
try:
|
||||
if (not self.args or
|
||||
not isinstance(self.args, list)):
|
||||
raise ValueError()
|
||||
self.value1, self.value2 = self.args
|
||||
except ValueError:
|
||||
msg = _('Arguments to "%s" must be of the form: '
|
||||
'[value_1, value_2]')
|
||||
raise ValueError(msg % self.fn_name)
|
||||
|
||||
def result(self):
|
||||
resolved_v1 = function.resolve(self.value1)
|
||||
resolved_v2 = function.resolve(self.value2)
|
||||
|
||||
return resolved_v1 == resolved_v2
|
||||
|
@ -30,10 +30,10 @@ class HOTemplate20130523(template.Template):
|
||||
|
||||
SECTIONS = (
|
||||
VERSION, DESCRIPTION, PARAMETER_GROUPS,
|
||||
PARAMETERS, RESOURCES, OUTPUTS, MAPPINGS
|
||||
PARAMETERS, RESOURCES, OUTPUTS, MAPPINGS,
|
||||
) = (
|
||||
'heat_template_version', 'description', 'parameter_groups',
|
||||
'parameters', 'resources', 'outputs', '__undefined__'
|
||||
'parameters', 'resources', 'outputs', '__undefined__',
|
||||
)
|
||||
|
||||
OUTPUT_KEYS = (
|
||||
@ -394,6 +394,15 @@ class HOTemplate20160408(HOTemplate20151015):
|
||||
|
||||
|
||||
class HOTemplate20161014(HOTemplate20160408):
|
||||
|
||||
CONDITIONS = 'conditions'
|
||||
|
||||
SECTIONS = HOTemplate20160408.SECTIONS + (CONDITIONS,)
|
||||
|
||||
_CFN_TO_HOT_SECTIONS = HOTemplate20160408._CFN_TO_HOT_SECTIONS
|
||||
_CFN_TO_HOT_SECTIONS.update({
|
||||
cfn_template.CfnTemplate.CONDITIONS: CONDITIONS})
|
||||
|
||||
deletion_policies = {
|
||||
'Delete': rsrc_defn.ResourceDefinition.DELETE,
|
||||
'Retain': rsrc_defn.ResourceDefinition.RETAIN,
|
||||
@ -426,7 +435,6 @@ class HOTemplate20161014(HOTemplate20160408):
|
||||
|
||||
# functions added in 2016-10-14
|
||||
'yaql': hot_funcs.Yaql,
|
||||
'equals': cfn_funcs.Equals,
|
||||
'map_replace': hot_funcs.MapReplace,
|
||||
|
||||
# functions removed from 2015-10-15
|
||||
@ -442,3 +450,20 @@ class HOTemplate20161014(HOTemplate20160408):
|
||||
'Fn::ResourceFacade': hot_funcs.Removed,
|
||||
'Ref': hot_funcs.Removed,
|
||||
}
|
||||
|
||||
condition_functions = {
|
||||
'get_param': hot_funcs.GetParam,
|
||||
'equals': hot_funcs.Equals,
|
||||
}
|
||||
|
||||
def __init__(self, tmpl, template_id=None, files=None, env=None):
|
||||
super(HOTemplate20161014, self).__init__(
|
||||
tmpl, template_id, files, env)
|
||||
|
||||
self._parser_condition_functions = {}
|
||||
for n, f in six.iteritems(self.functions):
|
||||
if not isinstance(f, hot_funcs.Removed):
|
||||
self._parser_condition_functions[n] = function.Invalid
|
||||
else:
|
||||
self._parser_condition_functions[n] = f
|
||||
self._parser_condition_functions.update(self.condition_functions)
|
||||
|
@ -90,6 +90,10 @@ def get_template_class(template_data):
|
||||
class Template(collections.Mapping):
|
||||
"""A stack template."""
|
||||
|
||||
condition_functions = {}
|
||||
_parser_condition_functions = {}
|
||||
functions = {}
|
||||
|
||||
def __new__(cls, template, *args, **kwargs):
|
||||
"""Create a new Template of the appropriate class."""
|
||||
global _template_classes
|
||||
@ -260,6 +264,9 @@ class Template(collections.Mapping):
|
||||
def parse(self, stack, snippet, path=''):
|
||||
return parse(self.functions, stack, snippet, path)
|
||||
|
||||
def parse_condition(self, stack, snippet):
|
||||
return parse(self._parser_condition_functions, stack, snippet)
|
||||
|
||||
def validate(self):
|
||||
"""Validate the template.
|
||||
|
||||
|
@ -110,7 +110,7 @@ class TestContainer(common.HeatTestCase):
|
||||
self.stack = utils.parse_stack(tmpl)
|
||||
else:
|
||||
self.stack = stack
|
||||
resource_defns = self.stack.t.resource_definitions(stack)
|
||||
resource_defns = self.stack.t.resource_definitions(self.stack)
|
||||
if snippet is None:
|
||||
snippet = resource_defns['container']
|
||||
res_class = container.resource_mapping()[tmpl_name]
|
||||
|
@ -156,6 +156,10 @@ class HOTemplateTest(common.HeatTestCase):
|
||||
def resolve(snippet, template, stack=None):
|
||||
return function.resolve(template.parse(stack, snippet))
|
||||
|
||||
@staticmethod
|
||||
def resolve_condition(snippet, template, stack=None):
|
||||
return function.resolve(template.parse_condition(stack, snippet))
|
||||
|
||||
def test_defaults(self):
|
||||
"""Test default content behavior of HOT template."""
|
||||
|
||||
@ -1073,7 +1077,7 @@ class HOTemplateTest(common.HeatTestCase):
|
||||
tmpl = template.Template(hot_tpl)
|
||||
stack = parser.Stack(utils.dummy_context(),
|
||||
'test_equals_false', tmpl)
|
||||
resolved = self.resolve(snippet, tmpl, stack)
|
||||
resolved = self.resolve_condition(snippet, tmpl, stack)
|
||||
self.assertFalse(resolved)
|
||||
# when param 'env_type' is 'prod', equals function resolve to true
|
||||
tmpl = template.Template(hot_tpl,
|
||||
@ -1081,7 +1085,7 @@ class HOTemplateTest(common.HeatTestCase):
|
||||
{'env_type': 'prod'}))
|
||||
stack = parser.Stack(utils.dummy_context(),
|
||||
'test_equals_true', tmpl)
|
||||
resolved = self.resolve(snippet, tmpl, stack)
|
||||
resolved = self.resolve_condition(snippet, tmpl, stack)
|
||||
self.assertTrue(resolved)
|
||||
|
||||
def test_equals_invalid_args(self):
|
||||
@ -1089,15 +1093,27 @@ class HOTemplateTest(common.HeatTestCase):
|
||||
|
||||
snippet = {'equals': ['test', 'prod', 'invalid']}
|
||||
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))
|
||||
self.resolve_condition, snippet, tmpl)
|
||||
|
||||
error_msg = ('.equals: Arguments to "equals" must be '
|
||||
'of the form: [value_1, value_2]')
|
||||
self.assertIn(error_msg, six.text_type(exc))
|
||||
|
||||
snippet = {'equals': "invalid condition"}
|
||||
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))
|
||||
self.resolve_condition, snippet, tmpl)
|
||||
self.assertIn(error_msg, six.text_type(exc))
|
||||
|
||||
def test_equals_with_non_supported_function(self):
|
||||
|
||||
tmpl = template.Template(hot_newton_tpl_empty)
|
||||
|
||||
snippet = {'equals': [{'get_attr': [None, 'att1']},
|
||||
{'get_attr': [None, 'att2']}]}
|
||||
exc = self.assertRaises(exception.InvalidConditionFunction,
|
||||
self.resolve_condition, snippet, tmpl)
|
||||
error_msg = 'The function is not supported in condition: get_attr'
|
||||
self.assertIn(error_msg, six.text_type(exc))
|
||||
|
||||
def test_repeat(self):
|
||||
"""Test repeat function."""
|
||||
|
@ -55,8 +55,8 @@ empty_template = template_format.parse('''{
|
||||
"HeatTemplateFormatVersion" : "2012-12-12",
|
||||
}''')
|
||||
|
||||
empty_template20161014 = template_format.parse('''{
|
||||
"HeatTemplateFormatVersion" : "2016-10-14",
|
||||
aws_empty_template = template_format.parse('''{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
}''')
|
||||
|
||||
parameter_template = template_format.parse('''{
|
||||
@ -119,17 +119,20 @@ class TemplatePluginFixture(fixtures.Fixture):
|
||||
class TestTemplatePluginManager(common.HeatTestCase):
|
||||
def test_template_NEW_good(self):
|
||||
class NewTemplate(template.Template):
|
||||
SECTIONS = (VERSION, MAPPINGS) = ('NEWTemplateFormatVersion',
|
||||
'__undefined__')
|
||||
SECTIONS = (VERSION, MAPPINGS, CONDITIONS) = (
|
||||
'NEWTemplateFormatVersion',
|
||||
'__undefined__',
|
||||
'conditions')
|
||||
RESOURCES = 'thingies'
|
||||
|
||||
def param_schemata(self):
|
||||
def param_schemata(self, param_defaults=None):
|
||||
pass
|
||||
|
||||
def get_section_name(self, section):
|
||||
pass
|
||||
|
||||
def parameters(self, stack_identifier, user_params):
|
||||
def parameters(self, stack_identifier, user_params,
|
||||
param_defaults=None):
|
||||
pass
|
||||
|
||||
def validate_resource_definitions(self, stack):
|
||||
@ -144,9 +147,6 @@ class TestTemplatePluginManager(common.HeatTestCase):
|
||||
def __getitem__(self, section):
|
||||
return {}
|
||||
|
||||
def functions(self):
|
||||
return {}
|
||||
|
||||
class NewTemplatePrint(function.Function):
|
||||
def result(self):
|
||||
return 'always this'
|
||||
@ -495,6 +495,10 @@ class TemplateTest(common.HeatTestCase):
|
||||
def resolve(snippet, template, stack=None):
|
||||
return function.resolve(template.parse(stack, snippet))
|
||||
|
||||
@staticmethod
|
||||
def resolve_condition(snippet, template, stack=None):
|
||||
return function.resolve(template.parse_condition(stack, snippet))
|
||||
|
||||
def test_defaults(self):
|
||||
empty = template.Template(empty_template)
|
||||
self.assertNotIn('AWSTemplateFormatVersion', empty)
|
||||
@ -593,8 +597,7 @@ class TemplateTest(common.HeatTestCase):
|
||||
invalid_heat_version_tmp)
|
||||
ex_error_msg = ('The template version is invalid: '
|
||||
'"HeatTemplateFormatVersion: 2010-09-09". '
|
||||
'"HeatTemplateFormatVersion" should be one of: '
|
||||
'2012-12-12, 2016-10-14')
|
||||
'"HeatTemplateFormatVersion" should be: 2012-12-12')
|
||||
self.assertEqual(ex_error_msg, six.text_type(init_ex))
|
||||
|
||||
def test_invalid_version_not_in_heat_versions(self):
|
||||
@ -771,7 +774,7 @@ class TemplateTest(common.HeatTestCase):
|
||||
|
||||
def test_equals(self):
|
||||
tpl = template_format.parse('''
|
||||
HeatTemplateFormatVersion: 2016-10-14
|
||||
AWSTemplateFormatVersion: 2010-09-09
|
||||
Parameters:
|
||||
env_type:
|
||||
Type: String
|
||||
@ -782,7 +785,7 @@ class TemplateTest(common.HeatTestCase):
|
||||
tmpl = template.Template(tpl)
|
||||
stk = stack.Stack(utils.dummy_context(),
|
||||
'test_equals_false', tmpl)
|
||||
resolved = self.resolve(snippet, tmpl, stk)
|
||||
resolved = self.resolve_condition(snippet, tmpl, stk)
|
||||
self.assertFalse(resolved)
|
||||
# when param 'env_type' is 'prod', equals function resolve to true
|
||||
tmpl = template.Template(tpl,
|
||||
@ -790,23 +793,24 @@ class TemplateTest(common.HeatTestCase):
|
||||
{'env_type': 'prod'}))
|
||||
stk = stack.Stack(utils.dummy_context(),
|
||||
'test_equals_true', tmpl)
|
||||
resolved = self.resolve(snippet, tmpl, stk)
|
||||
resolved = self.resolve_condition(snippet, tmpl, stk)
|
||||
self.assertTrue(resolved)
|
||||
|
||||
def test_equals_invalid_args(self):
|
||||
tmpl = template.Template(empty_template20161014)
|
||||
tmpl = template.Template(aws_empty_template)
|
||||
|
||||
snippet = {'Fn::Equals': ['test', 'prod', 'invalid']}
|
||||
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))
|
||||
self.resolve_condition, snippet, tmpl)
|
||||
|
||||
error_msg = ('.Fn::Equals: Arguments to "Fn::Equals" must be '
|
||||
'of the form: [value_1, value_2]')
|
||||
self.assertIn(error_msg, six.text_type(exc))
|
||||
# test invalid type
|
||||
snippet = {'Fn::Equals': {"equal": False}}
|
||||
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))
|
||||
self.resolve_condition, snippet, tmpl)
|
||||
self.assertIn(error_msg, six.text_type(exc))
|
||||
|
||||
def test_join(self):
|
||||
tmpl = template.Template(empty_template)
|
||||
|
@ -155,7 +155,6 @@ heat.templates =
|
||||
heat_template_version.2016-04-08 = heat.engine.hot.template:HOTemplate20160408
|
||||
heat_template_version.2016-10-14 = heat.engine.hot.template:HOTemplate20161014
|
||||
heat_template_version.newton = heat.engine.hot.template:HOTemplate20161014
|
||||
HeatTemplateFormatVersion.2016-10-14 = heat.engine.cfn.template:HeatTemplate20161014
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
|
Loading…
x
Reference in New Issue
Block a user