Merge "Add 'contains' function"

This commit is contained in:
Jenkins 2017-06-22 11:43:39 +00:00 committed by Gerrit Code Review
commit c63d3cdd3b
4 changed files with 107 additions and 4 deletions

View File

@ -296,8 +296,9 @@ for the ``heat_template_version`` key:
up until the Pike release. This version adds the ``make_url`` function for up until the Pike release. This version adds the ``make_url`` function for
assembling URLs, the ``list_concat`` function for combining multiple assembling URLs, the ``list_concat`` function for combining multiple
lists, the ``list_concat_unique`` function for combining multiple lists, the ``list_concat_unique`` function for combining multiple
lists without repeating items, and the``string_replace_vstrict`` which lists without repeating items, the``string_replace_vstrict`` which
raises errors for missing and empty params. The complete list of raises errors for missing and empty params, and the ``contains`` which
checks whether specific value is in a sequence. The complete list of
supported functions is:: supported functions is::
digest digest
@ -310,6 +311,7 @@ for the ``heat_template_version`` key:
make_url make_url
list_concat list_concat
list_concat_unique list_concat_unique
contains
map_merge map_merge
map_replace map_replace
repeat repeat
@ -321,7 +323,7 @@ for the ``heat_template_version`` key:
yaql yaql
if if
We support 'yaql' as condition function in this version. We support 'yaql' and 'contains' as condition functions in this version.
The complete list of supported condition functions is:: The complete list of supported condition functions is::
equals equals
@ -330,6 +332,7 @@ for the ``heat_template_version`` key:
and and
or or
yaql yaql
contains
.. _hot_spec_parameter_groups: .. _hot_spec_parameter_groups:
@ -1958,3 +1961,25 @@ For example
list_concat_unique: [['v1', 'v2'], ['v2', 'v43']] list_concat_unique: [['v1', 'v2'], ['v2', 'v43']]
Will resolve to the list ``['v1', 'v2', 'v3']``. Will resolve to the list ``['v1', 'v2', 'v3']``.
contains
--------
The ``contains`` function checks whether the specific value is
in a sequence.
The syntax of the ``contains`` function is
.. code-block:: yaml
contains: [<value>, <sequence>]
This function returns true if value is in sequence or false if it isn't.
For example
.. code-block:: yaml
contains: ['v1', ['v1', 'v2', 'v3']]
Will resolve to boolean true.

View File

@ -1527,3 +1527,43 @@ class ListConcatUnique(ListConcat):
""" """
_unique = True _unique = True
class Contains(function.Function):
"""A function for checking whether specific value is in sequence.
Takes the form::
contains:
- <value>
- <sequence>
The value can be any type that you want to check. Returns true
if the specific value is in the sequence, otherwise returns false.
"""
def __init__(self, stack, fn_name, args):
super(Contains, self).__init__(stack, fn_name, args)
example = '"%s" : [ "value1", [ "value1", "value2"]]' % self.fn_name
fmt_data = {'fn_name': self.fn_name,
'example': example}
if not self.args or not isinstance(self.args, list):
raise TypeError(_('Incorrect arguments to "%(fn_name)s" '
'should be: %(example)s') % fmt_data)
try:
self.value, self.sequence = self.args
except ValueError:
msg = _('Arguments to "%s" must be of the form: '
'[value1, [value1, value2]]')
raise ValueError(msg % self.fn_name)
def result(self):
resolved_value = function.resolve(self.value)
resolved_sequence = function.resolve(self.sequence)
if not isinstance(resolved_sequence, collections.Sequence):
raise TypeError(_('Second argument to "%s" should be '
'a sequence.') % self.fn_name)
return resolved_value in resolved_sequence

View File

@ -593,6 +593,7 @@ class HOTemplate20170901(HOTemplate20170224):
'list_concat': hot_funcs.ListConcat, 'list_concat': hot_funcs.ListConcat,
'str_replace_vstrict': hot_funcs.ReplaceJsonVeryStrict, 'str_replace_vstrict': hot_funcs.ReplaceJsonVeryStrict,
'list_concat_unique': hot_funcs.ListConcatUnique, 'list_concat_unique': hot_funcs.ListConcatUnique,
'contains': hot_funcs.Contains,
# functions removed from 2015-10-15 # functions removed from 2015-10-15
'Fn::Select': hot_funcs.Removed, 'Fn::Select': hot_funcs.Removed,
@ -616,5 +617,6 @@ class HOTemplate20170901(HOTemplate20170224):
'or': hot_funcs.Or, 'or': hot_funcs.Or,
# functions added in 2017-09-01 # functions added in 2017-09-01
'yaql': hot_funcs.Yaql 'yaql': hot_funcs.Yaql,
'contains': hot_funcs.Contains
} }

View File

@ -2201,6 +2201,42 @@ conditions:
snippet = {'list_concat': ['v1', 'v2']} snippet = {'list_concat': ['v1', 'v2']}
self._test_list_concat_invalid(snippet) self._test_list_concat_invalid(snippet)
def test_contains_with_list(self):
snippet = {'contains': ['v1', ['v1', 'v2']]}
tmpl = template.Template(hot_pike_tpl_empty)
resolved = self.resolve(snippet, tmpl)
self.assertTrue(resolved)
def test_contains_with_string(self):
snippet = {'contains': ['a', 'abc']}
tmpl = template.Template(hot_pike_tpl_empty)
resolved = self.resolve(snippet, tmpl)
self.assertTrue(resolved)
def test_contains_with_invalid_args_type(self):
snippet = {'contains': {'key': 'value'}}
tmpl = template.Template(hot_pike_tpl_empty)
exc = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, tmpl)
msg = 'Incorrect arguments to '
self.assertIn(msg, six.text_type(exc))
def test_contains_with_invalid_args_number(self):
snippet = {'contains': ['v1', ['v1', 'v2'], 'redundant']}
tmpl = template.Template(hot_pike_tpl_empty)
exc = self.assertRaises(exception.StackValidationFailed,
self.resolve, snippet, tmpl)
msg = 'must be of the form: [value1, [value1, value2]]'
self.assertIn(msg, six.text_type(exc))
def test_contains_with_invalid_sequence(self):
snippet = {'contains': ['v1', {'key': 'value'}]}
tmpl = template.Template(hot_pike_tpl_empty)
exc = self.assertRaises(TypeError,
self.resolve, snippet, tmpl)
msg = 'should be a sequence'
self.assertIn(msg, six.text_type(exc))
class HotStackTest(common.HeatTestCase): class HotStackTest(common.HeatTestCase):
"""Test stack function when stack was created from HOT template.""" """Test stack function when stack was created from HOT template."""