diff --git a/swh/storage/api/client.py b/swh/storage/api/client.py index 222076f7..125f132c 100644 --- a/swh/storage/api/client.py +++ b/swh/storage/api/client.py @@ -1,24 +1,41 @@ -# Copyright (C) 2015-2017 The Software Heritage developers +# Copyright (C) 2015-2020 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information -from swh.core.api import RPCClient +from swh.core.api import RPCClient, RemoteException -from ..exc import StorageAPIError +from .. import HashCollision +from ..exc import StorageAPIError, StorageArgumentException from ..interface import StorageInterface class RemoteStorage(RPCClient): """Proxy to a remote storage API""" api_exception = StorageAPIError backend_class = StorageInterface + reraise_exceptions = [ + StorageArgumentException, + ] + + def raise_for_status(self, response) -> None: + try: + super().raise_for_status(response) + except RemoteException as e: + if e.response is not None and e.response.status_code == 500 \ + and e.args and e.args[0].get('type') == 'HashCollision': + # XXX: workaround until we fix these HashCollisions happening + # when they shouldn't + raise HashCollision( + *e.args[0]['args']) + else: + raise def reset(self): return self.post('reset', {}) def stat_counters(self): return self.get('stat/counters') def refresh_stat_counters(self): return self.get('stat/refresh') diff --git a/swh/storage/api/server.py b/swh/storage/api/server.py index 05cf12e0..dd49c447 100644 --- a/swh/storage/api/server.py +++ b/swh/storage/api/server.py @@ -1,118 +1,124 @@ # Copyright (C) 2015-2019 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import os import logging from swh.core import config from swh.storage import get_storage as get_swhstorage from swh.core.api import (RPCServerApp, error_handler, encode_data_server as encode_data) from ..interface import StorageInterface from ..metrics import timed +from ..exc import StorageArgumentException def get_storage(): global storage if not storage: storage = get_swhstorage(**app.config['storage']) return storage app = RPCServerApp(__name__, backend_class=StorageInterface, backend_factory=get_storage) storage = None +@app.errorhandler(StorageArgumentException) +def argument_error_handler(exception): + return error_handler(exception, encode_data, status_code=400) + + @app.errorhandler(Exception) def my_error_handler(exception): return error_handler(exception, encode_data) @app.route('/') @timed def index(): return ''' Software Heritage storage server

You have reached the Software Heritage storage server.
See its documentation and API for more information

''' @app.route('/stat/counters', methods=['GET']) @timed def stat_counters(): return encode_data(get_storage().stat_counters()) @app.route('/stat/refresh', methods=['GET']) @timed def refresh_stat_counters(): return encode_data(get_storage().refresh_stat_counters()) api_cfg = None def load_and_check_config(config_file, type='local'): """Check the minimal configuration is set to run the api or raise an error explanation. Args: config_file (str): Path to the configuration file to load type (str): configuration type. For 'local' type, more checks are done. Raises: Error if the setup is not as expected Returns: configuration as a dict """ if not config_file: raise EnvironmentError('Configuration file must be defined') if not os.path.exists(config_file): raise FileNotFoundError('Configuration file %s does not exist' % ( config_file, )) cfg = config.read(config_file) if 'storage' not in cfg: raise KeyError("Missing '%storage' configuration") return cfg def make_app_from_configfile(): """Run the WSGI app from the webserver, loading the configuration from a configuration file. SWH_CONFIG_FILENAME environment variable defines the configuration path to load. """ global api_cfg if not api_cfg: config_file = os.environ.get('SWH_CONFIG_FILENAME') api_cfg = load_and_check_config(config_file) app.config.update(api_cfg) handler = logging.StreamHandler() app.logger.addHandler(handler) return app if __name__ == '__main__': print('Deprecated. Use swh-storage')