mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-14 17:32:03 +00:00
test(multiplayer): exercise compose supervisor drain
This commit is contained in:
@@ -24,3 +24,33 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
ports:
|
ports:
|
||||||
- "18080:8080"
|
- "18080:8080"
|
||||||
|
|
||||||
|
game-server:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: game-server
|
||||||
|
command:
|
||||||
|
- --drain-url=http://127.0.0.1:7780/drain
|
||||||
|
- --drain-token-env=COSMIC_CLASH_DRAIN_TOKEN
|
||||||
|
- --drain-grace=10s
|
||||||
|
- --
|
||||||
|
- /opt/cosmic-clash/CosmicClashServer.x86_64
|
||||||
|
- --port=31001
|
||||||
|
- --allocated-mode
|
||||||
|
- --match-id=compose-match
|
||||||
|
- --server-id=compose-server
|
||||||
|
- --playlist-version=casual
|
||||||
|
- --playlist=casual
|
||||||
|
- --client-build=build-1
|
||||||
|
- --assignment-expiry-unix=4102444800
|
||||||
|
- --server-image-digest=sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
- --transport=enet
|
||||||
|
- --region=EU
|
||||||
|
- --join-authorisations-file=/run/cosmic-clash/join-roster.json
|
||||||
|
- --join-authorisations-key-file=/run/secrets/cosmic-clash/join-signing-key
|
||||||
|
- --readiness-port=7780
|
||||||
|
environment:
|
||||||
|
COSMIC_CLASH_DRAIN_TOKEN: compose-drain-token
|
||||||
|
volumes:
|
||||||
|
- ${COMPOSE_SMOKE_DIR:-/tmp/cosmic-clash-allocated-smoke}/join-roster.json:/run/cosmic-clash/join-roster.json:ro
|
||||||
|
- ${COMPOSE_SMOKE_DIR:-/tmp/cosmic-clash-allocated-smoke}/join-signing-key:/run/secrets/cosmic-clash/join-signing-key:ro
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ compose_file="$root_dir/compose.allocated-smoke.yml"
|
|||||||
project="${COMPOSE_PROJECT_NAME:-cosmic-clash-allocated-smoke}"
|
project="${COMPOSE_PROJECT_NAME:-cosmic-clash-allocated-smoke}"
|
||||||
api_url="http://127.0.0.1:18080"
|
api_url="http://127.0.0.1:18080"
|
||||||
secret="compose-workload-secret"
|
secret="compose-workload-secret"
|
||||||
|
smoke_dir="${COMPOSE_SMOKE_DIR:-/tmp/cosmic-clash-allocated-smoke}"
|
||||||
compose=(docker compose -p "$project" -f "$compose_file")
|
compose=(docker compose -p "$project" -f "$compose_file")
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
@@ -20,9 +21,36 @@ trap cleanup EXIT
|
|||||||
command -v docker >/dev/null 2>&1 || { echo "Docker is required for 8.48" >&2; exit 2; }
|
command -v docker >/dev/null 2>&1 || { echo "Docker is required for 8.48" >&2; exit 2; }
|
||||||
docker info >/dev/null 2>&1 || { echo "A running Docker daemon is required for 8.48" >&2; exit 2; }
|
docker info >/dev/null 2>&1 || { echo "A running Docker daemon is required for 8.48" >&2; exit 2; }
|
||||||
|
|
||||||
|
mkdir -p "$smoke_dir"
|
||||||
|
python3 - "$smoke_dir" <<'PY'
|
||||||
|
import base64, hashlib, hmac, json, pathlib, sys, time
|
||||||
|
|
||||||
|
directory = pathlib.Path(sys.argv[1])
|
||||||
|
key = b"compose-join-signing-key"
|
||||||
|
expires = "2099-12-31T00:00:00Z"
|
||||||
|
fields = ["compose-match", "compose-server", "compose-player", "compose-steam", "0", "0", "v1", "1", expires]
|
||||||
|
canonical = b"\0".join(field.encode() for field in fields)
|
||||||
|
signature = base64.urlsafe_b64encode(hmac.new(key, canonical, hashlib.sha256).digest()).rstrip(b"=").decode()
|
||||||
|
envelope = {"Authorisation": {"MatchID": fields[0], "ServerID": fields[1], "PlayerID": fields[2], "SteamID": fields[3], "Slot": 0, "Team": 0, "Protocol": fields[6], "Generation": 1, "ExpiresAt": expires}, "Signature": signature}
|
||||||
|
(directory / "join-signing-key").write_bytes(key)
|
||||||
|
(directory / "join-roster.json").write_text(json.dumps([base64.urlsafe_b64encode(json.dumps(envelope, separators=(",", ":")).encode()).rstrip(b"=").decode()]) + "\n")
|
||||||
|
PY
|
||||||
|
|
||||||
"${compose[@]}" down --volumes --remove-orphans >/dev/null 2>&1 || true
|
"${compose[@]}" down --volumes --remove-orphans >/dev/null 2>&1 || true
|
||||||
"${compose[@]}" up -d --build
|
"${compose[@]}" up -d --build
|
||||||
|
|
||||||
|
for attempt in $(seq 1 60); do
|
||||||
|
if "${compose[@]}" logs game-server 2>/dev/null | grep -q '"event":"server_started"'; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [[ "$attempt" == 60 ]]; then
|
||||||
|
"${compose[@]}" logs >&2
|
||||||
|
echo "allocated Compose game server did not become ready" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
for attempt in $(seq 1 60); do
|
for attempt in $(seq 1 60); do
|
||||||
if curl -fsS "$api_url/healthz" >/dev/null 2>&1; then
|
if curl -fsS "$api_url/healthz" >/dev/null 2>&1; then
|
||||||
break
|
break
|
||||||
@@ -81,9 +109,15 @@ curl -fsS -o /dev/null -w '%{http_code}' \
|
|||||||
-H 'Content-Type: application/json' -d '{"reason":"server_draining"}' | grep -qx 204
|
-H 'Content-Type: application/json' -d '{"reason":"server_draining"}' | grep -qx 204
|
||||||
|
|
||||||
"${compose[@]}" exec -T database psql -At -U cosmic_clash_test -d cosmic_clash_test -c "SELECT count(*) FROM audit_events WHERE action = 'SERVER_SHUTDOWN' AND aggregate_id = 'compose-match'" | grep -qx 1
|
"${compose[@]}" exec -T database psql -At -U cosmic_clash_test -d cosmic_clash_test -c "SELECT count(*) FROM audit_events WHERE action = 'SERVER_SHUTDOWN' AND aggregate_id = 'compose-match'" | grep -qx 1
|
||||||
"${compose[@]}" stop -t 10 control-plane >/dev/null
|
|
||||||
if "${compose[@]}" ps --status running --services | grep -qx control-plane; then
|
"${compose[@]}" stop -t 12 game-server >/dev/null
|
||||||
echo "control-plane did not stop cleanly" >&2
|
if "${compose[@]}" ps --status running --services | grep -qx game-server; then
|
||||||
|
echo "allocated game-server did not stop after supervisor drain" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "8.48 PASS: allocated Compose HTTP result/retry/shutdown flow completed"
|
if ! "${compose[@]}" logs game-server | grep -q '"event":"server_draining"'; then
|
||||||
|
echo "allocated game-server did not record a drain request" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
"${compose[@]}" stop -t 10 control-plane >/dev/null
|
||||||
|
echo "8.48 PASS: allocated Compose HTTP result/retry/shutdown and supervisor drain completed"
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ class ComposeManifestTest(unittest.TestCase):
|
|||||||
allocated = (ROOT / "compose.allocated-smoke.yml").read_text()
|
allocated = (ROOT / "compose.allocated-smoke.yml").read_text()
|
||||||
legacy = (ROOT / "compose.phase6-smoke.yml").read_text()
|
legacy = (ROOT / "compose.phase6-smoke.yml").read_text()
|
||||||
self.assertIn("target: testkit-api", allocated)
|
self.assertIn("target: testkit-api", allocated)
|
||||||
|
self.assertIn("target: game-server", allocated)
|
||||||
|
self.assertIn("--drain-url=http://127.0.0.1:7780/drain", allocated)
|
||||||
|
self.assertIn("--allocated-mode", allocated)
|
||||||
self.assertIn('"18080:8080"', allocated)
|
self.assertIn('"18080:8080"', allocated)
|
||||||
self.assertNotIn("compose.phase6-smoke.yml", allocated)
|
self.assertNotIn("compose.phase6-smoke.yml", allocated)
|
||||||
self.assertNotIn("18080:8080", legacy)
|
self.assertNotIn("18080:8080", legacy)
|
||||||
|
|||||||
Reference in New Issue
Block a user