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 digest
resource_facade resource_facade
str_replace 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 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 provided parameter. The script for doing this is provided as userdata to the
compute instance, leveraging the ``str_replace`` function. 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) self.validate_usage(args)
return self.digest(*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, 'resource_facade': hot_funcs.ResourceFacade,
'str_replace': hot_funcs.Replace, 'str_replace': hot_funcs.Replace,
# functions added since 20150430
'str_split': hot_funcs.StrSplit,
# functions removed from 20150430 # functions removed from 20150430
'Fn::Select': hot_funcs.Removed, 'Fn::Select': hot_funcs.Removed,

View File

@ -746,6 +746,51 @@ class HOTemplateTest(common.HeatTestCase):
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl) exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
self.assertIn('Algorithm must be one of', six.text_type(exc)) 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): def test_prevent_parameters_access(self):
""" """
Test that the parameters section can't be accessed using the template Test that the parameters section can't be accessed using the template