-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start with (file) config system for openeo_driver
related to #168, Open-EO/openeo-geopyspark-integrationtests#6
- Loading branch information
Showing
10 changed files
with
282 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = "0.41.1a1" | ||
__version__ = "0.42.0a1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from openeo_driver.config.config import OpenEoBackendConfig, ConfigException | ||
from openeo_driver.config.load import get_backend_config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from typing import List, Optional | ||
|
||
import attrs | ||
|
||
from openeo_driver.users.oidc import OidcProvider | ||
|
||
|
||
class ConfigException(ValueError): | ||
pass | ||
|
||
|
||
@attrs.frozen | ||
class OpenEoBackendConfig: | ||
""" | ||
Configuration for openEO backend. | ||
""" | ||
|
||
# identifier for this config | ||
id: Optional[str] = None | ||
|
||
oidc_providers: List[OidcProvider] = attrs.Factory(list) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
""" | ||
Default OpenEoBackendConfig | ||
""" | ||
from openeo_driver.config import OpenEoBackendConfig | ||
|
||
config = OpenEoBackendConfig( | ||
id="default", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
""" | ||
Utilities for (lazy) loading of config | ||
""" | ||
import importlib.resources | ||
import logging | ||
import os | ||
from pathlib import Path | ||
from typing import Any, Optional, Union | ||
|
||
from openeo_driver.config.config import ConfigException | ||
|
||
_log = logging.getLogger(__name__) | ||
|
||
from openeo_driver.config import OpenEoBackendConfig | ||
|
||
|
||
def load_from_py_file( | ||
path: Union[str, Path], | ||
variable: str = "config", | ||
expected_class: Optional[type] = OpenEoBackendConfig, | ||
) -> Any: | ||
"""Load a config value from a Python file.""" | ||
path = Path(path) | ||
_log.info( | ||
f"Loading configuration from Python file {path!r} (variable {variable!r})" | ||
) | ||
|
||
# Based on flask's Config.from_pyfile | ||
with path.open(mode="rb") as f: | ||
code = compile(f.read(), path, "exec") | ||
globals = {"__file__": str(path)} | ||
exec(code, globals) | ||
|
||
try: | ||
config = globals[variable] | ||
except KeyError: | ||
raise ConfigException( | ||
f"No variable {variable!r} found in config file {path!r}" | ||
) from None | ||
|
||
if expected_class: | ||
if not isinstance(config, expected_class): | ||
raise ConfigException( | ||
f"Expected {expected_class.__name__} but got {type(config).__name__}" | ||
) | ||
return config | ||
|
||
|
||
class ConfigGetter: | ||
"""Config loader, with lazy-loading and flushing.""" | ||
|
||
def __init__(self): | ||
self._config: Optional[OpenEoBackendConfig] = None | ||
|
||
def __call__(self, force_reload: bool = False) -> OpenEoBackendConfig: | ||
if self._config is None or force_reload: | ||
self._config = self._load() | ||
return self._config | ||
|
||
def _load(self) -> OpenEoBackendConfig: | ||
with importlib.resources.path( | ||
"openeo_driver.config", "default.py" | ||
) as default_config: | ||
config_path = os.environ.get( | ||
"OPENEO_BACKEND_CONFIG", | ||
default_config, | ||
) | ||
config = load_from_py_file( | ||
path=config_path, variable="config", expected_class=OpenEoBackendConfig | ||
) | ||
return config | ||
|
||
def flush(self): | ||
self._config = None | ||
|
||
|
||
get_backend_config = ConfigGetter() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from openeo_driver.config import OpenEoBackendConfig | ||
from openeo_driver.users.oidc import OidcProvider | ||
|
||
oidc_providers = [ | ||
OidcProvider( | ||
id="testprovider", | ||
issuer="https://oidc.test", | ||
scopes=["openid"], | ||
title="Test", | ||
), | ||
OidcProvider( | ||
id="eoidc", | ||
issuer="https://eoidc.test", | ||
scopes=["openid"], | ||
title="e-OIDC", | ||
default_clients=[ | ||
{ | ||
"id": "badcafef00d", | ||
"grant_types": [ | ||
"urn:ietf:params:oauth:grant-type:device_code+pkce", | ||
"refresh_token", | ||
], | ||
} | ||
], | ||
), | ||
# Allow testing with Keycloak setup running in docker on localhost. | ||
OidcProvider( | ||
id="local", | ||
title="Local Keycloak", | ||
issuer="http://localhost:9090/auth/realms/master", | ||
scopes=["openid"], | ||
), | ||
# Allow testing the dummy backend with EGI | ||
OidcProvider( | ||
id="egi", | ||
issuer="https://aai.egi.eu/auth/realms/egi/", | ||
scopes=[ | ||
"openid", | ||
"email", | ||
"eduperson_entitlement", | ||
"eduperson_scoped_affiliation", | ||
], | ||
title="EGI Check-in", | ||
), | ||
OidcProvider( | ||
id="egi-dev", | ||
issuer="https://aai-dev.egi.eu/auth/realms/egi", | ||
scopes=[ | ||
"openid", | ||
"email", | ||
"eduperson_entitlement", | ||
"eduperson_scoped_affiliation", | ||
], | ||
title="EGI Check-in (dev)", | ||
), | ||
] | ||
|
||
|
||
config = OpenEoBackendConfig( | ||
id="dummy", | ||
oidc_providers=oidc_providers, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import random | ||
import textwrap | ||
|
||
import attrs.exceptions | ||
import pytest | ||
|
||
from openeo_driver.config import ( | ||
OpenEoBackendConfig, | ||
get_backend_config, | ||
ConfigException, | ||
) | ||
from openeo_driver.config.load import load_from_py_file | ||
|
||
|
||
def test_config_immutable(): | ||
conf = OpenEoBackendConfig(id="dontchangeme!") | ||
assert conf.id == "dontchangeme!" | ||
with pytest.raises(attrs.exceptions.FrozenInstanceError): | ||
conf.id = "let's try?" | ||
assert conf.id == "dontchangeme!" | ||
|
||
|
||
def test_load_from_py_file_default(tmp_path): | ||
path = tmp_path / "myconfig.py" | ||
cid = f"config-{random.randint(1, 100000)}" | ||
|
||
content = f""" | ||
from openeo_driver.config import OpenEoBackendConfig | ||
cid = {cid!r} | ||
config = OpenEoBackendConfig( | ||
id=cid | ||
) | ||
""" | ||
content = textwrap.dedent(content) | ||
path.write_text(content) | ||
|
||
config = load_from_py_file(path) | ||
assert isinstance(config, OpenEoBackendConfig) | ||
assert config.id == cid | ||
|
||
|
||
def test_load_from_py_file_custom(tmp_path): | ||
path = tmp_path / "myconfig.py" | ||
cid = f"config-{random.randint(1, 100000)}" | ||
path.write_text(f'konff = ("hello world", {cid!r}, list(range(3)))') | ||
config = load_from_py_file(path, variable="konff", expected_class=tuple) | ||
assert isinstance(config, tuple) | ||
assert config == ("hello world", cid, [0, 1, 2]) | ||
|
||
|
||
def test_load_from_py_file_wrong_type(tmp_path): | ||
path = tmp_path / "myconfig.py" | ||
path.write_text(f"config = [3, 5, 8]") | ||
with pytest.raises( | ||
ConfigException, match="Expected OpenEoBackendConfig but got list" | ||
): | ||
_ = load_from_py_file(path) | ||
|
||
|
||
def test_get_backend_config(monkeypatch, tmp_path): | ||
path = tmp_path / "myconfig.py" | ||
content = """ | ||
import random | ||
from openeo_driver.config import OpenEoBackendConfig | ||
config = OpenEoBackendConfig( | ||
id=f"config-{random.randint(1, 100000)}" | ||
) | ||
""" | ||
content = textwrap.dedent(content) | ||
path.write_text(content) | ||
|
||
monkeypatch.setenv("OPENEO_BACKEND_CONFIG", str(path)) | ||
|
||
get_backend_config.flush() | ||
config1 = get_backend_config() | ||
assert isinstance(config1, OpenEoBackendConfig) | ||
|
||
config2 = get_backend_config() | ||
assert config2 is config1 | ||
|
||
get_backend_config.flush() | ||
config3 = get_backend_config() | ||
assert not (config3 is config1) |