Files
2026-05-06 10:14:55 +03:00

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 }