Provides 'equals' intrinsic function

Provides condition function 'equals' for hot template,
'Fn::Equals' for HeatTemplate, which versions
are 2016-10-14.

Change-Id: Ib0ffa76e6c562dfbddca5f9dce807f2c6ea3eb82
Blueprint: support-conditions-function
This commit is contained in:
huangtianhua 2016-02-23 10:43:34 +08:00
parent 87f4bcfbcf
commit 1d6cc2d18e
8 changed files with 185 additions and 5 deletions

View File

@ -338,3 +338,28 @@ Usage
Returns ``{'key': 'door', 'colour': 'green'}``.
----------
Fn::Equals
----------
Compares whether two values are equal. And returns true if the
two values are equal or false if they aren't.
Parameters
~~~~~~~~~~
value1:
A value of any type that you want to compare.
value2:
A value of any type that you want to compare.
Usage
~~~~~
.. code-block:: yaml
{'Fn::Equals': [{'Ref': 'env_type'}, 'prod']}
Returns true if the param 'env_type' equals to 'prod',
otherwise returns false.

View File

@ -187,7 +187,7 @@ For example, Heat currently supports the following values for the
----------
The key with value ``2016-04-08`` indicates that the YAML document is a HOT
template and it may contain features added and/or removed up until the
Mitaka release. This version also adds the map_merge function which
Mitaka release. This version also adds the ``map_merge`` function which
can be used to merge the contents of maps. The complete list of supported
functions is::
@ -207,9 +207,10 @@ For example, Heat currently supports the following values for the
----------
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 also adds the yaql function which
can be used for evaluation of complex expressions. The complete list of
supported functions is::
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::
digest
get_attr
@ -223,6 +224,7 @@ For example, Heat currently supports the following values for the
str_replace
str_split
yaql
equals
.. _hot_spec_parameter_groups:
@ -1315,3 +1317,25 @@ For example
list_param: {get_param: list_param}
max_elem output will be evaluated to 3
equals
------
The ``equals`` function compares whether two values are equal.
The syntax of the ``equals`` function is
.. code-block:: yaml
equals: [value_1, value_2]
The value can be any type that you want to compare. This function
returns true if the two values are equal or false if they aren't.
For example
.. code-block:: yaml
equals: [{get_param: env_type}, 'prod']
If param 'env_type' equals to 'prod', this function returns true,
otherwise returns false.

View File

@ -48,6 +48,36 @@ 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.

View File

@ -214,3 +214,21 @@ 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,
}

View File

@ -414,6 +414,9 @@ class HOTemplate20161014(HOTemplate20160408):
# functions added since 20161014
'yaql': hot_funcs.Yaql,
# functions added since 20161014
'equals': cfn_funcs.Equals,
# functions added since 20151015
'map_merge': hot_funcs.MapMerge,

View File

@ -891,6 +891,43 @@ class HOTemplateTest(common.HeatTestCase):
self.assertEqual(2, resolved)
def test_equals(self):
hot_tpl = template_format.parse('''
heat_template_version: 2016-10-14
parameters:
env_type:
type: string
default: 'test'
''')
snippet = {'equals': [{'get_param': 'env_type'}, 'prod']}
# when param 'env_type' is 'test', equals function resolve to false
tmpl = template.Template(hot_tpl)
stack = parser.Stack(utils.dummy_context(),
'test_equals_false', tmpl)
resolved = self.resolve(snippet, tmpl, stack)
self.assertFalse(resolved)
# when param 'env_type' is 'prod', equals function resolve to true
tmpl = template.Template(hot_tpl,
env=environment.Environment(
{'env_type': 'prod'}))
stack = parser.Stack(utils.dummy_context(),
'test_equals_true', tmpl)
resolved = self.resolve(snippet, tmpl, stack)
self.assertTrue(resolved)
def test_equals_invalid_args(self):
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: '
'[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: '
'[value_1, value_2]', six.text_type(exc))
def test_repeat(self):
"""Test repeat function."""
snippet = {'repeat': {'template': 'this is %var%',

View File

@ -55,6 +55,10 @@ empty_template = template_format.parse('''{
"HeatTemplateFormatVersion" : "2012-12-12",
}''')
empty_template20161014 = template_format.parse('''{
"HeatTemplateFormatVersion" : "2016-10-14",
}''')
parameter_template = template_format.parse('''{
"HeatTemplateFormatVersion" : "2012-12-12",
"Parameters" : {
@ -523,7 +527,8 @@ class TemplateTest(common.HeatTestCase):
invalid_heat_version_tmp)
ex_error_msg = ('The template version is invalid: '
'"HeatTemplateFormatVersion: 2010-09-09". '
'"HeatTemplateFormatVersion" should be: 2012-12-12')
'"HeatTemplateFormatVersion" should be one of: '
'2012-12-12, 2016-10-14')
self.assertEqual(ex_error_msg, six.text_type(init_ex))
def test_invalid_version_not_in_heat_versions(self):
@ -697,6 +702,43 @@ class TemplateTest(common.HeatTestCase):
data = {"Fn::Select": ["one", '']}
self.assertEqual("", self.resolve(data, tmpl))
def test_equals(self):
tpl = template_format.parse('''
HeatTemplateFormatVersion: 2016-10-14
Parameters:
env_type:
Type: String
Default: 'test'
''')
snippet = {'Fn::Equals': [{'Ref': 'env_type'}, 'prod']}
# when param 'env_type' is 'test', equals function resolve to false
tmpl = template.Template(tpl)
stk = stack.Stack(utils.dummy_context(),
'test_equals_false', tmpl)
resolved = self.resolve(snippet, tmpl, stk)
self.assertFalse(resolved)
# when param 'env_type' is 'prod', equals function resolve to true
tmpl = template.Template(tpl,
env=environment.Environment(
{'env_type': 'prod'}))
stk = stack.Stack(utils.dummy_context(),
'test_equals_true', tmpl)
resolved = self.resolve(snippet, tmpl, stk)
self.assertTrue(resolved)
def test_equals_invalid_args(self):
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))
# 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))
def test_join(self):
tmpl = template.Template(empty_template)
join = {"Fn::Join": [" ", ["foo", "bar"]]}

View File

@ -145,6 +145,7 @@ heat.templates =
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
HeatTemplateFormatVersion.2016-10-14 = heat.engine.cfn.template:HeatTemplate20161014
AWSTemplateFormatVersion.2010-09-09 = heat.engine.cfn.template:CfnTemplate
# These are for backwards compat with Icehouse notification_driver configuration values