diff options
author | Steve Baker <sbaker@redhat.com> | 2013-10-24 11:22:38 +1300 |
---|---|---|
committer | Steve Baker <sbaker@redhat.com> | 2014-01-27 16:34:14 +1300 |
commit | e483b38ec107d53ca4134382f4ac0d69d5962ef6 (patch) | |
tree | ed9007addac662ef0d69bfac865159ec1e99ab1f | |
parent | 4ca7941a6526fafc9a68e19fe327da2057b3a1be (diff) | |
download | heat-e483b38ec107d53ca4134382f4ac0d69d5962ef6.tar.gz |
Tolerate lookup failures in Fn::Select
A wait condition will return an empty dict for its Data attribute
until it has been created, however Fn::Select on Data will be evaluated
before the wait condition is created (for example, during stack-list
or stack-show). The current error raising of Fn::Select leads these
operations to fail.
This change results in Fn::Select evaluation behaving in the
following way:
- KeyError on dict lookup failure results in empty string
- IndexError on list index results in empty string
- Passing an empty string as the value results in an empty string
Closes-Bug: #1243958
Change-Id: I3b01c8ce5dc56e804016274282d2ee69f539bf40
-rw-r--r-- | heat/engine/template.py | 19 | ||||
-rw-r--r-- | heat/tests/test_parser.py | 20 |
2 files changed, 31 insertions, 8 deletions
diff --git a/heat/engine/template.py b/heat/engine/template.py index d4da014de..537497cc3 100644 --- a/heat/engine/template.py +++ b/heat/engine/template.py @@ -219,12 +219,16 @@ class Template(collections.Mapping): (for a list lookup) { "Fn::Select" : [ "2", [ "apples", "grapes", "mangoes" ] ] } returns "mangoes" + { "Fn::Select" : [ "3", [ "apples", "grapes", "mangoes" ] ] } + returns "" (for a dict lookup) { "Fn::Select" : [ "red", {"red": "a", "flu": "b"} ] } returns "a" + { "Fn::Select" : [ "blue", {"red": "a", "flu": "b"} ] } + returns "" - Note: can raise IndexError, KeyError, ValueError and TypeError + Note: can raise ValueError and TypeError ''' def handle_select(args): if not isinstance(args, (list, tuple)): @@ -242,15 +246,24 @@ class Template(collections.Mapping): except ValueError as ex: index = lookup + if strings == '': + # an empty string is a common response from other + # functions when result is not currently available. + # Handle by returning an empty string + return '' + if isinstance(strings, basestring): # might be serialized json. # if not allow it to raise a ValueError strings = json.loads(strings) if isinstance(strings, (list, tuple)) and isinstance(index, int): - return strings[index] + try: + return strings[index] + except IndexError: + return '' if isinstance(strings, dict) and isinstance(index, basestring): - return strings[index] + return strings.get(index, '') if strings is None: return '' diff --git a/heat/tests/test_parser.py b/heat/tests/test_parser.py index 79d8faa84..5b9789042 100644 --- a/heat/tests/test_parser.py +++ b/heat/tests/test_parser.py @@ -239,9 +239,12 @@ Mappings: data) def test_select_from_list_out_of_bound(self): - data = {"Fn::Select": ["3", ["foo", "bar"]]} - self.assertRaises(IndexError, parser.Template.resolve_select, - data) + data = {"Fn::Select": ["0", ["foo", "bar"]]} + self.assertEqual(parser.Template.resolve_select(data), "foo") + data = {"Fn::Select": ["1", ["foo", "bar"]]} + self.assertEqual(parser.Template.resolve_select(data), "bar") + data = {"Fn::Select": ["2", ["foo", "bar"]]} + self.assertEqual(parser.Template.resolve_select(data), "") def test_select_from_dict(self): data = {"Fn::Select": ["red", {"red": "robin", "re": "foo"}]} @@ -258,8 +261,7 @@ Mappings: def test_select_from_dict_not_existing(self): data = {"Fn::Select": ["green", {"red": "robin", "re": "foo"}]} - self.assertRaises(KeyError, parser.Template.resolve_select, - data) + self.assertEqual(parser.Template.resolve_select(data), "") def test_select_from_serialized_json_map(self): js = json.dumps({"red": "robin", "re": "foo"}) @@ -288,6 +290,14 @@ Mappings: self.assertRaises(ValueError, parser.Template.resolve_select, join3) + def test_select_empty_string(self): + data = {"Fn::Select": ["0", '']} + self.assertEqual(parser.Template.resolve_select(data), "") + data = {"Fn::Select": ["1", '']} + self.assertEqual(parser.Template.resolve_select(data), "") + data = {"Fn::Select": ["one", '']} + self.assertEqual(parser.Template.resolve_select(data), "") + def test_join_reduce(self): join = {"Fn::Join": [" ", ["foo", "bar", "baz", {'Ref': 'baz'}, "bink", "bonk"]]} |