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
assembling URLs, the ``list_concat`` function for combining multiple
lists, the ``list_concat_unique`` function for combining multiple
lists without repeating items, and the``string_replace_vstrict`` which
raises errors for missing and empty params. The complete list of
lists without repeating items, the``string_replace_vstrict`` which
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::
digest
@ -310,6 +311,7 @@ for the ``heat_template_version`` key:
make_url
list_concat
list_concat_unique
contains
map_merge
map_replace
repeat
@ -321,7 +323,7 @@ for the ``heat_template_version`` key:
yaql
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::
equals
@ -330,6 +332,7 @@ for the ``heat_template_version`` key:
and
or
yaql
contains
.. _hot_spec_parameter_groups:
@ -1958,3 +1961,25 @@ For example
list_concat_unique: [['v1', 'v2'], ['v2', 'v43']]
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
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,
'str_replace_vstrict': hot_funcs.ReplaceJsonVeryStrict,
'list_concat_unique': hot_funcs.ListConcatUnique,
'contains': hot_funcs.Contains,
# functions removed from 2015-10-15
'Fn::Select': hot_funcs.Removed,
@ -616,5 +617,6 @@ class HOTemplate20170901(HOTemplate20170224):
'or': hot_funcs.Or,
# 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']}
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):
"""Test stack function when stack was created from HOT template."""