]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
make heat-api return a parsable error
authorJianing YANG <jianingy@unitedstack.com>
Thu, 11 Jul 2013 15:17:52 +0000 (23:17 +0800)
committerSteve Baker <sbaker@redhat.com>
Tue, 23 Jul 2013 23:02:22 +0000 (11:02 +1200)
add a wsgi middleware (faultwrap) that catches exceptions
and transform those exceptions into a parsable format
according to 'Content-Type' of the request.

Fixes bug 1158598
Implements blueprint exception-formatting

Change-Id: Iacdb8cc119b250ff1e39c99b7a7f66fd4c35e7d9

etc/heat/api-paste.ini
heat/api/middleware/fault.py [new file with mode: 0644]
heat/api/openstack/__init__.py
heat/api/openstack/v1/util.py
heat/common/config.py
heat/tests/test_api_openstack_v1.py

index 72e5e579a45cf55a19ea935a488f710c1be69b32..273e94c14fb02ad615ae5b148b3615e9f987f09e 100644 (file)
@@ -1,7 +1,7 @@
 
 # heat-api pipeline
 [pipeline:heat-api]
-pipeline = versionnegotiation authtoken context apiv1app
+pipeline = faultwrap versionnegotiation authtoken context apiv1app
 
 # heat-api pipeline for standalone heat
 # ie. uses alternative auth backend that authenticates users against keystone
@@ -12,7 +12,7 @@ pipeline = versionnegotiation authtoken context apiv1app
 #   flavor = standalone
 #
 [pipeline:heat-api-standalone]
-pipeline = versionnegotiation authpassword context apiv1app
+pipeline = faultwrap versionnegotiation authpassword context apiv1app
 
 # heat-api pipeline for custom cloud backends
 # i.e. in heat-api.conf:
@@ -20,7 +20,7 @@ pipeline = versionnegotiation authpassword context apiv1app
 #   flavor = custombackend
 #
 [pipeline:heat-api-custombackend]
-pipeline = versionnegotiation context custombackendauth apiv1app
+pipeline = faultwrap versionnegotiation context custombackendauth apiv1app
 
 # heat-api-cfn pipeline
 [pipeline:heat-api-cfn]
@@ -56,6 +56,10 @@ heat.app_factory = heat.api.cloudwatch:API
 paste.filter_factory = heat.common.wsgi:filter_factory
 heat.filter_factory = heat.api.openstack:version_negotiation_filter
 
+[filter:faultwrap]
+paste.filter_factory = heat.common.wsgi:filter_factory
+heat.filter_factory = heat.api.openstack:faultwrap_filter
+
 [filter:cfnversionnegotiation]
 paste.filter_factory = heat.common.wsgi:filter_factory
 heat.filter_factory = heat.api.cfn:version_negotiation_filter
