Add 'contains' function
Add new function 'contains' to check whether the specific value is in a sequence. The resolve result of the new function is a boolean value, so we put it as a condition function too. Change-Id: I90074070dd917df13345fb7c8d4ed09e4a76e864 Blueprint: support-contains-function
This commit is contained in:
parent
1c8c7a7914
commit
f4c4ec63ab
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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."""
|
||||
|
Loading…
Reference in New Issue
Block a user