FaultWrapper error mapping supports parent classes
Now the FaultWrapper middleware maps exceptions to webob ones using a recursive method: if the exception itself is not in the error_map dictionary, the method searches the first parent exception that has a match in the same dictionary. Without this change "any exception that subclasses an exception mapped in FaultWrapper.error_map will raise an http 500 error instead of the mapped http error. FaultWrapper should also map against all parent types of an exception." Two unit test were added: one to reproduce the bug and another to test the fix when remote exceptions are handled. Change-Id: I387e0acf35a4d6361494117218d5129f00d77eb7 Closes-Bug: #1232885
This commit is contained in:
parent
186b5a471b
commit
27e518c5c8
@ -79,6 +79,15 @@ class FaultWrapper(wsgi.Middleware):
|
||||
'Invalid': webob.exc.HTTPBadRequest,
|
||||
}
|
||||
|
||||
def _map_exception_to_error(self, class_exception):
|
||||
if class_exception == Exception:
|
||||
return webob.exc.HTTPInternalServerError
|
||||
|
||||
if class_exception.__name__ not in self.error_map:
|
||||
return self._map_exception_to_error(class_exception.__base__)
|
||||
|
||||
return self.error_map[class_exception.__name__]
|
||||
|
||||
def _error(self, ex):
|
||||
|
||||
trace = None
|
||||
@ -107,8 +116,7 @@ class FaultWrapper(wsgi.Middleware):
|
||||
trace = msg_trace
|
||||
|
||||
if not webob_exc:
|
||||
webob_exc = self.error_map.get(ex_type,
|
||||
webob.exc.HTTPInternalServerError)
|
||||
webob_exc = self._map_exception_to_error(ex.__class__)
|
||||
|
||||
error = {
|
||||
'code': webob_exc.code,
|
||||
|
@ -20,6 +20,10 @@ from oslo.config import cfg
|
||||
import heat.api.middleware.fault as fault
|
||||
|
||||
|
||||
class StackNotFoundChild(heat_exc.StackNotFound):
|
||||
pass
|
||||
|
||||
|
||||
class FaultMiddlewareTest(HeatTestCase):
|
||||
|
||||
def test_openstack_exception_with_kwargs(self):
|
||||
@ -85,3 +89,55 @@ class FaultMiddlewareTest(HeatTestCase):
|
||||
'explanation': 'The resource could not be found.',
|
||||
'title': 'Not Found'}
|
||||
self.assertEqual(expected, msg)
|
||||
|
||||
def test_should_not_ignore_parent_classes(self):
|
||||
wrapper = fault.FaultWrapper(None)
|
||||
|
||||
msg = wrapper._error(StackNotFoundChild(stack_name='a'))
|
||||
expected = {'code': 404,
|
||||
'error': {'message': 'The Stack (a) could not be found.',
|
||||
'traceback': None,
|
||||
'type': 'StackNotFoundChild'},
|
||||
'explanation': 'The resource could not be found.',
|
||||
'title': 'Not Found'}
|
||||
self.assertEqual(expected, msg)
|
||||
|
||||
def test_internal_server_error_when_exeption_and_parents_not_mapped(self):
|
||||
wrapper = fault.FaultWrapper(None)
|
||||
|
||||
class NotMappedException(Exception):
|
||||
pass
|
||||
|
||||
msg = wrapper._error(NotMappedException('A message'))
|
||||
expected = {'code': 500,
|
||||
'error': {'message': u'A message',
|
||||
'traceback': None,
|
||||
'type': 'NotMappedException'},
|
||||
'explanation': ('The server has either erred or is '
|
||||
'incapable of performing the requested '
|
||||
'operation.'),
|
||||
'title': 'Internal Server Error'}
|
||||
self.assertEqual(expected, msg)
|
||||
|
||||
def test_should_not_ignore_parent_classes_even_for_remote_ones(self):
|
||||
# We want tracebacks
|
||||
cfg.CONF.set_override('debug', True)
|
||||
cfg.CONF.set_override('allowed_rpc_exception_modules',
|
||||
['heat.tests.test_fault_middleware'])
|
||||
|
||||
error = StackNotFoundChild(stack_name='a')
|
||||
exc_info = (type(error), error, None)
|
||||
serialized = rpc_common.serialize_remote_exception(exc_info)
|
||||
remote_error = rpc_common.deserialize_remote_exception(cfg.CONF,
|
||||
serialized)
|
||||
|
||||
wrapper = fault.FaultWrapper(None)
|
||||
msg = wrapper._error(remote_error)
|
||||
expected_message, expected_traceback = str(remote_error).split('\n', 1)
|
||||
expected = {'code': 404,
|
||||
'error': {'message': expected_message,
|
||||
'traceback': expected_traceback,
|
||||
'type': 'StackNotFoundChild'},
|
||||
'explanation': 'The resource could not be found.',
|
||||
'title': 'Not Found'}
|
||||
self.assertEqual(expected, msg)
|
||||
|
Loading…
Reference in New Issue
Block a user