diff --git a/heat/api/middleware/fault.py b/heat/api/middleware/fault.py
new file mode 100644 (file)
index 0000000..370fe49
--- /dev/null
@@ -0,0 +1,108 @@
+# -*- encoding: utf-8 -*-
+#
+# Copyright © 2013 Unitedstack Inc.
+#
+# Author: Jianing YANG (jianingy@unitedstack.com)
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""A middleware that turns exceptions into parsable string. Inspired by
+Cinder's faultwrapper
+"""
+
+import traceback
+import webob
+
+from heat.openstack.common import log as logging
+import heat.openstack.common.rpc.common as rpc_common
+
+from heat.common import wsgi
+
+logger = logging.getLogger(__name__)
+
+
+class Fault(object):
+
+    def __init__(self, error):
+        self.error = error
+
+    @webob.dec.wsgify(RequestClass=wsgi.Request)
+    def __call__(self, req):
+        if req.content_type == 'application/xml':
+            serializer = wsgi.XMLResponseSerializer()
+        else:
+            serializer = wsgi.JSONResponseSerializer()
+        resp = webob.Response(request=req)
+        default_webob_exc = webob.exc.HTTPInternalServerError()
+        resp.status_code = self.error.get('code', default_webob_exc.code)
+        serializer.default(resp, self.error)
+        return resp
+
+
+class FaultWrapper(wsgi.Middleware):
+    """Replace error body with something the client can parse."""
+
+    error_map = {
+        'AttributeError': webob.exc.HTTPBadRequest,
+        'ValueError': webob.exc.HTTPBadRequest,
+        'StackNotFound': webob.exc.HTTPNotFound,
+        'ResourceNotFound': webob.exc.HTTPNotFound,
+        'ResourceNotAvailable': webob.exc.HTTPNotFound,
+        'PhysicalResourceNotFound': webob.exc.HTTPNotFound,
+        'InvalidTenant': webob.exc.HTTPForbidden,
+        'StackExists': webob.exc.HTTPConflict,
+        'StackValidationFailed': webob.exc.HTTPBadRequest,
+        'InvalidTemplateReference': webob.exc.HTTPBadRequest,
+        'UnknownUserParameter': webob.exc.HTTPBadRequest,
+        'RevertFailed': webob.exc.HTTPInternalServerError,
+        'ServerBuildFailed': webob.exc.HTTPInternalServerError,
+        'NotSupported': webob.exc.HTTPBadRequest,
+        'MissingCredentialError': webob.exc.HTTPBadRequest,
+        'UserParameterMissing': webob.exc.HTTPBadRequest,
+    }
+
+    def _error(self, ex):
+
+        ex_type = ex.__class__.__name__
+
+        if ex_type.endswith(rpc_common._REMOTE_POSTFIX):
+            ex_type = ex_type[:-len(rpc_common._REMOTE_POSTFIX)]
+
+        message = str(ex)
+        if message.find('\n') > -1:
+            message, trace = message.split('\n', 1)
+        else:
+            message = str(ex)
+            trace = traceback.format_exc()
+
+        webob_exc = self.error_map.get(ex_type,
+                                       webob.exc.HTTPInternalServerError)
+
+        error = {
+            'code': webob_exc.code,
+            'title': webob_exc.title,
+            'explanation': webob_exc.explanation,
+            'error': {
+                'message': message,
+                'type': ex_type,
+                'traceback': trace,
+            }
+        }
+
+        return error
+
+    def process_request(self, req):
+        try:
+            return req.get_response(self.application)
+        except Exception as exc:
+            return req.get_response(Fault(self._error(exc)))
index ff263f7c77cbc6fbe07507f940598a059895caec..dfb897f34d707886e6b8d8e1355a5d44d5497caf 100644 (file)
 #    under the License.
 
 from heat.api.middleware.version_negotiation import VersionNegotiationFilter
+from heat.api.middleware.fault import FaultWrapper
 from heat.api.openstack import versions
 
 
 def version_negotiation_filter(app, conf, **local_conf):
     return VersionNegotiationFilter(versions.Controller, app,
                                     conf, **local_conf)
+
+
+def faultwrap_filter(app, conf, **local_conf):
+    return FaultWrapper(app)
index a344154fd6dfba7f145552a9835b7b467b3970ec..d87e6adf57c1d56d698e5a902e29b297374615dd 100644 (file)
@@ -67,31 +67,10 @@ def make_link(req, identity, relationship='self'):
 
 
 def remote_error(ex):
-    """
-    Map rpc_common.RemoteError exceptions returned by the engine
-    to webob exceptions which can be used to return
-    properly formatted error responses.
+    """The RemoteError mapping work has been moved to heat.api.middleware.fault
+    which handles error formating now. This function will be deprecated
+    in the future, so please raise exceptions directly.
     """
 
