summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Baker <sbaker@redhat.com>2013-10-24 11:22:38 +1300
committerSteve Baker <sbaker@redhat.com>2014-01-27 16:34:14 +1300
commite483b38ec107d53ca4134382f4ac0d69d5962ef6 (patch)
treeed9007addac662ef0d69bfac865159ec1e99ab1f
parent4ca7941a6526fafc9a68e19fe327da2057b3a1be (diff)
downloadheat-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.py19
-rw-r--r--heat/tests/test_parser.py20
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"]]}