From 918bf5ac397a3fcd22b5eac5f47dd4377e148072 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Tue, 22 Oct 2024 15:54:03 +0200 Subject: [PATCH] chore: add docker + docker compose also change a few things to work correctly in that environment, since teh gameserver / simulator is builtin --- Dockerfile | 60 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 29 ++++++++++++++++++++++ docker-compose.yml | 12 ++++++++++ lobby/config.py | 23 +++++++++++++++--- lobby/main.py | 8 ++++++- 5 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ebbb2ca --- /dev/null +++ b/Dockerfile @@ -0,0 +1,60 @@ +FROM alpine:3.20 AS cpp-buildenv + +WORKDIR /tmp + +RUN apk update --no-cache && \ + apk add --no-cache ca-certificates \ + samurai git openssl-dev openssl pkgconf cmake \ + clang18 clang18-extra-tools && \ + git clone https://github.com/OpenBrickProtocolFoundation/simulator simulator + +ENV CC=clang-18 +ENV CXX=clang++-18 +ENV LD=clang-18 + +WORKDIR /tmp/simulator/ + +RUN cmake -B build -G "Ninja" \ + -DCMAKE_BUILD_TYPE=Release \ + -Dobpf_build_tests=OFF \ + -Dobpf_simulator_enable_undefined_behavior_sanitizer=OFF \ + -Dobpf_simulator_enable_address_sanitizer=OFF \ + -Dobpf_simulator_warnings_as_errors=ON \ + -Dobpf_simulator_build_shared_libs=OFF && \ + cmake --build build + + + +# final image starts here + +FROM python:3.12-alpine + +WORKDIR /app + + +# install python dependencies + +COPY requirements.txt requirements.txt + +RUN pip3 install --no-cache-dir -r requirements.txt + +COPY lobby/ lobby/ + +# copy game server + +ENV OBBF_CONFIG_PATH=/app/config/config.json +ENV OBBF_GAMESERVER_EXECUTABLE=/app/gameserver/server +ENV OBBF_SIMULATOR_LIBRARY_PATH=/app/gameserver/ +ENV OBPF_IS_DOCKER=true + +ENV PYTHONPATH="${PYTHONPATH}:/app/" + +COPY --from=cpp-buildenv /tmp/simulator/build/bin/server/server /app/gameserver/ + +## add finalization arguments + +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 CMD curl -f -X GET http://localhost:1717/health + +EXPOSE 1717 + +CMD [ "python3", "lobby/main.py", "production"] diff --git a/README.md b/README.md index f6e3615..c4fb9d1 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,32 @@ # The Lob(b)y Server Everybody Asked For! More to come here... + +## Deploy with docker compose + +You need a running docker service and docker compose installed + +Then build the image with docker compose: + +```bash +docker compose build +``` + +After this you can run the whole app with docker compose: + +```bash +docker compose up -d +``` + +Note: the port is 1717 to proxy it through something like e.g. nginx. + +There are two volumes defined: + +```yaml +./files/:/app/files/ +./config.json:/app/config/config.json +``` + +The first one maps the database content out of the container, so that it's persistent, You can inspect the database there if you want to. +The second one is for you to provide a config.json in the root folder. To see the exact schema look into `lobby/config.py` +NOTE: `SIMULATOR_LIBRARY_PATH` and `GAMESERVER_EXECUTABLE` don't need to be set, as the docker environment provides thos and sets them via environment variables. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..30ca3ee --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3.8" +name: "obpf-lobby" +services: + flask: + container_name: obpf-lobby + build: + context: . + ports: + - "1717:1717" + volumes: + - "./files/:/app/files/" + - ./config.json:/app/config/config.json diff --git a/lobby/config.py b/lobby/config.py index fe861a9..cfad529 100644 --- a/lobby/config.py +++ b/lobby/config.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from enum import Enum from typing import Self +import os from dataclasses_jsonschema import JsonSchemaMixin @@ -13,13 +14,29 @@ class ConfigValue(Enum): @dataclass -class Config(JsonSchemaMixin): +class BaseConfig(JsonSchemaMixin): jwt_secret: str - gameserver_executable: str - simulator_library_path: str @classmethod def from_file(cls, filename: str) -> Self: with open(filename) as file: data = json.load(file) return cls.from_dict(data) + +@dataclass +class Config(BaseConfig): + gameserver_executable: str + simulator_library_path: str + + @classmethod + def from_file(cls, filename: str) -> Self: + is_docker = len(os.environ.get("OBPF_IS_DOCKER",""))>= 1 + if is_docker: + data = BaseConfig.from_file(filename).to_dict() + data[ConfigValue.GAMESERVER_EXECUTABLE.value] = os.environ["OBBF_GAMESERVER_EXECUTABLE"] + data[ConfigValue.SIMULATOR_LIBRARY_PATH.value] = os.environ["OBBF_SIMULATOR_LIBRARY_PATH"] + return cls.from_dict(data) + else: + with open(filename) as file: + data = json.load(file) + return cls.from_dict(data) diff --git a/lobby/main.py b/lobby/main.py index 9f62756..fe4246e 100644 --- a/lobby/main.py +++ b/lobby/main.py @@ -147,6 +147,12 @@ def try_authenticate(client_request: Request) -> User | tuple[Response, HTTPStat return user + +@app.route("/health", methods=["GET"]) +def health_check(): + return create_ok_response({}) + + @app.route("/lobbies", methods=["GET"]) def lobby_list() -> tuple[Response, HTTPStatus]: @dataclass @@ -488,7 +494,7 @@ class SetClientReadyResponse(JsonSchemaMixin): def main() -> None: - config = Config.from_file("config.json") + config = Config.from_file(os.environ.get("OBBF_CONFIG_PATH","config.json")) app.config.update(config.to_dict()) pprint.pprint(app.config) if len(sys.argv) >= 2 and sys.argv[1] == "production":