// Package runtime owns the lifecycle of game-engine containers and the // engine-version registry on the platform side. It is the single // component permitted to talk to the Docker daemon // (`internal/dockerclient`) and to running engine HTTP listeners // (`internal/engineclient`); cross-cutting concerns such as the lobby // state machine, notification fan-out, or player-mapping persistence // live in their domain packages and reach into runtime through a // narrow interface set documented in `deps.go`. // // The package introduces the package on top of the The implementation lobby. The // lobby `RuntimeGateway` shifts from a logger-only no-op to a real // adapter backed by `*runtime.Service`; runtime publishes snapshots // back into lobby through `LobbyConsumer.OnRuntimeSnapshot`. The // engine-version registry CRUD endpoints under // `/api/v1/admin/engine-versions/*` and the runtime admin/user proxy // endpoints flip from 501 placeholders to real responses. package runtime import ( "errors" "github.com/jackc/pgx/v5/pgconn" ) // Runtime status vocabulary mirrors `runtime_records_status_chk` in // `backend/internal/postgres/migrations/00001_init.sql`. const ( RuntimeStatusStarting = "starting" RuntimeStatusRunning = "running" RuntimeStatusGenerationInProgress = "generation_in_progress" RuntimeStatusGenerationFailed = "generation_failed" RuntimeStatusStopped = "stopped" RuntimeStatusEngineUnreachable = "engine_unreachable" RuntimeStatusFinished = "finished" RuntimeStatusRemoved = "removed" ) // Operation log vocabulary recorded into `runtime_operation_log.op` and // `runtime_operation_log.status`. Kept as exported constants so // runtime, admin handlers, and tests share the same wire values. const ( OpStart = "start" OpStop = "stop" OpPause = "pause" OpResume = "resume" OpRestart = "restart" OpPatch = "patch" OpForceNextTurn = "force_next_turn" OpReconcile = "reconcile" OpTurn = "turn" OpSourceLobby = "lobby" OpSourceAdmin = "admin" OpSourceScheduler = "scheduler" OpSourceReconciler = "reconciler" OpStatusQueued = "queued" OpStatusRunning = "running" OpStatusSucceeded = "succeeded" OpStatusFailed = "failed" ) // Container naming convention. The hostname is the primary alias on // the user-defined Docker network; the engine endpoint URL is // synthesised by `dockerclient.Adapter.Run` as `http://{hostname}:8080`. const ( containerNamePrefix = "galaxy-game-" containerHostPrefix = "galaxy-game-" ) // pgErrCodeUniqueViolation is the SQLSTATE Postgres emits on a UNIQUE // constraint violation. Kept locally so the runtime package does not // import `internal/admin` or `internal/lobby` for the constant. const pgErrCodeUniqueViolation = "23505" // isUniqueViolation reports whether err is a Postgres UNIQUE // constraint violation, optionally restricted to a specific constraint // name. Empty constraintName matches any UNIQUE violation. func isUniqueViolation(err error, constraintName string) bool { var pgErr *pgconn.PgError if !errors.As(err, &pgErr) { return false } if pgErr.Code != pgErrCodeUniqueViolation { return false } if constraintName == "" { return true } return pgErr.ConstraintName == constraintName } // ContainerName synthesises the Docker container / hostname for the // supplied game id. Exported so tests and the reconciler can resolve // the inverse mapping without duplicating the format string. func ContainerName(gameID string) string { return containerNamePrefix + gameID } // HostName synthesises the in-network hostname for the supplied game // id. Mirrors ContainerName so the engine endpoint URL `http://{host}:8080` // resolves through Docker DNS on the user-defined network. func HostName(gameID string) string { return containerHostPrefix + gameID }