summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZane Bitter <zbitter@redhat.com>2020-07-28 12:18:43 -0400
committerLuke Short <ekultails@gmail.com>2020-09-08 09:39:51 -0400
commit4aafceb3798df94abf236f7f3abb5dcf6c425bb3 (patch)
tree64a01a55c03bc992105ae523f0b4e03e11ba99bd
parentcdfb5df5597fb258169b4245e81f2bfcd01f192f (diff)
downloadheat-4aafceb3798df94abf236f7f3abb5dcf6c425bb3.tar.gz
Lazily cache parsed value of list/json parameters
Parsing the value of JSON parameters at the time we create them (or update the default value) results in exceptions occurring while creating the Stack object, that are then not caught and transformed. The result is an HTTP 500 Internal Error reported by the API, with an unhelpful error message. By not parsing the JSON until it is needed, we ensure that any errors will occur during validation, where errors are appropriately transformed (to StackValidationFailed) and annotated with the necessary information to help the user determine the source. Change-Id: I70e341c344d6254173ad4519276626230087263a Story: 2007957 Task: 40443 (cherry picked from commit b603470a1207fcbc9ee30f3a3b4b457a9f83a03a)
-rw-r--r--heat/engine/parameters.py39
-rw-r--r--heat/tests/test_parameters.py4
2 files changed, 21 insertions, 22 deletions
diff --git a/heat/engine/parameters.py b/heat/engine/parameters.py
index 044773815..4aced043b 100644
--- a/heat/engine/parameters.py
+++ b/heat/engine/parameters.py
@@ -369,32 +369,32 @@ class StringParam(Parameter):
class ParsedParameter(Parameter):
"""A template parameter with cached parsed value."""
- __slots__ = ('parsed',)
+ __slots__ = ('_parsed',)
def __init__(self, name, schema, value=None):
super(ParsedParameter, self).__init__(name, schema, value)
- self._update_parsed()
-
- def set_default(self, value):
- super(ParsedParameter, self).set_default(value)
- self._update_parsed()
-
- def _update_parsed(self):
- if self.has_value():
- if self.user_value is not None:
- self.parsed = self.parse(self.user_value)
+ self._parsed = None
+
+ @property
+ def parsed(self):
+ if self._parsed is None:
+ if self.has_value():
+ if self.user_value is not None:
+ self._parsed = self.parse(self.user_value)
+ else:
+ self._parsed = self.parse(self.default())
else:
- self.parsed = self.parse(self.default())
+ self._parsed = self.default_parsed()
+ return self._parsed
class CommaDelimitedListParam(ParsedParameter, collections.Sequence):
"""A template parameter of type "CommaDelimitedList"."""
- __slots__ = ('parsed',)
+ __slots__ = tuple()
- def __init__(self, name, schema, value=None):
- self.parsed = []
- super(CommaDelimitedListParam, self).__init__(name, schema, value)
+ def default_parsed(self):
+ return []
def parse(self, value):
# only parse when value is not already a list
@@ -436,11 +436,10 @@ class CommaDelimitedListParam(ParsedParameter, collections.Sequence):
class JsonParam(ParsedParameter):
"""A template parameter who's value is map or list."""
- __slots__ = ('parsed',)
+ __slots__ = tuple()
- def __init__(self, name, schema, value=None):
- self.parsed = {}
- super(JsonParam, self).__init__(name, schema, value)
+ def default_parsed(self):
+ return {}
def parse(self, value):
try:
diff --git a/heat/tests/test_parameters.py b/heat/tests/test_parameters.py
index f438c37fb..9ced8e25e 100644
--- a/heat/tests/test_parameters.py
+++ b/heat/tests/test_parameters.py
@@ -380,7 +380,7 @@ class ParameterTestSpecific(common.HeatTestCase):
schema = {'Type': 'Json',
'ConstraintDescription': 'wibble'}
val = {"foo": "bar", "not_json": len}
- err = self.assertRaises(ValueError,
+ err = self.assertRaises(exception.StackValidationFailed,
new_parameter, 'p', schema, val)
self.assertIn('Value must be valid JSON', six.text_type(err))
@@ -398,7 +398,7 @@ class ParameterTestSpecific(common.HeatTestCase):
schema = {'Type': 'Json',
'ConstraintDescription': 'wibble'}
val = "I am not a map"
- err = self.assertRaises(ValueError,
+ err = self.assertRaises(exception.StackValidationFailed,
new_parameter, 'p', schema, val)
self.assertIn('Value must be valid JSON', six.text_type(err))