Skip to content

Commit 6a0fed5

Browse files
authored
Update configurability and docker documentation (#245)
Updates the configuration: - configuration directory or files can now be set through environment variables - the docker image will look at `/config` directory by default - there is now docker image documentation, and it is pushed to Docker Hub on release I'm aware the introduced logging situation is not ideal, but that's deferred to a follow up PR.
1 parent 97557d2 commit 6a0fed5

6 files changed

Lines changed: 85 additions & 12 deletions

File tree

.github/workflows/docker-publish.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,12 @@ jobs:
4848
labels: ${{ steps.meta.outputs.labels }}
4949
cache-from: type=gha
5050
cache-to: type=gha,mode=max
51+
52+
- name: Docker Hub Description
53+
uses: peter-evans/dockerhub-description@v5
54+
with:
55+
username: ${{ secrets.DOCKERHUB_USERNAME }}
56+
password: ${{ secrets.DOCKERHUB_PASSWORD }}
57+
repository: openml/rest-api
58+
short-description: "Experimental OpenML REST API"
59+
readme-filepath: docker/python/README.md

docker-compose.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ services:
7777
ports:
7878
- "8001:8000"
7979
volumes:
80-
- .:/python-api
80+
- .:/app
81+
- ./src/config.toml:/config/config.toml
82+
environment:
83+
OPENML_REST_API_CONFIG_DIRECTORY: "/config"
8184
depends_on:
8285
- database

docker/python/Dockerfile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@ RUN apt-get update \
55

66
ENV VIRTUAL_ENV=/opt/venv
77
RUN python -m venv $VIRTUAL_ENV
8+
# Prepending the path here means that `python` invocations below use the virtual environment
89
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
910

1011
RUN python -m pip install uv
1112

12-
WORKDIR /python-api
13+
WORKDIR /app
1314
COPY pyproject.toml .
1415

1516
RUN uv pip install -e ".[dev]"
16-
COPY . /python-api
17+
COPY . /app
1718

19+
RUN mkdir /config
20+
COPY ./src/config.toml /config/config.toml
21+
ENV OPENML_REST_API_CONFIG_FILE=/config/config.toml
22+
COPY ./docker/python/README.md /README.md
1823

1924
EXPOSE 8000
2025
ENTRYPOINT ["python", "src/main.py"]

docker/python/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# OpenML REST API
2+
This is the work-in-progress Python-based REST API implementation for OpenML.
3+
This should not be considered production ready.
4+
New releases may contain breaking changes without deprecation warnings.
5+
6+
## Usage
7+
The image is not intended to be used as a standalone image.
8+
Please reference either the docker compose file at [server-api](https://www.github.com/openml/server-api) for development purposes, or [services](https://www.github.com/openml/services) for deployment purposes.
9+
10+
## Configuration
11+
Configuration is currently loaded from both a TOML file and a .env file.
12+
Environment variables are used for configurations that either shouldn't be shared (secrets), or that inform how to load the configuration.
13+
The TOML configuration file is used for all other settings.
14+
By default both of these files are loaded from the `/config` directory.
15+
16+
### Configuration File
17+
A default configuration is available for reference at `/config/config.toml`, and can be used as reference.
18+
19+
### Environment Variables
20+
Environment variables are used for configurations that either shouldn't be shared (secrets), or that inform how to load the configuration:
21+
22+
- `OPENML_REST_API_CONFIG_DIRECTORY`: points to the directory that contains configuration files (`config.toml`, `.env`) (default: `/config`)
23+
- `OPENML_REST_API_CONFIG_FILE`: points to the file that contains the TOML configuration (default: not set). If set, takes precedence over `OPENML_REST_API_CONFIG_DIRECTORY`.
24+
- `OPENML_REST_API_DOTENV_FILE`: points to the dot file that contains the environment variable settings (default: not set). If set, takes precedence over `OPENML_REST_API_CONFIG_DIRECTORY`.
25+
- `OPENML_DATABASES_OPENML_USERNAME`: username for connecting to the `openml` database (default: `root`)
26+
- `OPENML_DATABASES_OPENML_PASSWORD`: password for connecting to the `openml` database (default: `ok`)
27+
- `OPENML_DATABASES_EXPDB_USERNAME`: username for connecting to the `openml_expdb` database (default: `root`)
28+
- `OPENML_DATABASES_EXPDB_PASSWORD`: password for connecting to the `openml_expdb` database (default: `ok`)
29+
30+
31+
## Repository
32+
The code and dockerfile for this image are maintained [on GitHub](https://www.github.com/openml/server-api).

src/config.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,37 @@
11
import functools
2+
import logging
23
import os
34
import tomllib
45
import typing
56
from pathlib import Path
67

78
from dotenv import load_dotenv
89

10+
logger = logging.getLogger(__name__)
11+
912
TomlTable = dict[str, typing.Any]
1013

11-
CONFIG_PATH = Path(__file__).parent / "config.toml"
14+
CONFIG_DIRECTORY_ENV = "OPENML_REST_API_CONFIG_DIRECTORY"
15+
CONFIG_FILE_ENV = "OPENML_REST_API_CONFIG_FILE"
16+
DOTENV_FILE_ENV = "OPENML_REST_API_DOTENV_FILE"
17+
18+
OPENML_DB_USERNAME_ENV = "OPENML_DATABASES_OPENML_USERNAME"
19+
OPENML_DB_PASSWORD_ENV = "OPENML_DATABASES_OPENML_PASSWORD" # noqa: S105 # not a password
20+
EXPDB_DB_USERNAME_ENV = "OPENML_DATABASES_EXPDB_USERNAME"
21+
EXPDB_DB_PASSWORD_ENV = "OPENML_DATABASES_EXPDB_PASSWORD" # noqa: S105 # not a password
22+
23+
_config_directory = Path(os.getenv(CONFIG_DIRECTORY_ENV, Path(__file__).parent))
24+
_config_directory = _config_directory.expanduser().absolute()
25+
_config_file = Path(os.getenv(CONFIG_FILE_ENV, _config_directory / "config.toml"))
26+
_config_file = _config_file.expanduser().absolute()
27+
_dotenv_file = Path(os.getenv(DOTENV_FILE_ENV, _config_directory / ".env"))
28+
_dotenv_file = _dotenv_file.expanduser().absolute()
29+
30+
logger.info("Configuration directory is '%s'", _config_directory)
31+
logger.info("Loading configuration file from '%s'", _config_file)
32+
logger.info("Loading environment variables from '%s'", _dotenv_file)
33+
34+
load_dotenv(dotenv_path=_dotenv_file)
1235

1336

1437
def _apply_defaults_to_siblings(configuration: TomlTable) -> TomlTable:
@@ -25,35 +48,34 @@ def _load_configuration(file: Path) -> TomlTable:
2548
return tomllib.loads(file.read_text())
2649

2750

28-
def load_routing_configuration(file: Path = CONFIG_PATH) -> TomlTable:
51+
def load_routing_configuration(file: Path = _config_file) -> TomlTable:
2952
return typing.cast("TomlTable", _load_configuration(file)["routing"])
3053

3154

3255
@functools.cache
33-
def load_database_configuration(file: Path = CONFIG_PATH) -> TomlTable:
56+
def load_database_configuration(file: Path = _config_file) -> TomlTable:
3457
configuration = _load_configuration(file)
3558
database_configuration = _apply_defaults_to_siblings(
3659
configuration["databases"],
3760
)
38-
load_dotenv()
3961
database_configuration["openml"]["username"] = os.environ.get(
40-
"OPENML_DATABASES_OPENML_USERNAME",
62+
OPENML_DB_USERNAME_ENV,
4163
"root",
4264
)
4365
database_configuration["openml"]["password"] = os.environ.get(
44-
"OPENML_DATABASES_OPENML_PASSWORD",
66+
OPENML_DB_PASSWORD_ENV,
4567
"ok",
4668
)
4769
database_configuration["expdb"]["username"] = os.environ.get(
48-
"OPENML_DATABASES_EXPDB_USERNAME",
70+
EXPDB_DB_USERNAME_ENV,
4971
"root",
5072
)
5173
database_configuration["expdb"]["password"] = os.environ.get(
52-
"OPENML_DATABASES_EXPDB_PASSWORD",
74+
EXPDB_DB_PASSWORD_ENV,
5375
"ok",
5476
)
5577
return database_configuration
5678

5779

58-
def load_configuration(file: Path = Path(__file__).parent / "config.toml") -> TomlTable:
80+
def load_configuration(file: Path = _config_file) -> TomlTable:
5981
return tomllib.loads(file.read_text())

src/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import argparse
2+
import logging
23

34
import uvicorn
45
from fastapi import FastAPI
@@ -58,6 +59,7 @@ def create_api() -> FastAPI:
5859

5960

6061
def main() -> None:
62+
logging.basicConfig(level=logging.INFO)
6163
args = _parse_args()
6264
uvicorn.run(
6365
app="main:create_api",

0 commit comments

Comments
 (0)