From 4503154c1241b73f8efd483b41c111fefe3aaa6e Mon Sep 17 00:00:00 2001 From: Thomas Herve Date: Wed, 2 Jul 2014 15:12:46 +0200 Subject: [PATCH] Fix serialization of InvalidSchemaError Move InvalidSchemaError to heat.common.exception and pass a keyword argument so that it can be serialized properly. Change-Id: Iada3273b2b0b129bc0125b68fecf1e0efc081611 Closes-Bug: #1336740 --- heat/api/aws/exception.py | 1 + heat/api/middleware/fault.py | 1 + heat/common/exception.py | 4 ++++ heat/engine/constraints.py | 42 +++++++++++++++++----------------- heat/engine/hot/parameters.py | 11 +++++---- heat/engine/parameters.py | 26 ++++++++++----------- heat/engine/properties.py | 14 ++++++------ heat/tests/test_constraints.py | 22 +++++++++--------- heat/tests/test_hot.py | 27 +++++++++++----------- heat/tests/test_parameters.py | 13 +++++------ heat/tests/test_properties.py | 6 ++--- 11 files changed, 86 insertions(+), 81 deletions(-) diff --git a/heat/api/aws/exception.py b/heat/api/aws/exception.py index 392abc9a4..131c573f2 100644 --- a/heat/api/aws/exception.py +++ b/heat/api/aws/exception.py @@ -276,6 +276,7 @@ def map_remote_error(ex): 'PhysicalResourceNotFound', 'WatchRuleNotFound', 'StackValidationFailed', + 'InvalidSchemaError', 'InvalidTemplateReference', 'InvalidTemplateVersion', 'InvalidTemplateSection', diff --git a/heat/api/middleware/fault.py b/heat/api/middleware/fault.py index 9c00509b4..0c3ea96e3 100644 --- a/heat/api/middleware/fault.py +++ b/heat/api/middleware/fault.py @@ -68,6 +68,7 @@ class FaultWrapper(wsgi.Middleware): 'Forbidden': webob.exc.HTTPForbidden, 'StackExists': webob.exc.HTTPConflict, 'StackValidationFailed': webob.exc.HTTPBadRequest, + 'InvalidSchemaError': webob.exc.HTTPBadRequest, 'InvalidTemplateReference': webob.exc.HTTPBadRequest, 'InvalidTemplateVersion': webob.exc.HTTPBadRequest, 'InvalidTemplateSection': webob.exc.HTTPBadRequest, diff --git a/heat/common/exception.py b/heat/common/exception.py index 2068d177e..1fa27e18b 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -253,6 +253,10 @@ class StackValidationFailed(HeatException): msg_fmt = _("%(message)s") +class InvalidSchemaError(HeatException): + msg_fmt = _("%(message)s") + + class ResourceNotFound(HeatException): msg_fmt = _("The Resource (%(resource_name)s) could not be found " "in Stack %(stack_name)s.") diff --git a/heat/engine/constraints.py b/heat/engine/constraints.py index cc1f38eeb..761b0b3c4 100644 --- a/heat/engine/constraints.py +++ b/heat/engine/constraints.py @@ -23,10 +23,6 @@ from heat.engine import resources from heat.openstack.common import strutils -class InvalidSchemaError(exception.Error): - pass - - class Schema(collections.Mapping): """ Schema base class for validating properties or parameters. @@ -85,7 +81,8 @@ class Schema(collections.Mapping): self.label = label self.type = data_type if self.type not in self.TYPES: - raise InvalidSchemaError(_('Invalid type (%s)') % self.type) + raise exception.InvalidSchemaError( + message=_('Invalid type (%s)') % self.type) self.description = description self.required = required @@ -95,7 +92,7 @@ class Schema(collections.Mapping): msg = _('Single schema valid only for ' '%(ltype)s, not %(utype)s') % dict(ltype=self.LIST, utype=self.type) - raise InvalidSchemaError(msg) + raise exception.InvalidSchemaError(message=msg) self.schema = AnyIndexDict(schema) else: @@ -106,7 +103,7 @@ class Schema(collections.Mapping): '%(mtype)s, not %(utype)s') % dict(ltype=self.LIST, mtype=self.MAP, utype=self.type) - raise InvalidSchemaError(msg) + raise exception.InvalidSchemaError(message=msg) self.constraints = constraints or [] self.default = default @@ -124,7 +121,7 @@ class Schema(collections.Mapping): 'invalid for %(utype)s') % dict( name=type(c).__name__, utype=self.type) - raise InvalidSchemaError(err_msg) + raise exception.InvalidSchemaError(message=err_msg) self._validate_default(context) # validated nested schema(ta) @@ -140,9 +137,9 @@ class Schema(collections.Mapping): try: self.validate_constraints(self.default, context) except (ValueError, TypeError) as exc: - raise InvalidSchemaError(_('Invalid default ' - '%(default)s (%(exc)s)') % - dict(default=self.default, exc=exc)) + raise exception.InvalidSchemaError( + message=_('Invalid default %(default)s (%(exc)s)') % + dict(default=self.default, exc=exc)) def set_default(self, default=None): """Set the default value for this Schema object.""" @@ -335,12 +332,13 @@ class Range(Constraint): for param in (min, max): if not isinstance(param, (float, int, long, type(None))): - raise InvalidSchemaError(_('min/max must be numeric')) + raise exception.InvalidSchemaError( + message=_('min/max must be numeric')) if min is max is None: - raise InvalidSchemaError( - _('A range constraint must have a min value and/or a max ' - 'value specified.')) + raise exception.InvalidSchemaError( + message=_('A range constraint must have a min value and/or ' + 'a max value specified.')) def _str(self): if self.max is None: @@ -395,16 +393,16 @@ class Length(Range): def __init__(self, min=None, max=None, description=None): if min is max is None: - raise InvalidSchemaError( - _('A length constraint must have a min value and/or a max ' - 'value specified.')) + raise exception.InvalidSchemaError( + message=_('A length constraint must have a min value and/or ' + 'a max value specified.')) super(Length, self).__init__(min, max, description) for param in (min, max): if not isinstance(param, (int, long, type(None))): msg = _('min/max length must be integral') - raise InvalidSchemaError(msg) + raise exception.InvalidSchemaError(message=msg) def _str(self): if self.max is None: @@ -443,7 +441,8 @@ class AllowedValues(Constraint): super(AllowedValues, self).__init__(description) if (not isinstance(allowed, collections.Sequence) or isinstance(allowed, basestring)): - raise InvalidSchemaError(_('AllowedValues must be a list')) + raise exception.InvalidSchemaError( + message=_('AllowedValues must be a list')) self.allowed = tuple(allowed) def _str(self): @@ -487,7 +486,8 @@ class AllowedPattern(Constraint): def __init__(self, pattern, description=None): super(AllowedPattern, self).__init__(description) if not isinstance(pattern, basestring): - raise InvalidSchemaError(_('AllowedPattern must be a string')) + raise exception.InvalidSchemaError( + message=_('AllowedPattern must be a string')) self.pattern = pattern self.match = re.compile(pattern).match diff --git a/heat/engine/hot/parameters.py b/heat/engine/hot/parameters.py index 35dfd6ffa..e3b27e93c 100644 --- a/heat/engine/hot/parameters.py +++ b/heat/engine/hot/parameters.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from heat.common import exception from heat.engine import constraints as constr from heat.engine import parameters @@ -63,9 +64,9 @@ class HOTParamSchema(parameters.Schema): return if not isinstance(constraints, list): - raise constr.InvalidSchemaError( - _("Invalid parameter constraints for parameter %s, " - "expected a list") % param_name) + raise exception.InvalidSchemaError( + message=_("Invalid parameter constraints for parameter " + "%s, expected a list") % param_name) valid_keys = (DESCRIPTION, LENGTH, RANGE, ALLOWED_VALUES, ALLOWED_PATTERN, CUSTOM_CONSTRAINT) @@ -96,8 +97,8 @@ class HOTParamSchema(parameters.Schema): cdef = constraint.get(CUSTOM_CONSTRAINT) yield constr.CustomConstraint(cdef, desc) else: - raise constr.InvalidSchemaError( - _("No constraint expressed")) + raise exception.InvalidSchemaError( + message=_("No constraint expressed")) # make update_allowed true by default on TemplateResources # as the template should deal with this. diff --git a/heat/engine/parameters.py b/heat/engine/parameters.py index 0ac84bd6e..22a3f6dd4 100644 --- a/heat/engine/parameters.py +++ b/heat/engine/parameters.py @@ -75,17 +75,16 @@ class Schema(constr.Schema): try: default_value = self.default.split(',') except (KeyError, AttributeError) as err: - raise constr.InvalidSchemaError(_('Default must be a ' - 'comma-delimited list ' - 'string: %s') % err) + raise exception.InvalidSchemaError( + message=_('Default must be a comma-delimited list ' + 'string: %s') % err) try: self.validate_constraints(default_value, context) except (ValueError, TypeError, exception.StackValidationFailed) as exc: - raise constr.InvalidSchemaError(_('Invalid default ' - '%(default)s (%(exc)s)') % - dict(default=self.default, - exc=exc)) + raise exception.InvalidSchemaError( + message=_('Invalid default %(default)s (%(exc)s)') % + dict(default=self.default, exc=exc)) def set_default(self, default=None): super(Schema, self).set_default(default) @@ -101,12 +100,12 @@ class Schema(constr.Schema): @staticmethod def _check_dict(schema_dict, allowed_keys, entity): if not isinstance(schema_dict, dict): - raise constr.InvalidSchemaError( - _("Invalid %s, expected a mapping") % entity) + raise exception.InvalidSchemaError( + message=_("Invalid %s, expected a mapping") % entity) for key in schema_dict: if key not in allowed_keys: - raise constr.InvalidSchemaError( - _("Invalid key '%(key)s' for %(entity)s") % { + raise exception.InvalidSchemaError( + message=_("Invalid key '%(key)s' for %(entity)s") % { "key": key, "entity": entity}) @classmethod @@ -116,8 +115,9 @@ class Schema(constr.Schema): "parameter (%s)" % param_name) if cls.TYPE not in schema_dict: - raise constr.InvalidSchemaError( - _("Missing parameter type for parameter: %s") % param_name) + raise exception.InvalidSchemaError( + message=_("Missing parameter type for parameter: %s") % + param_name) @classmethod def from_dict(cls, param_name, schema_dict): diff --git a/heat/engine/properties.py b/heat/engine/properties.py index a80e4e548..a52b28cd7 100644 --- a/heat/engine/properties.py +++ b/heat/engine/properties.py @@ -74,7 +74,8 @@ class Schema(constr.Schema): unknown = [k for k in schema_dict if k not in SCHEMA_KEYS] if unknown: - raise constr.InvalidSchemaError(_('Unknown key(s) %s') % unknown) + raise exception.InvalidSchemaError( + message=_('Unknown key(s) %s') % unknown) def constraints(): def get_num(key): @@ -95,7 +96,8 @@ class Schema(constr.Schema): try: data_type = schema_dict[TYPE] except KeyError: - raise constr.InvalidSchemaError(_('No %s specified') % TYPE) + raise exception.InvalidSchemaError( + message=_('No %s specified') % TYPE) if SCHEMA in schema_dict: if data_type == Schema.LIST: @@ -104,11 +106,9 @@ class Schema(constr.Schema): schema_dicts = schema_dict[SCHEMA].items() ss = dict((n, cls.from_legacy(sd)) for n, sd in schema_dicts) else: - raise constr.InvalidSchemaError(_('%(schema)s supplied for ' - ' %(type)s %(data)s') % - dict(schema=SCHEMA, - type=TYPE, - data=data_type)) + raise exception.InvalidSchemaError( + message=_('%(schema)s supplied for %(type)s %(data)s') % + dict(schema=SCHEMA, type=TYPE, data=data_type)) else: ss = None diff --git a/heat/tests/test_constraints.py b/heat/tests/test_constraints.py index 134406394..f2020e841 100644 --- a/heat/tests/test_constraints.py +++ b/heat/tests/test_constraints.py @@ -181,11 +181,11 @@ class SchemaTest(testtools.TestCase): self.assertEqual(d, dict(l)) def test_invalid_type(self): - self.assertRaises(constraints.InvalidSchemaError, constraints.Schema, + self.assertRaises(exception.InvalidSchemaError, constraints.Schema, 'Fish') def test_schema_invalid_type(self): - self.assertRaises(constraints.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, constraints.Schema, 'String', schema=constraints.Schema('String')) @@ -193,14 +193,14 @@ class SchemaTest(testtools.TestCase): def test_range_invalid_type(self): schema = constraints.Schema('String', constraints=[constraints.Range(1, 10)]) - err = self.assertRaises(constraints.InvalidSchemaError, + err = self.assertRaises(exception.InvalidSchemaError, schema.validate) self.assertIn('Range constraint invalid for String', str(err)) def test_length_invalid_type(self): schema = constraints.Schema('Integer', constraints=[constraints.Length(1, 10)]) - err = self.assertRaises(constraints.InvalidSchemaError, + err = self.assertRaises(exception.InvalidSchemaError, schema.validate) self.assertIn('Length constraint invalid for Integer', str(err)) @@ -209,21 +209,21 @@ class SchemaTest(testtools.TestCase): 'Integer', constraints=[constraints.AllowedPattern('[0-9]*')] ) - err = self.assertRaises(constraints.InvalidSchemaError, + err = self.assertRaises(exception.InvalidSchemaError, schema.validate) self.assertIn('AllowedPattern constraint invalid for Integer', str(err)) def test_range_vals_invalid_type(self): - self.assertRaises(constraints.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, constraints.Range, '1', 10) - self.assertRaises(constraints.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, constraints.Range, 1, '10') def test_length_vals_invalid_type(self): - self.assertRaises(constraints.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, constraints.Length, '1', 10) - self.assertRaises(constraints.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, constraints.Length, 1, '10') def test_schema_validate_good(self): @@ -236,7 +236,7 @@ class SchemaTest(testtools.TestCase): s = constraints.Schema(constraints.Schema.STRING, 'A string', default='wibble', required=True, constraints=[constraints.Range(max=4)]) - err = self.assertRaises(constraints.InvalidSchemaError, s.validate) + err = self.assertRaises(exception.InvalidSchemaError, s.validate) self.assertIn('Range constraint invalid for String', str(err)) def test_schema_nested_validate_good(self): @@ -253,7 +253,7 @@ class SchemaTest(testtools.TestCase): constraints=[constraints.Range(max=4)]) s = constraints.Schema(constraints.Schema.MAP, 'A map', schema={'Foo': nested}) - err = self.assertRaises(constraints.InvalidSchemaError, s.validate) + err = self.assertRaises(exception.InvalidSchemaError, s.validate) self.assertIn('Range constraint invalid for String', str(err)) def test_allowed_values_numeric_int(self): diff --git a/heat/tests/test_hot.py b/heat/tests/test_hot.py index 3cfe52582..5098bf391 100644 --- a/heat/tests/test_hot.py +++ b/heat/tests/test_hot.py @@ -17,7 +17,6 @@ import six from heat.common import exception from heat.common import identifier from heat.common import template_format -from heat.engine import constraints from heat.engine import environment from heat.engine import function from heat.engine.hot import functions as hot_functions @@ -1235,7 +1234,7 @@ class HOTParamValidatorTest(HeatTestCase): schema = hot_param.HOTParamSchema.from_dict('db_port', param['db_port']) - err = self.assertRaises(constraints.InvalidSchemaError, + err = self.assertRaises(exception.InvalidSchemaError, schema.validate) self.assertIn(range_desc, str(err)) @@ -1247,7 +1246,7 @@ class HOTParamValidatorTest(HeatTestCase): foo: bar ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual("Invalid key 'foo' for parameter (param1)", str(error)) @@ -1260,7 +1259,7 @@ class HOTParamValidatorTest(HeatTestCase): description: Hi! ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual("Missing parameter type for parameter: param1", str(error)) @@ -1273,7 +1272,7 @@ class HOTParamValidatorTest(HeatTestCase): type: Unicode ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "Invalid type (Unicode)", str(error)) @@ -1289,7 +1288,7 @@ class HOTParamValidatorTest(HeatTestCase): default: foo ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "Invalid key 'allowed_valus' for parameter constraints", @@ -1305,7 +1304,7 @@ class HOTParamValidatorTest(HeatTestCase): default: foo ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "Invalid parameter constraints for parameter param1, " @@ -1321,7 +1320,7 @@ class HOTParamValidatorTest(HeatTestCase): default: foo ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "Invalid parameter constraints, expected a mapping", str(error)) @@ -1337,7 +1336,7 @@ class HOTParamValidatorTest(HeatTestCase): default: foo ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual("No constraint expressed", str(error)) @@ -1352,7 +1351,7 @@ class HOTParamValidatorTest(HeatTestCase): default: foo ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "Invalid range constraint, expected a mapping", str(error)) @@ -1368,7 +1367,7 @@ class HOTParamValidatorTest(HeatTestCase): default: 1 ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "Invalid key 'foo' for range constraint", str(error)) @@ -1384,7 +1383,7 @@ class HOTParamValidatorTest(HeatTestCase): default: foo ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "Invalid length constraint, expected a mapping", str(error)) @@ -1400,7 +1399,7 @@ class HOTParamValidatorTest(HeatTestCase): default: foo ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "Invalid key 'foo' for length constraint", str(error)) @@ -1416,7 +1415,7 @@ class HOTParamValidatorTest(HeatTestCase): default: foo ''') error = self.assertRaises( - constraints.InvalidSchemaError, parameters.Parameters, + exception.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual( "AllowedPattern must be a string", str(error)) diff --git a/heat/tests/test_parameters.py b/heat/tests/test_parameters.py index 2530995e6..c4cadc19d 100644 --- a/heat/tests/test_parameters.py +++ b/heat/tests/test_parameters.py @@ -17,7 +17,6 @@ import testtools from heat.common import exception from heat.common import identifier -from heat.engine import constraints as constr from heat.engine import parameters from heat.engine import template @@ -51,8 +50,8 @@ class ParameterTest(testtools.TestCase): self.assertIsInstance(p, parameters.JsonParam) def test_new_bad_type(self): - self.assertRaises(constr.InvalidSchemaError, self.new_parameter, 'p', - {'Type': 'List'}, validate_value=False) + self.assertRaises(exception.InvalidSchemaError, self.new_parameter, + 'p', {'Type': 'List'}, validate_value=False) def test_default_no_override(self): p = self.new_parameter('defaulted', {'Type': 'String', @@ -75,7 +74,7 @@ class ParameterTest(testtools.TestCase): 'AllowedValues': ['foo'], 'ConstraintDescription': 'wibble', 'Default': 'bar'} - err = self.assertRaises(constr.InvalidSchemaError, + err = self.assertRaises(exception.InvalidSchemaError, self.new_parameter, 'p', schema, 'foo') self.assertIn('wibble', str(err)) @@ -470,7 +469,7 @@ class ParametersTest(testtools.TestCase): params = {'Parameters': {'Foo': {'Type': 'String'}, 'NoAttr': 'No attribute.', 'Bar': {'Type': 'Number', 'Default': '1'}}} - self.assertRaises(constr.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, self.new_parameters, 'test', params) @@ -479,14 +478,14 @@ class ParametersTest(testtools.TestCase): class ParameterSchemaTest(testtools.TestCase): def test_validate_schema_wrong_key(self): - error = self.assertRaises(constr.InvalidSchemaError, + error = self.assertRaises(exception.InvalidSchemaError, parameters.Schema.from_dict, 'param_name', {"foo": "bar"}) self.assertEqual("Invalid key 'foo' for parameter (param_name)", str(error)) def test_validate_schema_no_type(self): - error = self.assertRaises(constr.InvalidSchemaError, + error = self.assertRaises(exception.InvalidSchemaError, parameters.Schema.from_dict, 'broken', {"Description": "Hi!"}) diff --git a/heat/tests/test_properties.py b/heat/tests/test_properties.py index c6fb3931c..619d43526 100644 --- a/heat/tests/test_properties.py +++ b/heat/tests/test_properties.py @@ -310,7 +310,7 @@ class PropertySchemaTest(testtools.TestCase): self.assertEqual('wibble', ss.default) def test_from_legacy_invalid_key(self): - self.assertRaises(constraints.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, properties.Schema.from_legacy, {'Type': 'String', 'Foo': 'Bar'}) @@ -604,11 +604,11 @@ class PropertyTest(testtools.TestCase): self.assertEqual('String', p.type()) def test_bad_type(self): - self.assertRaises(constraints.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, properties.Property, {'Type': 'Fish'}) def test_bad_key(self): - self.assertRaises(constraints.InvalidSchemaError, + self.assertRaises(exception.InvalidSchemaError, properties.Property, {'Type': 'String', 'Foo': 'Bar'})