feat: runtime manager

This commit is contained in:
Ilia Denisov
2026-04-28 20:39:18 +02:00
committed by GitHub
parent e0a99b346b
commit a7cee15115
289 changed files with 45660 additions and 2207 deletions
+31
View File
@@ -7,6 +7,7 @@ import (
"strings"
"time"
"galaxy/lobby/internal/domain/engineimage"
"galaxy/lobby/internal/telemetry"
"galaxy/postgres"
"galaxy/redisconn"
@@ -49,6 +50,8 @@ const (
raceNameDirectoryBackendEnvVar = "LOBBY_RACE_NAME_DIRECTORY_BACKEND"
raceNameExpirationIntervalEnvVar = "LOBBY_RACE_NAME_EXPIRATION_INTERVAL"
engineImageTemplateEnvVar = "LOBBY_ENGINE_IMAGE_TEMPLATE"
otelServiceNameEnvVar = "OTEL_SERVICE_NAME"
otelTracesExporterEnvVar = "OTEL_TRACES_EXPORTER"
otelMetricsExporterEnvVar = "OTEL_METRICS_EXPORTER"
@@ -78,6 +81,7 @@ const (
defaultGMTimeout = 5 * time.Second
defaultEnrollmentAutomationInterval = 30 * time.Second
defaultRaceNameExpirationInterval = time.Hour
defaultEngineImageTemplate = "galaxy/game:" + engineimage.VersionPlaceholder
defaultOTelServiceName = "galaxy-lobby"
// RaceNameDirectoryBackendPostgres selects the PostgreSQL-backed
@@ -134,6 +138,9 @@ type Config struct {
// every pending_registration whose eligible_until has passed.
PendingRegistration PendingRegistrationConfig
// RuntimeManager configures the Runtime Manager publisher contract.
RuntimeManager RuntimeManagerConfig
// Telemetry configures the process-wide OpenTelemetry runtime.
Telemetry TelemetryConfig
}
@@ -410,6 +417,27 @@ func (cfg PendingRegistrationConfig) Validate() error {
return nil
}
// RuntimeManagerConfig configures the Lobby-side Runtime Manager
// publisher contract. Lobby resolves the Docker image reference it
// publishes on `runtime:start_jobs` from a per-game
// `target_engine_version` and the configured EngineImageTemplate.
type RuntimeManagerConfig struct {
// EngineImageTemplate stores the Docker reference template applied
// to a game's `target_engine_version`. The string must contain the
// literal placeholder `{engine_version}`; Lobby fails fast at
// startup otherwise.
EngineImageTemplate string
}
// Validate reports whether cfg stores a usable Runtime Manager
// publisher configuration.
func (cfg RuntimeManagerConfig) Validate() error {
if _, err := engineimage.NewResolver(cfg.EngineImageTemplate); err != nil {
return fmt.Errorf("engine image template: %w", err)
}
return nil
}
// TelemetryConfig configures the Game Lobby Service OpenTelemetry runtime.
type TelemetryConfig struct {
// ServiceName overrides the default OpenTelemetry service name.
@@ -504,6 +532,9 @@ func DefaultConfig() Config {
PendingRegistration: PendingRegistrationConfig{
Interval: defaultRaceNameExpirationInterval,
},
RuntimeManager: RuntimeManagerConfig{
EngineImageTemplate: defaultEngineImageTemplate,
},
Telemetry: TelemetryConfig{
ServiceName: defaultOTelServiceName,
TracesExporter: "none",
+32
View File
@@ -40,6 +40,7 @@ func TestDefaultConfig(t *testing.T) {
assert.Equal(t, 5*time.Second, cfg.GM.Timeout)
assert.Equal(t, 30*time.Second, cfg.EnrollmentAutomation.Interval)
assert.Equal(t, time.Hour, cfg.PendingRegistration.Interval)
assert.Equal(t, "galaxy/game:{engine_version}", cfg.RuntimeManager.EngineImageTemplate)
assert.Equal(t, "galaxy-lobby", cfg.Telemetry.ServiceName)
assert.Equal(t, "none", cfg.Telemetry.TracesExporter)
assert.Equal(t, "none", cfg.Telemetry.MetricsExporter)
@@ -114,6 +115,7 @@ func TestLoadFromEnvOverrides(t *testing.T) {
t.Setenv("LOBBY_NOTIFICATION_INTENTS_STREAM", "alt:intents")
t.Setenv("LOBBY_ENROLLMENT_AUTOMATION_INTERVAL", "45s")
t.Setenv("LOBBY_RACE_NAME_EXPIRATION_INTERVAL", "15m")
t.Setenv("LOBBY_ENGINE_IMAGE_TEMPLATE", "registry.example.com/galaxy/game:{engine_version}")
t.Setenv("OTEL_SERVICE_NAME", "galaxy-lobby-test")
cfg, err := LoadFromEnv()
@@ -129,6 +131,7 @@ func TestLoadFromEnvOverrides(t *testing.T) {
assert.Equal(t, "alt:intents", cfg.Redis.NotificationIntentsStream)
assert.Equal(t, 45*time.Second, cfg.EnrollmentAutomation.Interval)
assert.Equal(t, 15*time.Minute, cfg.PendingRegistration.Interval)
assert.Equal(t, "registry.example.com/galaxy/game:{engine_version}", cfg.RuntimeManager.EngineImageTemplate)
assert.Equal(t, "galaxy-lobby-test", cfg.Telemetry.ServiceName)
}
@@ -291,6 +294,34 @@ func TestEnrollmentAutomationConfigValidate(t *testing.T) {
require.ErrorContains(t, EnrollmentAutomationConfig{}.Validate(), "interval must be positive")
}
func TestRuntimeManagerConfigValidate(t *testing.T) {
t.Parallel()
require.NoError(t, RuntimeManagerConfig{EngineImageTemplate: "galaxy/game:{engine_version}"}.Validate())
require.ErrorContains(t,
RuntimeManagerConfig{EngineImageTemplate: ""}.Validate(),
"template must not be empty",
)
require.ErrorContains(t,
RuntimeManagerConfig{EngineImageTemplate: "galaxy/game:1.0.0"}.Validate(),
"placeholder",
)
}
func TestLoadFromEnvRejectsInvalidEngineImageTemplate(t *testing.T) {
clearAllEnv(t)
t.Setenv("LOBBY_REDIS_MASTER_ADDR", testRedisAddr)
t.Setenv("LOBBY_REDIS_PASSWORD", testRedisSecret)
t.Setenv("LOBBY_POSTGRES_PRIMARY_DSN", testDSN)
t.Setenv("LOBBY_USER_SERVICE_BASE_URL", testUserBaseURL)
t.Setenv("LOBBY_GM_BASE_URL", testGMBaseURL)
t.Setenv("LOBBY_ENGINE_IMAGE_TEMPLATE", "galaxy/game:no-placeholder")
_, err := LoadFromEnv()
require.Error(t, err)
require.Contains(t, err.Error(), "LOBBY_ENGINE_IMAGE_TEMPLATE")
}
func TestPendingRegistrationConfigValidate(t *testing.T) {
t.Parallel()
@@ -367,6 +398,7 @@ func clearAllEnv(t *testing.T) {
enrollmentAutomationIntervalEnvVar,
raceNameDirectoryBackendEnvVar,
raceNameExpirationIntervalEnvVar,
engineImageTemplateEnvVar,
otelServiceNameEnvVar,
otelTracesExporterEnvVar,
otelMetricsExporterEnvVar,
+2
View File
@@ -108,6 +108,8 @@ func LoadFromEnv() (Config, error) {
return Config{}, err
}
cfg.RuntimeManager.EngineImageTemplate = stringEnv(engineImageTemplateEnvVar, cfg.RuntimeManager.EngineImageTemplate)
cfg.Telemetry.ServiceName = stringEnv(otelServiceNameEnvVar, cfg.Telemetry.ServiceName)
cfg.Telemetry.TracesExporter = normalizeExporterValue(stringEnv(otelTracesExporterEnvVar, cfg.Telemetry.TracesExporter))
cfg.Telemetry.MetricsExporter = normalizeExporterValue(stringEnv(otelMetricsExporterEnvVar, cfg.Telemetry.MetricsExporter))
+3
View File
@@ -41,6 +41,9 @@ func (cfg Config) Validate() error {
if err := cfg.PendingRegistration.Validate(); err != nil {
return fmt.Errorf("%s: %w", raceNameExpirationIntervalEnvVar, err)
}
if err := cfg.RuntimeManager.Validate(); err != nil {
return fmt.Errorf("%s: %w", engineImageTemplateEnvVar, err)
}
if err := cfg.Telemetry.Validate(); err != nil {
return err
}