Merge "Add str_split function to HOT 2015-10-15"

This commit is contained in:
Jenkins 2015-07-08 01:01:06 +00:00 committed by Gerrit Code Review
commit 4b4b27d0ff
4 changed files with 152 additions and 0 deletions

View File

@ -171,6 +171,7 @@ For example, Heat currently supports the following values for the
digest
resource_facade
str_replace
str_split
@ -1046,3 +1047,43 @@ In the example above, one can imagine that MySQL is being configured on a
compute instance and the root password is going to be set based on a user
provided parameter. The script for doing this is provided as userdata to the
compute instance, leveraging the ``str_replace`` function.
str_split
---------
The *str_split* function allows for splitting a string into a list by providing
an arbitrary delimiter, the opposite of list_join.
The syntax of the str_split function is as follows:
::
str_split:
- ','
- string,to,split
Or:
::
str_split: [',', 'string,to,split']
The result of which is:
::
['string', 'to', 'split']
Optionally, an index may be provided to select a specific entry from the
resulting list, similar to get_attr/get_param:
::
str_split: [',', 'string,to,split', 0]
The result of which is:
::
'string'
Note: The index starts at zero, and any value outside the maximum (e.g the
length of the list minus one) will cause an error.

View File

@ -424,3 +424,66 @@ class Digest(function.Function):
self.validate_usage(args)
return self.digest(*args)
class StrSplit(function.Function):
'''
A function for splitting delimited strings into a list
and optionally extracting a specific list member by index.
Takes the form::
str_split: [delimiter, string, <index> ]
or::
str_split:
- delimiter
- string
- <index>
If <index> is specified, the specified list item will be returned
otherwise, the whole list is returned, similar to get_attr with
path based attributes accessing lists.
'''
def __init__(self, stack, fn_name, args):
super(StrSplit, self).__init__(stack, fn_name, args)
example = '"%s" : [ ",", "apples,pears", <index>]' % fn_name
self.fmt_data = {'fn_name': fn_name,
'example': example}
self.fn_name = fn_name
if isinstance(args, (six.string_types, collections.Mapping)):
raise TypeError(_('Incorrect arguments to "%(fn_name)s" '
'should be: %(example)s') % self.fmt_data)
def result(self):
args = function.resolve(self.args)
try:
delim = args.pop(0)
str_to_split = args.pop(0)
except (AttributeError, IndexError):
raise ValueError(_('Incorrect arguments to "%(fn_name)s" '
'should be: %(example)s') % self.fmt_data)
split_list = str_to_split.split(delim)
# Optionally allow an index to be specified
if args:
try:
index = int(args.pop(0))
except ValueError:
raise ValueError(_('Incorrect index to "%(fn_name)s" '
'should be: %(example)s') % self.fmt_data)
else:
try:
res = split_list[index]
except IndexError:
raise ValueError(_('Incorrect index to "%(fn_name)s" '
'should be between 0 and '
'%(max_index)s')
% {'fn_name': self.fn_name,
'max_index': len(split_list) - 1})
else:
res = split_list
return res

View File

@ -339,6 +339,9 @@ class HOTemplate20151015(HOTemplate20150430):
'resource_facade': hot_funcs.ResourceFacade,
'str_replace': hot_funcs.Replace,
# functions added since 20150430
'str_split': hot_funcs.StrSplit,
# functions removed from 20150430
'Fn::Select': hot_funcs.Removed,

View File

@ -746,6 +746,51 @@ class HOTemplateTest(common.HeatTestCase):
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
self.assertIn('Algorithm must be one of', six.text_type(exc))
def test_str_split(self):
tmpl = template.Template(hot_liberty_tpl_empty)
snippet = {'str_split': [',', 'bar,baz']}
snippet_resolved = ['bar', 'baz']
self.assertEqual(snippet_resolved, self.resolve(snippet, tmpl))
def test_str_split_index(self):
tmpl = template.Template(hot_liberty_tpl_empty)
snippet = {'str_split': [',', 'bar,baz', 1]}
snippet_resolved = 'baz'
self.assertEqual(snippet_resolved, self.resolve(snippet, tmpl))
def test_str_split_index_str(self):
tmpl = template.Template(hot_liberty_tpl_empty)
snippet = {'str_split': [',', 'bar,baz', '1']}
snippet_resolved = 'baz'
self.assertEqual(snippet_resolved, self.resolve(snippet, tmpl))
def test_str_split_index_bad(self):
tmpl = template.Template(hot_liberty_tpl_empty)
snippet = {'str_split': [',', 'bar,baz', 'bad']}
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
self.assertIn('Incorrect index to \"str_split\"', six.text_type(exc))
def test_str_split_index_out_of_range(self):
tmpl = template.Template(hot_liberty_tpl_empty)
snippet = {'str_split': [',', 'bar,baz', '2']}
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
expected = 'Incorrect index to \"str_split\" should be between 0 and 1'
self.assertEqual(expected, six.text_type(exc))
def test_str_split_bad_novalue(self):
tmpl = template.Template(hot_liberty_tpl_empty)
snippet = {'str_split': [',']}
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
self.assertIn('Incorrect arguments to \"str_split\"',
six.text_type(exc))
def test_str_split_bad_empty(self):
tmpl = template.Template(hot_liberty_tpl_empty)
snippet = {'str_split': []}
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
self.assertIn('Incorrect arguments to \"str_split\"',
six.text_type(exc))
def test_prevent_parameters_access(self):
"""
Test that the parameters section can't be accessed using the template