diff --git a/swh/storage/api/server.py b/swh/storage/api/server.py
index 991fd299..afce5528 100644
--- a/swh/storage/api/server.py
+++ b/swh/storage/api/server.py
@@ -1,128 +1,128 @@
# 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
import logging
import os
-from typing import Any, Dict
+from typing import Any, Dict, Optional
from swh.core import config
from swh.core.api import RPCServerApp
from swh.core.api import encode_data_server as encode_data
from swh.core.api import error_handler
from swh.storage import get_storage as get_swhstorage
from ..exc import StorageArgumentException
from ..interface import StorageInterface
from ..metrics import timed
from .serializers import DECODERS, ENCODERS
def get_storage():
global storage
if not storage:
storage = get_swhstorage(**app.config["storage"])
return storage
class StorageServerApp(RPCServerApp):
extra_type_decoders = DECODERS
extra_type_encoders = ENCODERS
app = StorageServerApp(
__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_path: str) -> Dict[str, Any]:
+def load_and_check_config(config_path: Optional[str]) -> Dict[str, Any]:
"""Check the minimal configuration is set to run the api or raise an
error explanation.
Args:
config_path: Path to the configuration file to load
Raises:
Error if the setup is not as expected
Returns:
configuration as a dict
"""
if not config_path:
raise EnvironmentError("Configuration file must be defined")
if not os.path.exists(config_path):
raise FileNotFoundError(f"Configuration file {config_path} does not exist")
cfg = config.read(config_path)
if "storage" not in cfg:
raise KeyError("Missing 'storage' configuration")
return cfg
-def make_app_from_configfile():
+def make_app_from_configfile() -> StorageServerApp:
"""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_path = os.environ.get("SWH_CONFIG_FILENAME")
api_cfg = load_and_check_config(config_path)
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 817ae856..d4089cf8 100644
--- a/swh/storage/tests/test_server.py
+++ b/swh/storage/tests/test_server.py
@@ -1,60 +1,96 @@
# Copyright (C) 2019-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
+import os
+from typing import Any, Dict
+
import pytest
import yaml
-from swh.storage.api.server import load_and_check_config
+from swh.core.config import load_from_envvar
+from swh.storage.api.server import (
+ StorageServerApp,
+ load_and_check_config,
+ make_app_from_configfile,
+)
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)
@pytest.mark.parametrize("storage_class", [None, ""])
def test_load_and_check_config_no_configuration(storage_class):
"""Inexistent configuration files raises"""
with pytest.raises(EnvironmentError, match="Configuration file must be defined"):
load_and_check_config(storage_class)
def test_load_and_check_config_inexistent_file():
config_path = "/some/inexistent/config.yml"
expected_error = f"Configuration file {config_path} does not exist"
with pytest.raises(FileNotFoundError, match=expected_error):
load_and_check_config(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, match="Missing 'storage' configuration"):
load_and_check_config(config_path)
def test_load_and_check_config_local_config_fine(tmpdir):
"""'local' complete configuration is fine"""
config = {"storage": {"cls": "local", "db": "db", "objstorage": "something",}}
config_path = prepare_config_file(tmpdir, config)
cfg = load_and_check_config(config_path)
assert cfg == config
+
+
+@pytest.fixture
+def swh_storage_server_config(
+ swh_storage_backend_config: Dict[str, Any]
+) -> Dict[str, Any]:
+ return {"storage": swh_storage_backend_config}
+
+
+@pytest.fixture
+def swh_storage_config(monkeypatch, swh_storage_server_config, tmp_path):
+ conf_path = os.path.join(str(tmp_path), "storage.yml")
+ with open(conf_path, "w") as f:
+ f.write(yaml.dump(swh_storage_server_config))
+ monkeypatch.setenv("SWH_CONFIG_FILENAME", conf_path)
+ return conf_path
+
+
+def test_server_make_app_from_config_file(swh_storage_config):
+ app = make_app_from_configfile()
+ expected_cfg = load_from_envvar()
+
+ assert app is not None
+ assert isinstance(app, StorageServerApp)
+ assert app.config["storage"] == expected_cfg["storage"]
+
+ app2 = make_app_from_configfile()
+ assert app is app2