-    error_map = {
-        'AttributeError': exc.HTTPBadRequest,
-        'ValueError': exc.HTTPBadRequest,
-        'StackNotFound': exc.HTTPNotFound,
-        'ResourceNotFound': exc.HTTPNotFound,
-        'ResourceNotAvailable': exc.HTTPNotFound,
-        'PhysicalResourceNotFound': exc.HTTPNotFound,
-        'InvalidTenant': exc.HTTPForbidden,
-        'StackExists': exc.HTTPConflict,
-        'StackValidationFailed': exc.HTTPBadRequest,
-        'InvalidTemplateReference': exc.HTTPBadRequest,
-        'UnknownUserParameter': exc.HTTPBadRequest,
-        'RevertFailed': exc.HTTPInternalServerError,
-        'ServerBuildFailed': exc.HTTPInternalServerError,
-        'NotSupported': exc.HTTPBadRequest,
-        'MissingCredentialError': exc.HTTPBadRequest,
-        'UserParameterMissing': exc.HTTPBadRequest,
-    }
-
-    Exc = error_map.get(ex.exc_type, exc.HTTPInternalServerError)
-
-    raise Exc(str(ex))
+    # TODO(jianingy): add a deprecated warning here to inform others.
+    raise ex
index d32025a656fedeb2ac99aa302065d8f47ba0bb41..8113ec1289d616bbd77c39756b70eb57dbf402af 100644 (file)
@@ -116,6 +116,14 @@ cfg.CONF.register_opts(rpc_opts)
 cfg.CONF.register_group(paste_deploy_group)
 cfg.CONF.register_opts(paste_deploy_opts, group=paste_deploy_group)
 
+# TODO(jianingy): I'll set allowed_rpc_exception_modules here for now.
+#                 after figure out why rpc_set_default was not called,
+#                 I'll move these settings into rpc_set_default()
+allowed_rpc_exception_modules = cfg.CONF.allowed_rpc_exception_modules
+allowed_rpc_exception_modules.append('heat.common.exception')
+cfg.CONF.set_default(name='allowed_rpc_exception_modules',
+                     default=allowed_rpc_exception_modules)
+
 
 def rpc_set_default():
     rpc.set_defaults(control_exchange='heat')
index 10b1b0c91945342f10ee3e4c2f797e9913cd9e60..ce38c4eb3cccb228845e9433484e60f482a8d3c0 100644 (file)
@@ -19,9 +19,11 @@ import webob.exc
 
 from heat.common import identifier
 from heat.openstack.common import rpc
-import heat.openstack.common.rpc.common as rpc_common
+
+from heat.common import exception as heat_exc
 from heat.common.wsgi import Request
 from heat.common import urlfetch
+from heat.openstack.common.rpc import common as rpc_common
 from heat.rpc import api as rpc_api
 from heat.tests.common import HeatTestCase
 
@@ -32,6 +34,33 @@ import heat.api.openstack.v1.events as events
 import heat.api.openstack.v1.actions as actions
 from heat.tests.utils import dummy_context
 
+import heat.api.middleware.fault as fault
+
+
+def request_with_middleware(middleware, func, req, *args, **kwargs):
+
+    @webob.dec.wsgify
+    def _app(req):
+        return func(req, *args, **kwargs)
+
+    resp = middleware(_app).process_request(req)
+    return resp
+
+
+def remote_error(ex_type, message=''):
+    """convert rpc original exception to the one with _Remote suffix."""
+
+    # NOTE(jianingy): this function helps simulate the real world exceptions
+
+    module = ex_type().__class__.__module__
+    str_override = lambda self: "%s\n<Traceback>" % message
+    new_ex_type = type(ex_type.__name__ + rpc_common._REMOTE_POSTFIX,
+                       (ex_type,),
+                       {'__str__': str_override, '__unicode__': str_override})
+
+    new_ex_type.__module__ = '%s%s' % (module, rpc_common._REMOTE_POSTFIX)
+    return new_ex_type()
+
 
 class InstantiationDataTest(HeatTestCase):
 
@@ -369,12 +398,15 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'list_stacks',
                   'args': {},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("AttributeError"))
+                 None).AndRaise(remote_error(AttributeError))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPBadRequest,
-                          self.controller.index,
-                          req, tenant_id=self.tenant)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.index,
+                                       req, tenant_id=self.tenant)
+
+        self.assertEqual(resp.json['code'], 400)
+        self.assertEqual(resp.json['error']['type'], 'AttributeError')
         self.m.VerifyAll()
 
     def test_index_rmt_interr(self):
