Stream plugin file-tail logs through Run plan
This commit is contained in:
@@ -264,7 +264,7 @@ func runAutonomousLifecyclePlan(distribution domain.RunDistribution, instance do
|
||||
}
|
||||
}
|
||||
for _, source := range plugin.RuntimeProfiles.LogSources {
|
||||
if source.Kind == "process.stdout" || source.Kind == "process.stderr" {
|
||||
if autonomousRunLogSourceKind(source.Kind) {
|
||||
plan.LogSources = append(plan.LogSources, autonomousLogSource(source))
|
||||
}
|
||||
}
|
||||
@@ -318,6 +318,10 @@ func autonomousLogSource(source domain.RuntimeLogSource) domain.RunAutonomousLog
|
||||
return domain.RunAutonomousLogSource{Key: source.Key, Kind: source.Kind, TargetKey: source.TargetKey, StreamKey: source.StreamKey, CursorKind: source.CursorKind, RetentionDays: source.RetentionDays}
|
||||
}
|
||||
|
||||
func autonomousRunLogSourceKind(kind string) bool {
|
||||
return kind == "process.stdout" || kind == "process.stderr" || kind == "file.tail"
|
||||
}
|
||||
|
||||
func autonomousDLLExtension(extension domain.RuntimeDLLExtensionPlan) domain.RunAutonomousDLLExtension {
|
||||
return domain.RunAutonomousDLLExtension{Key: extension.Key, Version: extension.Version, ReleaseURL: extension.ReleaseURL, Checksum: extension.Checksum, SizeBytes: extension.SizeBytes, TargetKey: extension.TargetKey, ModKey: extension.ModKey, DLLRef: extension.DLLRef, SCUMExecutableChecksum: extension.SCUMExecutableChecksum, UE4SSABI: extension.UE4SSABI, RCONPort: extension.RCONPort}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ func TestCoreServiceKeepsPlatformBuildKeyOffMachineJobChannel(t *testing.T) {
|
||||
{Path: "actions/install.json", Content: `{"version":1,"action":"install","mode":"oneshot"}`, Mode: 0o600},
|
||||
{Path: "bin/install-server", Content: "#!/usr/bin/env sh\n", Mode: 0o700},
|
||||
}
|
||||
plugin.RuntimeProfiles.LogSources = append(plugin.RuntimeProfiles.LogSources, domain.RuntimeLogSource{Key: "console", Kind: "process.stdout", TargetKey: "server/process", StreamKey: "console", CursorKind: "sequence", RetentionDays: 14})
|
||||
plugin.RuntimeProfiles.LogSources = append(plugin.RuntimeProfiles.LogSources,
|
||||
domain.RuntimeLogSource{Key: "console", Kind: "process.stdout", TargetKey: "server/process", StreamKey: "console", CursorKind: "sequence", RetentionDays: 14},
|
||||
domain.RuntimeLogSource{Key: "server-events", Kind: "file.tail", TargetKey: "logs/server", StreamKey: "scum.server", CursorKind: "fingerprint", RetentionDays: 90},
|
||||
)
|
||||
if err := svc.store.GamePlugins().Update(plugin); err != nil {
|
||||
t.Fatalf("seed plugin lifecycle assets: %v", err)
|
||||
}
|
||||
@@ -87,7 +90,7 @@ func TestCoreServiceKeepsPlatformBuildKeyOffMachineJobChannel(t *testing.T) {
|
||||
if plan == nil || plan.SchemaVersion != "1" || plan.ServerInstanceID != instance.ID || plan.PluginID != plugin.ID || plan.ProfileKey != "local" || plan.Bootstrap == nil || plan.Bootstrap.Action != domain.ServerLifecycleActionStart || plan.Bootstrap.TargetKey != "actions/start.json" {
|
||||
t.Fatalf("platform builder received incomplete autonomous lifecycle plan: %+v", plan)
|
||||
}
|
||||
if len(plan.DependencyProbes) != 1 || plan.DependencyProbes[0].Key != "java-runtime" || len(plan.InstallPlans) != 1 || plan.InstallPlans[0].Key != "java-install" || len(plan.LogSources) != 1 || plan.LogSources[0].Kind != "process.stdout" || plan.RuntimeBindings["logs/latest"] != "runtime.logs.latest" {
|
||||
if len(plan.DependencyProbes) != 1 || plan.DependencyProbes[0].Key != "java-runtime" || len(plan.InstallPlans) != 1 || plan.InstallPlans[0].Key != "java-install" || len(plan.LogSources) != 3 || !hasAutonomousLogSource(plan.LogSources, "process.stdout", "console") || !hasAutonomousLogSource(plan.LogSources, "file.tail", "latest-log") || !hasAutonomousLogSource(plan.LogSources, "file.tail", "scum.server") || plan.RuntimeBindings["logs/latest"] != "runtime.logs.latest" {
|
||||
t.Fatalf("autonomous lifecycle plan lost plugin runtime declarations: %+v", plan)
|
||||
}
|
||||
var seededPlan domain.RunAutonomousLifecyclePlan
|
||||
@@ -143,6 +146,15 @@ func TestCoreServiceKeepsPlatformBuildKeyOffMachineJobChannel(t *testing.T) {
|
||||
completeDistributionBuild(t, svc, distribution, nil)
|
||||
}
|
||||
|
||||
func hasAutonomousLogSource(sources []domain.RunAutonomousLogSource, kind string, streamKey string) bool {
|
||||
for _, source := range sources {
|
||||
if source.Kind == kind && source.StreamKey == streamKey {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestCoreServiceBuildsWithoutRegisteredDistributionWorker(t *testing.T) {
|
||||
svc, session, instance := newDistributionTestFixture(t)
|
||||
bootstrapEndpoint, err := svc.store.RunEndpoints().Get(instance.RunEndpointID)
|
||||
|
||||
@@ -130,7 +130,7 @@ func (svc *CoreService) ensureLogStreamForBatch(batch domain.LogBatchIngest, sta
|
||||
}
|
||||
|
||||
func (svc *CoreService) ensureRunLogStreamForBatch(batch domain.LogBatchIngest, stamp time.Time) error {
|
||||
if batch.Source != domain.LogStreamSourceProcess && batch.Source != domain.LogStreamSourceManagementProgram {
|
||||
if batch.Source != domain.LogStreamSourceProcess && batch.Source != domain.LogStreamSourceFile && batch.Source != domain.LogStreamSourceManagementProgram {
|
||||
return repo.ErrNotFound
|
||||
}
|
||||
if batch.LogStreamID != runLogStreamID(batch.RunEndpointID, batch.ServerInstanceID, batch.StreamKey) && !legacyAutonomousLogStream(batch) {
|
||||
|
||||
@@ -285,6 +285,42 @@ func TestCoreServiceAcceptsAutonomousRunLogStreamWithoutPlatformJob(t *testing.T
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoreServiceAcceptsAutonomousRunFileTailLogStreamWithoutPlatformJob(t *testing.T) {
|
||||
svc, sessionToken := newRegisteredLogIngestService(t)
|
||||
entry := domain.LogEntry{Seq: 1, Timestamp: time.Date(2026, 7, 3, 12, 0, 1, 0, time.UTC), Level: "info", Line: "LogSCUM: Display: player joined"}
|
||||
streamID := runLogStreamID("run-local", "server-1", "scum.server")
|
||||
ack, err := svc.IngestLogBatch(domain.LogBatchIngest{
|
||||
RunEndpointID: "run-local",
|
||||
SessionToken: sessionToken,
|
||||
LogStreamID: streamID,
|
||||
ServerInstanceID: "server-1",
|
||||
StreamKey: "scum.server",
|
||||
Source: domain.LogStreamSourceFile,
|
||||
FirstSeq: entry.Seq,
|
||||
LastSeq: entry.Seq,
|
||||
Compression: "none",
|
||||
Checksum: validator.LogLineChecksum(entry.Line),
|
||||
Entries: []domain.LogEntry{entry},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ingest autonomous run file-tail log batch: %v", err)
|
||||
}
|
||||
if !ack.Accepted || ack.LogStreamID != streamID || ack.LatestSeq != entry.Seq {
|
||||
t.Fatalf("unexpected autonomous file-tail stream ack: %+v", ack)
|
||||
}
|
||||
stream, err := svc.GetLogStream(streamID)
|
||||
if err != nil {
|
||||
t.Fatalf("get autonomous file-tail stream: %v", err)
|
||||
}
|
||||
if stream.ServerInstanceID != "server-1" || stream.StreamKey != "scum.server" || stream.Source != domain.LogStreamSourceFile {
|
||||
t.Fatalf("unexpected autonomous file-tail stream metadata: %+v", stream)
|
||||
}
|
||||
query, err := svc.QueryLogStream(domain.LogStreamCursorQuery{LogStreamID: streamID, AfterSeq: 0, Limit: 10})
|
||||
if err != nil || len(query.Entries) != 1 || query.Entries[0].Line != entry.Line {
|
||||
t.Fatalf("unexpected autonomous file-tail query: %+v err=%v", query, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoreServiceAcceptsLegacyAutonomousJobLogStreamWithoutPlatformJob(t *testing.T) {
|
||||
svc, sessionToken := newRegisteredLogIngestService(t)
|
||||
entry := domain.LogEntry{Seq: 1, Timestamp: time.Date(2026, 7, 3, 12, 0, 1, 0, time.UTC), Level: "info", Line: "legacy autonomous bootstrap output"}
|
||||
|
||||
Reference in New Issue
Block a user