diff --git a/swh/storage/api/server.py b/swh/storage/api/server.py
index 4a0ea0c2..cc6634ae 100644
--- a/swh/storage/api/server.py
+++ b/swh/storage/api/server.py
@@ -1,132 +1,118 @@
# 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 ..storage import Storage
from ..metrics import timed
def get_storage():
global storage
if not storage:
storage = get_swhstorage(**app.config['storage'])
return storage
app = RPCServerApp(__name__,
backend_class=Storage,
backend_factory=get_storage)
storage = None
@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")
- if type == 'local':
- vcfg = cfg['storage']
- cls = vcfg.get('cls')
- if cls != 'local':
- raise ValueError(
- "The storage backend can only be started with a 'local' "
- "configuration")
-
- args = vcfg['args']
- for key in ('db', 'objstorage'):
- if not args.get(key):
- raise ValueError(
- "Invalid configuration; missing '%s' config entry" % key)
-
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')
diff --git a/swh/storage/tests/test_server.py b/swh/storage/tests/test_server.py
index 28ab7df3..a412c5a1 100644
--- a/swh/storage/tests/test_server.py
+++ b/swh/storage/tests/test_server.py
@@ -1,129 +1,85 @@
# Copyright (C) 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 copy
import pytest
import yaml
from swh.storage.api.server import load_and_check_config
def prepare_config_file(tmpdir, content, name='config.yml'):
"""Prepare configuration file in `$tmpdir/name` with content `content`.
Args:
tmpdir (LocalPath): root directory
content (str/dict): Content of the file either as string or as a dict.
If a dict, converts the dict into a yaml string.
name (str): configuration filename
Returns
path (str) of the configuration file prepared.
"""
config_path = tmpdir / name
if isinstance(content, dict): # convert if needed
content = yaml.dump(content)
config_path.write_text(content, encoding='utf-8')
# pytest on python3.5 does not support LocalPath manipulation, so
# convert path to string
return str(config_path)
def test_load_and_check_config_no_configuration():
"""Inexistent configuration files raises"""
with pytest.raises(EnvironmentError) as e:
load_and_check_config(None)
assert e.value.args[0] == 'Configuration file must be defined'
config_path = '/some/inexistent/config.yml'
with pytest.raises(FileNotFoundError) as e:
load_and_check_config(config_path)
assert e.value.args[0] == 'Configuration file %s does not exist' % (
config_path, )
def test_load_and_check_config_wrong_configuration(tmpdir):
"""Wrong configuration raises"""
config_path = prepare_config_file(tmpdir, 'something: useless')
with pytest.raises(KeyError) as e:
load_and_check_config(config_path)
assert e.value.args[0] == 'Missing \'%storage\' configuration'
-def test_load_and_check_config_remote_config_local_type_raise(tmpdir):
- """'local' configuration without 'local' storage raises"""
- config = {
- 'storage': {
- 'cls': 'remote',
- 'args': {}
- }
- }
- config_path = prepare_config_file(tmpdir, config)
- with pytest.raises(ValueError) as e:
- load_and_check_config(config_path, type='local')
-
- assert (
- e.value.args[0] ==
- "The storage backend can only be started with a 'local' configuration"
- )
-
-
-def test_load_and_check_config_local_incomplete_configuration(tmpdir):
- """Incomplete 'local' configuration should raise"""
- config = {
- 'storage': {
- 'cls': 'local',
- 'args': {
- 'db': 'database',
- 'objstorage': 'object_storage'
- }
- }
- }
-
- for key in ('db', 'objstorage'):
- c = copy.deepcopy(config)
- c['storage']['args'].pop(key)
- config_path = prepare_config_file(tmpdir, c)
- with pytest.raises(ValueError) as e:
- load_and_check_config(config_path)
-
- assert (
- e.value.args[0] ==
- "Invalid configuration; missing '%s' config entry" % key
- )
-
-
def test_load_and_check_config_local_config_fine(tmpdir):
"""'Remote configuration is fine"""
config = {
'storage': {
'cls': 'local',
'args': {
'db': 'db',
'objstorage': 'something',
}
}
}
config_path = prepare_config_file(tmpdir, config)
cfg = load_and_check_config(config_path, type='local')
assert cfg == config
def test_load_and_check_config_remote_config_fine(tmpdir):
"""'Remote configuration is fine"""
config = {
'storage': {
'cls': 'remote',
'args': {}
}
}
config_path = prepare_config_file(tmpdir, config)
cfg = load_and_check_config(config_path, type='any')
assert cfg == config