From 18e3060af5045093e6a49193d6b7d8b6355b4583 Mon Sep 17 00:00:00 2001 From: Eric K Date: Tue, 12 Mar 2019 15:14:43 -0700 Subject: [PATCH] Return 503 rather than 500 error when _ds_manager unreachable It can happen that the _ds_manager is temporarily unreachable due service not ready or network issues. In these cases, the patch makes sure the API returns 503 http status code instead of 500, so that the client knows the issue is temporary. Change-Id: I8a03a2e0ac84c2395b5ea2015647c5c7d2fed03e Closes-bug: 1811754 --- congress/api/datasource_model.py | 11 +++++++++++ congress/dse2/dse_node.py | 4 ++-- congress/exception.py | 4 ++++ congress/tests/api/test_datasource_model.py | 19 +++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/congress/api/datasource_model.py b/congress/api/datasource_model.py index d74d82a38..72600014e 100644 --- a/congress/api/datasource_model.py +++ b/congress/api/datasource_model.py @@ -93,6 +93,11 @@ class DatasourceModel(base.APIModel): LOG.debug(_("Datasource creation failed.")) raise webservice.DataModelException( e.code, webservice.original_msg(e), http_status_code=e.code) + except exception.RpcTargetNotFound as e: + LOG.debug("Datasource creation failed.") + LOG.warning(webservice.original_msg(e)) + raise webservice.DataModelException( + e.code, webservice.original_msg(e), http_status_code=503) return (obj['id'], obj) @@ -116,7 +121,13 @@ class DatasourceModel(base.APIModel): # Let PE synchronizer takes care of deleting policy except (exception.DatasourceNotFound, exception.DanglingReference) as e: + LOG.debug("Datasource deletion failed.") raise webservice.DataModelException(e.code, str(e)) + except exception.RpcTargetNotFound as e: + LOG.debug("Datasource deletion failed.") + LOG.warning(webservice.original_msg(e)) + raise webservice.DataModelException( + e.code, webservice.original_msg(e), http_status_code=503) # Note(thread-safety): blocking function def request_refresh_action(self, params, context=None, request=None): diff --git a/congress/dse2/dse_node.py b/congress/dse2/dse_node.py index 423fa0312..e170182d9 100644 --- a/congress/dse2/dse_node.py +++ b/congress/dse2/dse_node.py @@ -320,7 +320,7 @@ class DseNode(object): except (messaging_exceptions.MessagingTimeout, messaging_exceptions.MessageDeliveryFailure): msg = "service '%s' could not be found" - raise exception.NotFound(msg % service_id) + raise exception.RpcTargetNotFound(msg % service_id) if kwargs is None: kwargs = {} try: @@ -356,7 +356,7 @@ class DseNode(object): kwargs = {} if not self.is_valid_service(service_id): msg = "service '%s' is not a registered service" - raise exception.NotFound(msg % service_id) + raise exception.RpcTargetNotFound(msg % service_id) target = self.service_rpc_target(service_id, fanout=True) LOG.trace("<%s> Casting RPC '%s' on %s", self.node_id, method, target) diff --git a/congress/exception.py b/congress/exception.py index 2501109da..77ec6ad01 100644 --- a/congress/exception.py +++ b/congress/exception.py @@ -235,6 +235,10 @@ class InvalidDatasourceName(BadConfig): "start with underscore. Must be valid in policy language") +class RpcTargetNotFound(NotFound): + pass + + class DatasourceNotFound(NotFound): msg_fmt = _("Datasource not found %(id)s") diff --git a/congress/tests/api/test_datasource_model.py b/congress/tests/api/test_datasource_model.py index b22ffa239..af6950a43 100644 --- a/congress/tests/api/test_datasource_model.py +++ b/congress/tests/api/test_datasource_model.py @@ -81,6 +81,16 @@ class TestDatasourceModel(base.SqlTestCase): self.assertEqual('datasource_test_3', obj.name) self.assertIsNotNone(ds_obj) + def test_add_item_manager_unreachable(self): + datasource3 = self._get_datasource_request() + datasource3['name'] = 'datasource_test_3' + self.datasource_model.invoke_rpc = mock.Mock( + side_effect=exception.RpcTargetNotFound()) + try: + self.datasource_model.add_item(datasource3, {}) + except webservice.DataModelException as e: + self.assertEqual(e.http_status_code, 503) + def test_add_datasource_with_custom_driver(self): datasource4 = self._get_datasource_request() datasource4['name'] = 'datasource_test_4' @@ -124,6 +134,15 @@ class TestDatasourceModel(base.SqlTestCase): self.datasource_model.delete_item, None, {}, context=context) + def test_delete_item_manager_unreachable(self): + context = {'ds_id': 'fake'} + self.datasource_model.invoke_rpc = mock.Mock( + side_effect=exception.RpcTargetNotFound()) + try: + self.datasource_model.add_item(None, {}, context=context) + except webservice.DataModelException as e: + self.assertEqual(e.http_status_code, 503) + def test_datasource_api_model_execute(self): def _execute_api(client, action, action_args): positional_args = action_args.get('positional', [])