@@ -386,12 +418,15 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'list_stacks',
                   'args': {},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("Exception"))
+                 None).AndRaise(remote_error(Exception))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPInternalServerError,
-                          self.controller.index,
-                          req, tenant_id=self.tenant)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.index,
+                                       req, tenant_id=self.tenant)
+
+        self.assertEqual(resp.json['code'], 500)
+        self.assertEqual(resp.json['error']['type'], 'Exception')
         self.m.VerifyAll()
 
     def test_create(self):
@@ -488,7 +523,7 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                            'files': {},
                            'args': {'timeout_mins': 30}},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("AttributeError"))
+                 None).AndRaise(remote_error(AttributeError))
         rpc.call(req.context, self.topic,
                  {'namespace': None,
                   'method': 'create_stack',
@@ -498,7 +533,7 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                            'files': {},
                            'args': {'timeout_mins': 30}},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("UnknownUserParameter"))
+                 None).AndRaise(remote_error(heat_exc.UnknownUserParameter))
         rpc.call(req.context, self.topic,
                  {'namespace': None,
                   'method': 'create_stack',
@@ -508,20 +543,28 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                            'files': {},
                            'args': {'timeout_mins': 30}},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("UserParameterMissing"))
-
+                 None).AndRaise(remote_error(heat_exc.UserParameterMissing))
         self.m.ReplayAll()
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.create,
+                                       req, tenant_id=self.tenant, body=body)
 
-        self.assertRaises(webob.exc.HTTPBadRequest,
-                          self.controller.create,
-                          req, tenant_id=self.tenant, body=body)
-        self.assertRaises(webob.exc.HTTPBadRequest,
-                          self.controller.create,
-                          req, tenant_id=self.tenant, body=body)
-        self.assertRaises(webob.exc.HTTPBadRequest,
-                          self.controller.create,
-                          req, tenant_id=self.tenant, body=body)
+        self.assertEqual(resp.json['code'], 400)
+        self.assertEqual(resp.json['error']['type'], 'AttributeError')
+
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.create,
+                                       req, tenant_id=self.tenant, body=body)
+
+        self.assertEqual(resp.json['code'], 400)
+        self.assertEqual(resp.json['error']['type'], 'UnknownUserParameter')
+
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.create,
+                                       req, tenant_id=self.tenant, body=body)
 
+        self.assertEqual(resp.json['code'], 400)
+        self.assertEqual(resp.json['error']['type'], 'UserParameterMissing')
         self.m.VerifyAll()
 
     def test_create_err_existing(self):
@@ -546,12 +589,15 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                            'files': {},
                            'args': {'timeout_mins': 30}},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackExists"))
+                 None).AndRaise(remote_error(heat_exc.StackExists))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPConflict,
-                          self.controller.create,
-                          req, tenant_id=self.tenant, body=body)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.create,
+                                       req, tenant_id=self.tenant, body=body)
+
+        self.assertEqual(resp.json['code'], 409)
+        self.assertEqual(resp.json['error']['type'], 'StackExists')
         self.m.VerifyAll()
 
     def test_create_err_engine(self):
@@ -576,14 +622,15 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                            'files': {},
                            'args': {'timeout_mins': 30}},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError(
-                     'StackValidationFailed',
-                     'Something went wrong'))
+                 None).AndRaise(remote_error(heat_exc.StackValidationFailed))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPBadRequest,
-                          self.controller.create,
-                          req, tenant_id=self.tenant, body=body)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.create,
+                                       req, tenant_id=self.tenant, body=body)
+
+        self.assertEqual(resp.json['code'], 400)
+        self.assertEqual(resp.json['error']['type'], 'StackValidationFailed')
         self.m.VerifyAll()
 
     def test_lookup(self):
