102 lines
3.7 KiB
Go
102 lines
3.7 KiB
Go
// 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 }
|