diff --git a/bin/heat-api b/bin/heat-api index 4ac438596..8f58c0944 100755 --- a/bin/heat-api +++ b/bin/heat-api @@ -33,7 +33,7 @@ if os.path.exists(os.path.join(possible_topdir, 'heat', '__init__.py')): from heat.openstack.common import gettextutils -gettextutils.install('heat') +gettextutils.install('heat', lazy=True) from oslo.config import cfg diff --git a/bin/heat-api-cfn b/bin/heat-api-cfn index 8eceb6aef..95a946ca8 100755 --- a/bin/heat-api-cfn +++ b/bin/heat-api-cfn @@ -35,7 +35,7 @@ if os.path.exists(os.path.join(possible_topdir, 'heat', '__init__.py')): from heat.openstack.common import gettextutils -gettextutils.install('heat') +gettextutils.install('heat', lazy=True) from oslo.config import cfg diff --git a/bin/heat-api-cloudwatch b/bin/heat-api-cloudwatch index 7453b7744..155a4d48f 100755 --- a/bin/heat-api-cloudwatch +++ b/bin/heat-api-cloudwatch @@ -35,7 +35,7 @@ if os.path.exists(os.path.join(possible_topdir, 'heat', '__init__.py')): from heat.openstack.common import gettextutils -gettextutils.install('heat') +gettextutils.install('heat', lazy=True) from oslo.config import cfg diff --git a/bin/heat-cfn b/bin/heat-cfn index fc31938df..34a5bd0d8 100755 --- a/bin/heat-cfn +++ b/bin/heat-cfn @@ -41,7 +41,7 @@ scriptname = os.path.basename(sys.argv[0]) from heat.openstack.common import gettextutils -gettextutils.install('heat') +gettextutils.install('heat', lazy=True) if scriptname == 'heat-boto': from heat.cfn_client import boto_client as heat_client diff --git a/bin/heat-engine b/bin/heat-engine index 14e6f1851..36412cbae 100755 --- a/bin/heat-engine +++ b/bin/heat-engine @@ -36,7 +36,7 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'heat', '__init__.py')): from heat.openstack.common import gettextutils -gettextutils.install('heat') +gettextutils.install('heat', lazy=True) from oslo.config import cfg diff --git a/bin/heat-manage b/bin/heat-manage index 95e4a5d14..32bdc8f78 100755 --- a/bin/heat-manage +++ b/bin/heat-manage @@ -25,6 +25,10 @@ POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'heat', '__init__.py')): sys.path.insert(0, POSSIBLE_TOPDIR) +from heat.openstack.common import gettextutils + +gettextutils.install('heat') + from heat.cmd import manage manage.main() diff --git a/heat/api/middleware/fault.py b/heat/api/middleware/fault.py index 9df7ae60e..c8e77093f 100644 --- a/heat/api/middleware/fault.py +++ b/heat/api/middleware/fault.py @@ -23,6 +23,7 @@ Cinder's faultwrapper import traceback import webob +from heat.common import exception from heat.openstack.common import log as logging import heat.openstack.common.rpc.common as rpc_common @@ -79,11 +80,18 @@ class FaultWrapper(wsgi.Middleware): 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) + if isinstance(ex, exception.OpenstackException): + # If the exception is an OpenstackException it is going to have a + # translated Message object as the message, let's recreate it here + message = (ex.message % ex.kwargs + if hasattr(ex, 'kwargs') else ex.message) + else: + message = ex.message + + trace = str(ex) + if trace.find('\n') > -1: + unused, trace = trace.split('\n', 1) else: - message = str(ex) trace = traceback.format_exc() webob_exc = self.error_map.get(ex_type, diff --git a/heat/api/openstack/v1/__init__.py b/heat/api/openstack/v1/__init__.py index a1ab038f1..f21f89b83 100644 --- a/heat/api/openstack/v1/__init__.py +++ b/heat/api/openstack/v1/__init__.py @@ -15,10 +15,6 @@ import routes -from heat.openstack.common import gettextutils - -gettextutils.install('heat') - from heat.api.openstack.v1 import stacks from heat.api.openstack.v1 import resources from heat.api.openstack.v1 import events diff --git a/heat/api/openstack/v1/events.py b/heat/api/openstack/v1/events.py index 81264a50e..b0c0bbb69 100644 --- a/heat/api/openstack/v1/events.py +++ b/heat/api/openstack/v1/events.py @@ -21,7 +21,6 @@ from heat.common import wsgi from heat.rpc import api as engine_api from heat.common import identifier from heat.rpc import client as rpc_client -from heat.openstack.common.gettextutils import _ summary_keys = [ diff --git a/heat/api/openstack/v1/stacks.py b/heat/api/openstack/v1/stacks.py index 617f25a3f..a79db25a9 100644 --- a/heat/api/openstack/v1/stacks.py +++ b/heat/api/openstack/v1/stacks.py @@ -29,7 +29,6 @@ from heat.rpc import client as rpc_client from heat.common import urlfetch from heat.openstack.common import log as logging -from heat.openstack.common.gettextutils import _ logger = logging.getLogger(__name__) diff --git a/heat/api/openstack/v1/util.py b/heat/api/openstack/v1/util.py index 43381bfce..b6dcdc5ed 100644 --- a/heat/api/openstack/v1/util.py +++ b/heat/api/openstack/v1/util.py @@ -18,8 +18,6 @@ from functools import wraps from heat.common import identifier -from heat.openstack.common.gettextutils import _ - def tenant_local(handler): ''' diff --git a/heat/common/exception.py b/heat/common/exception.py index eb236a5a3..ed4fbc5c2 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -20,7 +20,6 @@ import functools import urlparse import sys -from heat.openstack.common.gettextutils import _ from heat.openstack.common import exception diff --git a/heat/common/wsgi.py b/heat/common/wsgi.py index f108fe68e..a4da6b0eb 100644 --- a/heat/common/wsgi.py +++ b/heat/common/wsgi.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -43,8 +44,8 @@ import webob.dec import webob.exc from heat.common import exception +from heat.openstack.common import gettextutils from heat.openstack.common import importutils -from heat.openstack.common.gettextutils import _ URL_LENGTH_LIMIT = 50000 @@ -431,6 +432,12 @@ class Request(webob.Request): else: return content_type + def best_match_language(self): + """Determine language for returned response.""" + all_languages = gettextutils.get_available_languages('heat') + return self.accept_language.best_match(all_languages, + default_match='en_US') + def is_json_content_type(request): if request.method == 'GET': @@ -589,7 +596,17 @@ class Resource(object): request, **action_args) except TypeError as err: logging.error(_('Exception handling resource: %s') % str(err)) - raise webob.exc.HTTPBadRequest() + msg = _('The server could not comply with the request since\r\n' + 'it is either malformed or otherwise incorrect.\r\n') + err = webob.exc.HTTPBadRequest(msg) + raise translate_exception(err, request.best_match_language()) + except webob.exc.HTTPException as err: + logging.error(_("Returning %(code)s to user: %(explanation)s"), + {'code': err.code, 'explanation': err.explanation}) + raise translate_exception(err, request.best_match_language()) + except Exception as err: + logging.error(_("Unexpected error occurred serving API: %s") % err) + raise translate_exception(err, request.best_match_language()) # Here we support either passing in a serializer or detecting it # based on the content type. @@ -653,6 +670,26 @@ class Resource(object): return args +def translate_exception(exc, locale): + """Translates all translatable elements of the given exception.""" + exc.message = gettextutils.get_localized_message(exc.message, locale) + if isinstance(exc, webob.exc.HTTPError): + # If the explanation is not a Message, that means that the + # explanation is the default, generic and not translatable explanation + # from webop.exc. Since the explanation is the error shown when the + # exception is converted to a response, let's actually swap it with + # message, since message is what gets passed in at construction time + # in the API + if not isinstance(exc.explanation, gettextutils.Message): + exc.explanation = exc.message + exc.detail = '' + else: + exc.explanation = \ + gettextutils.get_localized_message(exc.explanation, locale) + exc.detail = gettextutils.get_localized_message(exc.detail, locale) + return exc + + class BasePasteFactory(object): """A base class for paste app and filter factories. diff --git a/heat/openstack/common/exception.py b/heat/openstack/common/exception.py index ca1195a3e..9ba9fc144 100644 --- a/heat/openstack/common/exception.py +++ b/heat/openstack/common/exception.py @@ -21,8 +21,6 @@ Exceptions common to OpenStack projects import logging -from heat.openstack.common.gettextutils import _ - _FATAL_EXCEPTION_FORMAT_ERRORS = False @@ -120,6 +118,7 @@ class OpenstackException(Exception): def __init__(self, **kwargs): try: + self.kwargs = kwargs self._error_string = self.message % kwargs except Exception as e: diff --git a/heat/openstack/common/rpc/common.py b/heat/openstack/common/rpc/common.py index 31ecbdf3d..365161124 100644 --- a/heat/openstack/common/rpc/common.py +++ b/heat/openstack/common/rpc/common.py @@ -24,7 +24,6 @@ import traceback from oslo.config import cfg import six -from heat.openstack.common.gettextutils import _ from heat.openstack.common import importutils from heat.openstack.common import jsonutils from heat.openstack.common import local diff --git a/heat/tests/test_api_openstack_v1.py b/heat/tests/test_api_openstack_v1.py index db0275a28..12be4f89c 100644 --- a/heat/tests/test_api_openstack_v1.py +++ b/heat/tests/test_api_openstack_v1.py @@ -47,11 +47,15 @@ def request_with_middleware(middleware, func, req, *args, **kwargs): 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 +def to_remote_error(error): + """Converts the given exception to the one with the _Remote suffix. + This is how RPC exceptions are recreated on the caller's side, so + this helps better simulate how the exception mechanism actually works. + """ + ex_type = type(error) + kwargs = error.kwargs if hasattr(error, 'kwargs') else {} + message = error.message module = ex_type().__class__.__module__ str_override = lambda self: "%s\n" % message new_ex_type = type(ex_type.__name__ + rpc_common._REMOTE_POSTFIX, @@ -59,7 +63,7 @@ def remote_error(ex_type, message=''): {'__str__': str_override, '__unicode__': str_override}) new_ex_type.__module__ = '%s%s' % (module, rpc_common._REMOTE_POSTFIX) - return new_ex_type() + return new_ex_type(**kwargs) class InstantiationDataTest(HeatTestCase): @@ -398,7 +402,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'list_stacks', 'args': {}, 'version': self.api_version}, - None).AndRaise(remote_error(AttributeError)) + None).AndRaise(to_remote_error(AttributeError())) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -418,7 +422,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'list_stacks', 'args': {}, 'version': self.api_version}, - None).AndRaise(remote_error(Exception)) + None).AndRaise(to_remote_error(Exception())) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -513,6 +517,8 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._post('/stacks', json.dumps(body)) + unknown_parameter = heat_exc.UnknownUserParameter(key='a') + missing_parameter = heat_exc.UserParameterMissing(key='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -523,7 +529,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'files': {}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, - None).AndRaise(remote_error(AttributeError)) + None).AndRaise(to_remote_error(AttributeError())) rpc.call(req.context, self.topic, {'namespace': None, 'method': 'create_stack', @@ -533,7 +539,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'files': {}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.UnknownUserParameter)) + None).AndRaise(to_remote_error(unknown_parameter)) rpc.call(req.context, self.topic, {'namespace': None, 'method': 'create_stack', @@ -543,7 +549,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'files': {}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.UserParameterMissing)) + None).AndRaise(to_remote_error(missing_parameter)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, self.controller.create, @@ -579,6 +585,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._post('/stacks', json.dumps(body)) + error = heat_exc.StackExists(stack_name='s') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -589,7 +596,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'files': {}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackExists)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -612,6 +619,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._post('/stacks', json.dumps(body)) + error = heat_exc.StackValidationFailed(message='') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -622,7 +630,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'files': {}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackValidationFailed)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -678,13 +686,14 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._get('/stacks/%(stack_name)s' % locals()) + error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'identify_stack', 'args': {'stack_name': stack_name}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -727,13 +736,14 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._get('/stacks/%(stack_name)s/resources' % locals()) + error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'identify_stack', 'args': {'stack_name': stack_name}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -820,13 +830,14 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity) + error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'show_stack', 'args': {'stack_identity': dict(identity)}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -844,13 +855,14 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity) + error = heat_exc.InvalidTenant(target='a', actual='b') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'show_stack', 'args': {'stack_identity': dict(identity)}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.InvalidTenant)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -887,15 +899,15 @@ class StackControllerTest(ControllerTest, HeatTestCase): def test_get_template_err_notfound(self): identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity) - template = {u'Foo': u'bar'} + error = error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'get_template', 'args': {'stack_identity': dict(identity)}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() @@ -958,6 +970,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._put('/stacks/%(stack_name)s/%(stack_id)s' % identity, json.dumps(body)) + error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -968,7 +981,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'files': {}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1021,6 +1034,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): req = self._delete('/stacks/%(stack_name)s/%(stack_id)s' % identity) + error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') # Engine returns None when delete successful rpc.call(req.context, self.topic, @@ -1028,7 +1042,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'delete_stack', 'args': {'stack_identity': dict(identity)}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1122,13 +1136,14 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'AWS::EC2::EIP', 'AWS::EC2::EIPAssociation'] + error = heat_exc.ServerError(body='') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'list_resource_types', 'args': {}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.ServerError)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1158,13 +1173,15 @@ class StackControllerTest(ControllerTest, HeatTestCase): def test_generate_template_not_found(self): req = self._get('/resource_types/NOT_FOUND/template') + + error = heat_exc.ResourceTypeNotFound(type_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'generate_template', 'args': {'type_name': 'NOT_FOUND'}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.ResourceTypeNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, self.controller.generate_template, @@ -1267,13 +1284,14 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): req = self._get(stack_identity._tenant_path() + '/resources') + error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'list_stack_resources', 'args': {'stack_identity': stack_identity}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1354,6 +1372,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): req = self._get(res_identity._tenant_path()) + error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -1361,7 +1380,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): 'args': {'stack_identity': stack_identity, 'resource_name': res_name}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1384,6 +1403,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): req = self._get(res_identity._tenant_path()) + error = heat_exc.ResourceNotFound(stack_name='a', resource_name='b') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -1391,7 +1411,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): 'args': {'stack_identity': stack_identity, 'resource_name': res_name}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.ResourceNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1414,6 +1434,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): req = self._get(res_identity._tenant_path()) + error = heat_exc.ResourceNotAvailable(resource_name='') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -1421,7 +1442,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): 'args': {'stack_identity': stack_identity, 'resource_name': res_name}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.ResourceNotAvailable)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1488,6 +1509,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): req = self._get(res_identity._tenant_path() + '/metadata') + error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -1495,7 +1517,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): 'args': {'stack_identity': stack_identity, 'resource_name': res_name}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1518,6 +1540,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): req = self._get(res_identity._tenant_path() + '/metadata') + error = heat_exc.ResourceNotFound(stack_name='a', resource_name='b') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, @@ -1525,7 +1548,7 @@ class ResourceControllerTest(ControllerTest, HeatTestCase): 'args': {'stack_identity': stack_identity, 'resource_name': res_name}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.ResourceNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1699,13 +1722,14 @@ class EventControllerTest(ControllerTest, HeatTestCase): req = self._get(stack_identity._tenant_path() + '/events') + error = error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'list_events', 'args': {'stack_identity': stack_identity}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -1943,13 +1967,14 @@ class EventControllerTest(ControllerTest, HeatTestCase): req = self._get(stack_identity._tenant_path() + '/resources/' + res_name + '/events/' + event_id) + error = error = heat_exc.StackNotFound(stack_name='a') self.m.StubOutWithMock(rpc, 'call') rpc.call(req.context, self.topic, {'namespace': None, 'method': 'list_events', 'args': {'stack_identity': stack_identity}, 'version': self.api_version}, - None).AndRaise(remote_error(heat_exc.StackNotFound)) + None).AndRaise(to_remote_error(error)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -2380,7 +2405,7 @@ class ActionControllerTest(ControllerTest, HeatTestCase): 'method': 'stack_suspend', 'args': {'stack_identity': stack_identity}, 'version': self.api_version}, - None).AndRaise(remote_error(AttributeError)) + None).AndRaise(to_remote_error(AttributeError())) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, diff --git a/heat/tests/test_wsgi.py b/heat/tests/test_wsgi.py index e6e113027..ab26f2547 100644 --- a/heat/tests/test_wsgi.py +++ b/heat/tests/test_wsgi.py @@ -17,6 +17,7 @@ import datetime +import stubout import webob from heat.common import exception @@ -26,6 +27,10 @@ from heat.tests.common import HeatTestCase class RequestTest(HeatTestCase): + def setUp(self): + self.stubs = stubout.StubOutForTesting() + super(RequestTest, self).setUp() + def test_content_type_missing(self): request = wsgi.Request.blank('/tests/123') self.assertRaises(exception.InvalidContentType, @@ -74,9 +79,28 @@ class RequestTest(HeatTestCase): result = request.best_match_content_type() self.assertEqual(result, "application/json") + def test_best_match_language(self): + # Here we test that we are actually invoking language negotiation + # by webop and also that the default locale always available is en-US + request = wsgi.Request.blank('/') + accepted = 'unknown-lang' + request.headers = {'Accept-Language': accepted} + + def fake_best_match(self, offers, default_match=None): + return default_match + + self.stubs.SmartSet(request.accept_language, + 'best_match', fake_best_match) + + self.assertEqual(request.best_match_language(), 'en_US') + class ResourceTest(HeatTestCase): + def setUp(self): + self.stubs = stubout.StubOutForTesting() + super(ResourceTest, self).setUp() + def test_get_action_args(self): env = { 'wsgiorg.routing_args': [ @@ -160,6 +184,34 @@ class ResourceTest(HeatTestCase): None) self.assertRaises(webob.exc.HTTPBadRequest, resource, request) + def test_resource_call_error_handle_localized(self): + class Controller(object): + def delete(self, req, identity): + return (req, identity) + + actions = {'action': 'delete', 'id': 12, 'body': 'data'} + env = {'wsgiorg.routing_args': [None, actions]} + request = wsgi.Request.blank('/tests/123', environ=env) + request.body = '{"foo" : "value"}' + message_es = "No Encontrado" + translated_ex = webob.exc.HTTPBadRequest(message_es) + + resource = wsgi.Resource(Controller(), + wsgi.JSONRequestDeserializer(), + None) + + def fake_translate_exception(ex, locale): + return translated_ex + + self.stubs.SmartSet(wsgi, + 'translate_exception', fake_translate_exception) + + try: + resource(request) + except webob.exc.HTTPBadRequest as e: + self.assertEquals(message_es, e.message) + self.m.VerifyAll() + class JSONResponseSerializerTest(HeatTestCase):