@@ -637,11 +684,16 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'identify_stack',
                   'args': {'stack_name': stack_name},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound, self.controller.lookup,
-                          req, tenant_id=self.tenant, stack_name=stack_name)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.lookup,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_name)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_lookup_resource(self):
@@ -681,12 +733,17 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'identify_stack',
                   'args': {'stack_name': stack_name},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound, self.controller.lookup,
-                          req, tenant_id=self.tenant, stack_name=stack_name,
-                          path='resources')
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.lookup,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_name,
+                                       path='resources')
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_show(self):
@@ -769,14 +826,17 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'show_stack',
                   'args': {'stack_identity': dict(identity)},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.show,
-                          req, tenant_id=identity.tenant,
-                          stack_name=identity.stack_name,
-                          stack_id=identity.stack_id)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.show,
+                                       req, tenant_id=identity.tenant,
+                                       stack_name=identity.stack_name,
+                                       stack_id=identity.stack_id)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_show_invalidtenant(self):
@@ -790,14 +850,17 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'show_stack',
                   'args': {'stack_identity': dict(identity)},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("InvalidTenant"))
+                 None).AndRaise(remote_error(heat_exc.InvalidTenant))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPForbidden,
-                          self.controller.show,
-                          req, tenant_id=identity.tenant,
-                          stack_name=identity.stack_name,
-                          stack_id=identity.stack_id)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.show,
+                                       req, tenant_id=identity.tenant,
+                                       stack_name=identity.stack_name,
+                                       stack_id=identity.stack_id)
+
+        self.assertEqual(resp.json['code'], 403)
+        self.assertEqual(resp.json['error']['type'], 'InvalidTenant')
         self.m.VerifyAll()
 
     def test_get_template(self):
@@ -832,15 +895,18 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'get_template',
                   'args': {'stack_identity': dict(identity)},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
 
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.template,
-                          req, tenant_id=identity.tenant,
-                          stack_name=identity.stack_name,
-                          stack_id=identity.stack_id)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.template,
+                                       req, tenant_id=identity.tenant,
+                                       stack_name=identity.stack_name,
+                                       stack_id=identity.stack_id)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_update(self):
@@ -902,15 +968,18 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                            'files': {},
                            'args': {'timeout_mins': 30}},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.update,
-                          req, tenant_id=identity.tenant,
-                          stack_name=identity.stack_name,
-                          stack_id=identity.stack_id,
-                          body=body)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.update,
+                                       req, tenant_id=identity.tenant,
+                                       stack_name=identity.stack_name,
+                                       stack_id=identity.stack_id,
+                                       body=body)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_delete(self):
@@ -959,14 +1028,17 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'delete_stack',
                   'args': {'stack_identity': dict(identity)},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.delete,
-                          req, tenant_id=identity.tenant,
-                          stack_name=identity.stack_name,
-                          stack_id=identity.stack_id)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.delete,
+                                       req, tenant_id=identity.tenant,
+                                       stack_name=identity.stack_name,
+                                       stack_id=identity.stack_id)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_validate_template(self):
@@ -1056,12 +1128,14 @@ class StackControllerTest(ControllerTest, HeatTestCase):
                   'method': 'list_resource_types',
                   'args': {},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("ValueError"))
+                 None).AndRaise(remote_error(heat_exc.ServerError))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPInternalServerError,
-                          self.controller.list_resource_types,
-                          req, tenant_id=self.tenant)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.list_resource_types,
+                                       req, tenant_id=self.tenant)
+        self.assertEqual(resp.json['code'], 500)
+        self.assertEqual(resp.json['error']['type'], 'ServerError')
         self.m.VerifyAll()
 
 
@@ -1163,14 +1237,17 @@ class ResourceControllerTest(ControllerTest, HeatTestCase):
                   'method': 'list_stack_resources',
                   'args': {'stack_identity': stack_identity},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.index,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.index,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_show(self):
@@ -1248,15 +1325,18 @@ class ResourceControllerTest(ControllerTest, HeatTestCase):
                   'args': {'stack_identity': stack_identity,
                            'resource_name': res_name},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.show,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id,
