diff --git a/DOCKER_README.md b/DOCKER_README.md index 4c28c052..4c40a861 100644 --- a/DOCKER_README.md +++ b/DOCKER_README.md @@ -43,10 +43,14 @@ docker compose up -d This command will: - Build the TravianZ web application container -- Start a MariaDB (latest) database container -- Start a phpMyAdmin container for database management +- Start a MariaDB (latest) database container and wait for it to become ready +- Start the web application and phpMyAdmin after MariaDB is healthy - Set up a network for all containers to communicate +The installation wizard creates the TravianZ schema. SQL files in `var/db` +are templates whose placeholders are resolved by the installer; MariaDB does +not execute them directly during container initialization. + ### 4. Access the Installation Wizard Once the containers are running, open your browser and navigate to: diff --git a/docker-compose.yml b/docker-compose.yml index 76b5f520..2a09a4aa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,8 @@ services: environment: - APACHE_DOCUMENT_ROOT=/var/www/html depends_on: - - db + db: + condition: service_healthy networks: - travianz-network restart: unless-stopped @@ -26,13 +27,18 @@ services: MARIADB_PASSWORD: ${MARIADB_PASSWORD:-${MYSQL_PASSWORD:-travianzpass}} volumes: - db-data:/var/lib/mysql - - ./var/db:/docker-entrypoint-initdb.d:ro ports: - "3306:3306" networks: - travianz-network restart: unless-stopped command: --sql_mode="" + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 10s phpmyadmin: image: phpmyadmin/phpmyadmin:latest @@ -45,7 +51,8 @@ services: ports: - "8081:80" depends_on: - - db + db: + condition: service_healthy networks: - travianz-network restart: unless-stopped diff --git a/tests/docker-compose-contract.sh b/tests/docker-compose-contract.sh new file mode 100644 index 00000000..2d8b03d2 --- /dev/null +++ b/tests/docker-compose-contract.sh @@ -0,0 +1,91 @@ +#!/bin/sh +set -eu + +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +repo_root=$(CDPATH= cd -- "$script_dir/.." && pwd) +compose_file=${1:-"$repo_root/docker-compose.yml"} + +fail() +{ + printf 'FAIL: %s\n' "$1" >&2 + exit 1 +} + +[ -f "$compose_file" ] || fail "Compose file not found: $compose_file" + +if awk ' + /^[[:space:]]*#/ { next } + index($0, "./var/db") && index($0, "/docker-entrypoint-initdb.d") { found = 1 } + END { exit found ? 0 : 1 } +' "$compose_file" +then + fail "installer SQL templates must not be mounted into /docker-entrypoint-initdb.d" +fi + +if ! awk ' + $0 == " db:" { + in_db = 1 + next + } + in_db && /^ [[:alnum:]_-]+:$/ { + in_db = 0 + } + in_db && $0 == " healthcheck:" { + healthcheck = 1 + } + in_db && /healthcheck[.]sh/ && /--connect/ && /--innodb_initialized/ { + readiness_probe = 1 + } + in_db && $0 ~ /^ interval:/ { + interval = 1 + } + in_db && $0 ~ /^ timeout:/ { + timeout = 1 + } + in_db && $0 ~ /^ retries:/ { + retries = 1 + } + END { + exit (healthcheck && readiness_probe && interval && timeout && retries) ? 0 : 1 + } +' "$compose_file" +then + fail "db service must define a bounded MariaDB readiness healthcheck" +fi + +require_healthy_db_dependency() +{ + service=$1 + + if ! awk -v service="$service" ' + $0 == " " service ":" { + in_service = 1 + next + } + in_service && /^ [[:alnum:]_-]+:$/ { + in_service = 0 + } + in_service && $0 == " depends_on:" { + in_dependencies = 1 + next + } + in_service && in_dependencies && $0 == " db:" { + in_db_dependency = 1 + next + } + in_service && in_db_dependency && $0 == " condition: service_healthy" { + found = 1 + } + END { + exit found ? 0 : 1 + } + ' "$compose_file" + then + fail "$service must wait for the db service to become healthy" + fi +} + +require_healthy_db_dependency web +require_healthy_db_dependency phpmyadmin + +printf 'PASS: Docker Compose database initialization contract\n'