122 lines
4.0 KiB
Go
122 lines
4.0 KiB
Go
// Package engineversion defines the engine version registry domain
|
|
// model owned by Game Master.
|
|
//
|
|
// The registry mirrors the durable shape of the `engine_versions`
|
|
// PostgreSQL table (see
|
|
// `galaxy/gamemaster/internal/adapters/postgres/migrations/00001_init.sql`)
|
|
// and the user-visible status enum frozen in
|
|
// `galaxy/gamemaster/api/internal-openapi.yaml`.
|
|
//
|
|
// `Options` is intentionally kept opaque ([]byte holding raw JSON) so
|
|
// the v1 service does not impose a Go-side schema on the engine-owned
|
|
// document. Schema-aware handling lands when an engine version actually
|
|
// requires it; until then the registry is a pass-through store.
|
|
package engineversion
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Status identifies one engine-version registry state.
|
|
type Status string
|
|
|
|
const (
|
|
// StatusActive marks a version as deployable. Lobby's start flow
|
|
// resolves image refs only against active versions.
|
|
StatusActive Status = "active"
|
|
|
|
// StatusDeprecated marks a version as no longer offered for new
|
|
// starts. Already-running games on a deprecated version are
|
|
// unaffected; the runtime stays bound to the version it started on.
|
|
StatusDeprecated Status = "deprecated"
|
|
)
|
|
|
|
// IsKnown reports whether status belongs to the frozen engine-version
|
|
// status vocabulary.
|
|
func (status Status) IsKnown() bool {
|
|
switch status {
|
|
case StatusActive, StatusDeprecated:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// AllStatuses returns the frozen list of every engine-version status
|
|
// value. The slice order is stable across calls.
|
|
func AllStatuses() []Status {
|
|
return []Status{StatusActive, StatusDeprecated}
|
|
}
|
|
|
|
// EngineVersion stores one row of the `engine_versions` registry table.
|
|
// Options carries the raw `jsonb` document verbatim so the registry
|
|
// stays decoupled from any engine-side schema.
|
|
type EngineVersion struct {
|
|
// Version stores the canonical semver string (primary key).
|
|
Version string
|
|
|
|
// ImageRef stores the Docker reference of the engine image.
|
|
ImageRef string
|
|
|
|
// Options stores the engine-side options document as raw JSON. Empty
|
|
// is treated as `{}` by adapters that hydrate the column.
|
|
Options []byte
|
|
|
|
// Status reports whether the version is deployable (`active`) or
|
|
// no longer offered for new starts (`deprecated`).
|
|
Status Status
|
|
|
|
// CreatedAt stores the wall-clock at which the row was created.
|
|
CreatedAt time.Time
|
|
|
|
// UpdatedAt stores the wall-clock of the most recent mutation.
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// Validate reports whether record satisfies the engine-version
|
|
// invariants implied by `engine_versions_status_chk` and the README
|
|
// §Engine Version Registry surface.
|
|
func (record EngineVersion) Validate() error {
|
|
if strings.TrimSpace(record.Version) == "" {
|
|
return fmt.Errorf("version must not be empty")
|
|
}
|
|
if strings.TrimSpace(record.ImageRef) == "" {
|
|
return fmt.Errorf("image ref must not be empty")
|
|
}
|
|
if !record.Status.IsKnown() {
|
|
return fmt.Errorf("status %q is unsupported", record.Status)
|
|
}
|
|
if record.CreatedAt.IsZero() {
|
|
return fmt.Errorf("created at must not be zero")
|
|
}
|
|
if record.UpdatedAt.IsZero() {
|
|
return fmt.Errorf("updated at must not be zero")
|
|
}
|
|
if record.UpdatedAt.Before(record.CreatedAt) {
|
|
return fmt.Errorf("updated at must not be before created at")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ErrNotFound reports that an engine-version lookup failed because no
|
|
// matching row exists.
|
|
var ErrNotFound = errors.New("engine version not found")
|
|
|
|
// ErrInUse reports that a hard-delete or deprecate operation was
|
|
// rejected because the version is still referenced by a non-finished
|
|
// runtime record.
|
|
var ErrInUse = errors.New("engine version in use")
|
|
|
|
// ErrConflict reports that an engine-version mutation could not be
|
|
// applied because a row with the same primary key already exists.
|
|
// Adapters surface a PostgreSQL unique-violation through this sentinel
|
|
// so the service layer maps it to a `conflict` REST envelope.
|
|
var ErrConflict = errors.New("engine version already exists")
|
|
|
|
// ErrInvalidSemver reports that a semver string did not parse against
|
|
// `golang.org/x/mod/semver`'s grammar.
|
|
var ErrInvalidSemver = errors.New("invalid semver")
|