Fix run package profile and log ingestion
This commit is contained in:
@@ -234,6 +234,7 @@ func (svc *CoreService) runDistributionPackageContext(distribution domain.RunDis
|
|||||||
} else if !errors.Is(bindingErr, repo.ErrNotFound) {
|
} else if !errors.Is(bindingErr, repo.ErrNotFound) {
|
||||||
return runDistributionPackageContext{}, bindingErr
|
return runDistributionPackageContext{}, bindingErr
|
||||||
}
|
}
|
||||||
|
profileKey = defaultRunDistributionProfileKey(plugin, profileKey)
|
||||||
plan, err := runAutonomousLifecyclePlan(distribution, instance, plugin, profileKey, bindings)
|
plan, err := runAutonomousLifecyclePlan(distribution, instance, plugin, profileKey, bindings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return runDistributionPackageContext{}, err
|
return runDistributionPackageContext{}, err
|
||||||
@@ -245,6 +246,20 @@ func (svc *CoreService) runDistributionPackageContext(distribution domain.RunDis
|
|||||||
return runDistributionPackageContext{profileKey: profileKey, workspaceSeed: seed, autonomousLifecycle: plan}, nil
|
return runDistributionPackageContext{profileKey: profileKey, workspaceSeed: seed, autonomousLifecycle: plan}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func defaultRunDistributionProfileKey(plugin domain.GamePlugin, profileKey string) string {
|
||||||
|
profileKey = strings.TrimSpace(profileKey)
|
||||||
|
if profileKey != "" {
|
||||||
|
if profile, ok := runtimeLifecycleProfileForKey(plugin.RuntimeProfiles, profileKey); ok {
|
||||||
|
return profile.Key
|
||||||
|
}
|
||||||
|
return profileKey
|
||||||
|
}
|
||||||
|
if len(plugin.RuntimeProfiles.LifecycleProfiles) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return plugin.RuntimeProfiles.LifecycleProfiles[0].Key
|
||||||
|
}
|
||||||
|
|
||||||
func runAutonomousLifecyclePlan(distribution domain.RunDistribution, instance domain.ServerInstance, plugin domain.GamePlugin, profileKey string, bindings map[string]string) (*domain.RunAutonomousLifecyclePlan, error) {
|
func runAutonomousLifecyclePlan(distribution domain.RunDistribution, instance domain.ServerInstance, plugin domain.GamePlugin, profileKey string, bindings map[string]string) (*domain.RunAutonomousLifecyclePlan, error) {
|
||||||
plan := &domain.RunAutonomousLifecyclePlan{
|
plan := &domain.RunAutonomousLifecyclePlan{
|
||||||
SchemaVersion: "1",
|
SchemaVersion: "1",
|
||||||
|
|||||||
@@ -214,6 +214,55 @@ func hasAutonomousLogSource(sources []domain.RunAutonomousLogSource, kind string
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCoreServiceRunDistributionDefaultsEmptyDeploymentProfileToPluginLifecycleProfile(t *testing.T) {
|
||||||
|
svc := newTestCoreService()
|
||||||
|
plugin := scumDeploymentTestPlugin()
|
||||||
|
plugin.Name = "SCUM"
|
||||||
|
plugin.Version = "0.1.1"
|
||||||
|
plugin.ServerType = "scum"
|
||||||
|
plugin.Status = domain.GamePluginStatusInstalled
|
||||||
|
plugin.SupportedOS = []string{"windows"}
|
||||||
|
plugin.DeclaredPermissions = []string{"server.run.distribution"}
|
||||||
|
plugin.LifecycleActions = domain.PluginLifecycleActions{Install: "actions/install.json", Start: "actions/start.json", Stop: "actions/stop.json", Status: "actions/status.json"}
|
||||||
|
plugin.RuntimeProfiles.LifecycleProfiles = []domain.RuntimeLifecycleProfile{{Key: "run-local", Mode: "local-process", Capabilities: []string{domain.LifecycleCapabilityInstall, domain.LifecycleCapabilityStart, domain.LifecycleCapabilityStop, domain.LifecycleCapabilityStatus}, ActionRefs: domain.PluginLifecycleActions{Install: "actions/install.json", Start: "actions/start.json", Stop: "actions/stop.json", Status: "actions/status.json"}, Platforms: []string{"windows"}}}
|
||||||
|
requireManualSCUMRuntimeBindings(&plugin, "run-local")
|
||||||
|
if err := svc.store.GamePlugins().Create(plugin); err != nil {
|
||||||
|
t.Fatalf("create SCUM plugin: %v", err)
|
||||||
|
}
|
||||||
|
session := createServiceUserAndLogin(t, svc, domain.User{ID: "scum-default-profile-owner", DisplayName: "SCUM Default Profile Owner", Email: "scum-default-profile@example.test", Roles: []string{"server-owner"}, PasswordHash: "secret-password"})
|
||||||
|
created, err := svc.CreateServerInstanceWorkflowForSession(session, domain.ServerLifecycleCreate{
|
||||||
|
ID: "scum-default-profile-package", PluginID: plugin.ID, Name: "SCUM Default Profile Package", IdempotencyKey: "scum-default-profile-package-create",
|
||||||
|
Deployment: domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided, ServerRoot: `D:\scum-default-profile`, CreateInputs: map[string]string{"serverName": "Moon", "gamePort": "27000", "queryPort": "27015", "maxPlayers": "128"}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create SCUM guided draft without explicit profile: %v", err)
|
||||||
|
}
|
||||||
|
if created.Instance.Deployment.ProfileKey != "run-local" {
|
||||||
|
t.Fatalf("expected create defaults to persist plugin lifecycle profile, got %+v", created.Instance.Deployment)
|
||||||
|
}
|
||||||
|
|
||||||
|
legacy := created.Instance
|
||||||
|
legacy.Deployment.ProfileKey = ""
|
||||||
|
if err := svc.store.ServerInstances().Update(legacy); err != nil {
|
||||||
|
t.Fatalf("simulate legacy empty deployment profile: %v", err)
|
||||||
|
}
|
||||||
|
inputs := make(chan domain.DistributionBuildInput, 1)
|
||||||
|
svc.ConfigureDistributionBuilder(captureDistributionBuilder{inputs: inputs, payload: []byte("profile-default-build")})
|
||||||
|
if _, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ServerInstanceID: legacy.ID, TargetOS: "windows", TargetArch: "amd64", IdempotencyKey: "scum-default-profile-package"}); err != nil {
|
||||||
|
t.Fatalf("generate Run distribution with empty deployment profile: %v", err)
|
||||||
|
}
|
||||||
|
var platformInput domain.DistributionBuildInput
|
||||||
|
select {
|
||||||
|
case platformInput = <-inputs:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("platform builder did not receive profile-defaulted input")
|
||||||
|
}
|
||||||
|
plan := platformInput.AutonomousLifecycle
|
||||||
|
if platformInput.ProfileKey != "run-local" || plan == nil || plan.ProfileKey != "run-local" || plan.Deployment == nil || plan.Deployment.ProfileKey != "run-local" {
|
||||||
|
t.Fatalf("expected package context to default empty deployment profile to run-local, input=%+v plan=%+v", platformInput, plan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCoreServiceBuildsWithoutRegisteredDistributionWorker(t *testing.T) {
|
func TestCoreServiceBuildsWithoutRegisteredDistributionWorker(t *testing.T) {
|
||||||
svc, session, instance := newDistributionTestFixture(t)
|
svc, session, instance := newDistributionTestFixture(t)
|
||||||
bootstrapEndpoint, err := svc.store.RunEndpoints().Get(instance.RunEndpointID)
|
bootstrapEndpoint, err := svc.store.RunEndpoints().Get(instance.RunEndpointID)
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ func (svc *CoreService) ensureRunLogStreamForBatch(batch domain.LogBatchIngest,
|
|||||||
expectedStreamID = runSessionLogStreamID(batch.RunEndpointID, batch.ServerInstanceID, batch.LogSessionID, batch.StreamKey)
|
expectedStreamID = runSessionLogStreamID(batch.RunEndpointID, batch.ServerInstanceID, batch.LogSessionID, batch.StreamKey)
|
||||||
}
|
}
|
||||||
if batch.LogStreamID != expectedStreamID {
|
if batch.LogStreamID != expectedStreamID {
|
||||||
if !legacyAutonomousLogStream(batch) {
|
if !legacySessionRunLogStream(batch) && !legacyAutonomousLogStream(batch) {
|
||||||
return repo.ErrNotFound
|
return repo.ErrNotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,6 +168,10 @@ func (svc *CoreService) ensureRunLogStreamForBatch(batch domain.LogBatchIngest,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func legacySessionRunLogStream(batch domain.LogBatchIngest) bool {
|
||||||
|
return batch.LogSessionID != "" && batch.LogStreamID == runLogStreamID(batch.RunEndpointID, batch.ServerInstanceID, batch.StreamKey)
|
||||||
|
}
|
||||||
|
|
||||||
func legacyAutonomousLogStream(batch domain.LogBatchIngest) bool {
|
func legacyAutonomousLogStream(batch domain.LogBatchIngest) bool {
|
||||||
jobID, ok := jobIDFromLogBatch(batch)
|
jobID, ok := jobIDFromLogBatch(batch)
|
||||||
return ok && strings.HasPrefix(jobID, "autonomous-")
|
return ok && strings.HasPrefix(jobID, "autonomous-")
|
||||||
|
|||||||
@@ -463,6 +463,28 @@ func TestCoreServiceAcceptsSessionMetadataOnLegacyAutonomousStreamID(t *testing.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCoreServiceAcceptsSessionMetadataOnLegacyRunStreamID(t *testing.T) {
|
||||||
|
svc, sessionToken := newRegisteredLogIngestService(t)
|
||||||
|
batch := validLogBatch(t, sessionToken, 1, 1)
|
||||||
|
batch.LogStreamID = runLogStreamID("run-local", "server-1", "stdout")
|
||||||
|
batch.LogSessionID = "session-a"
|
||||||
|
batch.SessionStartedAt = time.Date(2026, 7, 3, 12, 30, 0, 0, time.UTC)
|
||||||
|
ack, err := svc.IngestLogBatch(batch)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ingest session-scoped legacy run stream: %v", err)
|
||||||
|
}
|
||||||
|
if !ack.Accepted || ack.LogStreamID != batch.LogStreamID || ack.LatestSeq != batch.LastSeq {
|
||||||
|
t.Fatalf("unexpected legacy run session ack: %+v", ack)
|
||||||
|
}
|
||||||
|
stream, err := svc.GetLogStream(batch.LogStreamID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get session-scoped legacy run stream: %v", err)
|
||||||
|
}
|
||||||
|
if stream.LogSessionID != batch.LogSessionID || !stream.SessionStartedAt.Equal(batch.SessionStartedAt) {
|
||||||
|
t.Fatalf("session metadata was not persisted: %+v", stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCoreServiceRejectsOutOfOrderAndConflictingLogBatches(t *testing.T) {
|
func TestCoreServiceRejectsOutOfOrderAndConflictingLogBatches(t *testing.T) {
|
||||||
svc, sessionToken := newRegisteredLogIngestService(t)
|
svc, sessionToken := newRegisteredLogIngestService(t)
|
||||||
createLogStreamFixture(t, svc)
|
createLogStreamFixture(t, svc)
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ func (svc *CoreService) CreateServerInstanceWorkflow(create domain.ServerLifecyc
|
|||||||
instance.State = domain.ServerInstanceStateDraft
|
instance.State = domain.ServerInstanceStateDraft
|
||||||
}
|
}
|
||||||
if instance.Deployment.Mode != "" {
|
if instance.Deployment.Mode != "" {
|
||||||
|
if strings.TrimSpace(create.ProfileKey) != "" {
|
||||||
instance.Deployment.ProfileKey = create.ProfileKey
|
instance.Deployment.ProfileKey = create.ProfileKey
|
||||||
|
}
|
||||||
instance.Deployment.RuntimeBindings = domain.CopyStringMap(create.Bindings)
|
instance.Deployment.RuntimeBindings = domain.CopyStringMap(create.Bindings)
|
||||||
instance.Deployment.Revision = maxInt(1, instance.Deployment.Revision)
|
instance.Deployment.Revision = maxInt(1, instance.Deployment.Revision)
|
||||||
instance.Deployment.UpdatedAt = stamp
|
instance.Deployment.UpdatedAt = stamp
|
||||||
|
|||||||
Reference in New Issue
Block a user