-                          resource_name=res_name)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.show,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id,
+                                       resource_name=res_name)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_show_nonexist_resource(self):
@@ -1275,15 +1355,18 @@ class ResourceControllerTest(ControllerTest, HeatTestCase):
                   'args': {'stack_identity': stack_identity,
                            'resource_name': res_name},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("ResourceNotFound"))
+                 None).AndRaise(remote_error(heat_exc.ResourceNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.show,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id,
-                          resource_name=res_name)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.show,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id,
+                                       resource_name=res_name)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'ResourceNotFound')
         self.m.VerifyAll()
 
     def test_show_uncreated_resource(self):
@@ -1302,15 +1385,18 @@ class ResourceControllerTest(ControllerTest, HeatTestCase):
                   'args': {'stack_identity': stack_identity,
                            'resource_name': res_name},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("ResourceNotAvailable"))
+                 None).AndRaise(remote_error(heat_exc.ResourceNotAvailable))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.show,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id,
-                          resource_name=res_name)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.show,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id,
+                                       resource_name=res_name)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'ResourceNotAvailable')
         self.m.VerifyAll()
 
     def test_metadata_show(self):
@@ -1373,15 +1459,18 @@ class ResourceControllerTest(ControllerTest, HeatTestCase):
                   'args': {'stack_identity': stack_identity,
                            'resource_name': res_name},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.metadata,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id,
-                          resource_name=res_name)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.metadata,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id,
+                                       resource_name=res_name)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_metadata_show_nonexist_resource(self):
@@ -1400,15 +1489,18 @@ class ResourceControllerTest(ControllerTest, HeatTestCase):
                   'args': {'stack_identity': stack_identity,
                            'resource_name': res_name},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("ResourceNotFound"))
+                 None).AndRaise(remote_error(heat_exc.ResourceNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.metadata,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id,
-                          resource_name=res_name)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.metadata,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id,
+                                       resource_name=res_name)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'ResourceNotFound')
         self.m.VerifyAll()
 
 
@@ -1577,14 +1669,17 @@ class EventControllerTest(ControllerTest, HeatTestCase):
                   'method': 'list_events',
                   'args': {'stack_identity': stack_identity},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.index,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.index,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
     def test_index_resource_nonexist(self):
@@ -1818,15 +1913,19 @@ class EventControllerTest(ControllerTest, HeatTestCase):
                   'method': 'list_events',
                   'args': {'stack_identity': stack_identity},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("StackNotFound"))
+                 None).AndRaise(remote_error(heat_exc.StackNotFound))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPNotFound,
-                          self.controller.show,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id,
-                          resource_name=res_name, event_id=event_id)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.show,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id,
+                                       resource_name=res_name,
+                                       event_id=event_id)
+
+        self.assertEqual(resp.json['code'], 404)
+        self.assertEqual(resp.json['error']['type'], 'StackNotFound')
         self.m.VerifyAll()
 
 
@@ -2233,15 +2332,18 @@ class ActionControllerTest(ControllerTest, HeatTestCase):
                   'method': 'stack_suspend',
                   'args': {'stack_identity': stack_identity},
                   'version': self.api_version},
-                 None).AndRaise(rpc_common.RemoteError("AttributeError"))
+                 None).AndRaise(remote_error(AttributeError))
         self.m.ReplayAll()
 
-        self.assertRaises(webob.exc.HTTPBadRequest, self.controller.action,
-                          req, tenant_id=self.tenant,
-                          stack_name=stack_identity.stack_name,
-                          stack_id=stack_identity.stack_id,
-                          body=body)
+        resp = request_with_middleware(fault.FaultWrapper,
+                                       self.controller.action,
+                                       req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id,
+                                       body=body)
 
+        self.assertEqual(resp.json['code'], 400)
+        self.assertEqual(resp.json['error']['type'], 'AttributeError')
         self.m.VerifyAll()
 
     def test_action_badaction_ise(self):