Make Run runtime state authoritative
This commit is contained in:
@@ -443,6 +443,93 @@ func TestCoreServiceGeneratedSCUMRunRegistrationQueuesGuidedStart(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoreServiceGeneratedRunRegistrationReconcilesStaleRunningState(t *testing.T) {
|
||||
svc := newTestCoreService()
|
||||
plugin := createGeneratedRunStatusPlugin(t, svc)
|
||||
instance := domain.ServerInstance{ID: "managed-stale-running", PluginID: plugin.ID, PluginVersion: plugin.Version, RunEndpointID: dedicatedRunEndpointID("managed-stale-running"), Name: "Managed Stale Running", State: domain.ServerInstanceStateRunning, ConfigVersion: 1, Deployment: domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided, ProfileKey: "run-local", ServerRoot: `D:\scum-stale`, Revision: 1}}
|
||||
if err := svc.store.ServerInstances().Create(instance); err != nil {
|
||||
t.Fatalf("create stale running server: %v", err)
|
||||
}
|
||||
registered := registerGeneratedRunForStatusTest(t, svc, instance, plugin.ID)
|
||||
jobs, err := svc.store.Jobs().List(domain.JobFilter{ServerInstanceID: instance.ID})
|
||||
if err != nil || len(jobs) != 1 || jobs[0].Capability != domain.LifecycleCapabilityStatus || jobs[0].TargetKey != "actions/status.json" {
|
||||
t.Fatalf("expected one status reconciliation job, jobs=%+v err=%v", jobs, err)
|
||||
}
|
||||
claim, err := svc.ClaimRunJob(domain.RunJobClaim{RunEndpointID: instance.RunEndpointID, SessionToken: registered.SessionToken, Capabilities: []string{domain.LifecycleCapabilityStatus}, Capacity: domain.RunCapacity{MaxJobs: 1}})
|
||||
if err != nil || !claim.HasJob || claim.Job.JobID != jobs[0].ID {
|
||||
t.Fatalf("claim status reconciliation: claim=%+v err=%v", claim, err)
|
||||
}
|
||||
if _, err := svc.CompleteRunJob(domain.RunJobResult{RunEndpointID: instance.RunEndpointID, SessionToken: registered.SessionToken, JobID: claim.Job.JobID, LeaseToken: claim.Job.LeaseToken, Attempt: claim.Job.Attempt, State: domain.JobStateSucceeded, Progress: domain.RunJobProgressReport{Percent: 100, Message: "process status queried"}, Message: "process status queried", ExecutionResult: domain.JobExecutionResult{Kind: "process", ProcessState: "not-started", ExitClassification: "not-started", AuditSummary: "bounded process state"}}); err != nil {
|
||||
t.Fatalf("complete status reconciliation: %v", err)
|
||||
}
|
||||
stored, err := svc.GetServerInstance(instance.ID)
|
||||
if err != nil || stored.State != domain.ServerInstanceStateStopped {
|
||||
t.Fatalf("Run-reported not-started should correct stale running state, server=%+v err=%v", stored, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoreServiceGeneratedRunRegistrationSkipsStatusWhenLifecycleJobActive(t *testing.T) {
|
||||
svc := newTestCoreService()
|
||||
plugin := createGeneratedRunStatusPlugin(t, svc)
|
||||
instance := domain.ServerInstance{ID: "managed-active-start", PluginID: plugin.ID, PluginVersion: plugin.Version, RunEndpointID: dedicatedRunEndpointID("managed-active-start"), Name: "Managed Active Start", State: domain.ServerInstanceStateRunning, ConfigVersion: 1, Deployment: domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided, ProfileKey: "run-local", ServerRoot: `D:\scum-active`, Revision: 1}}
|
||||
if err := svc.store.ServerInstances().Create(instance); err != nil {
|
||||
t.Fatalf("create active-start server: %v", err)
|
||||
}
|
||||
if err := svc.store.RunEndpoints().Create(domain.RunEndpoint{ID: instance.RunEndpointID, DisplayName: "Managed Active Start Run", Version: "0.1.0", Platform: "windows", Architecture: "amd64", Status: domain.RunEndpointStatusOnline, Capabilities: []string{domain.LifecycleCapabilityInstall, domain.LifecycleCapabilityStart, domain.LifecycleCapabilityStop, domain.LifecycleCapabilityStatus}, Capacity: domain.RunCapacity{MaxJobs: 1}, LastHeartbeatAt: fixedTime}); err != nil {
|
||||
t.Fatalf("create active-start endpoint: %v", err)
|
||||
}
|
||||
if _, err := svc.dispatchLifecycleJob(instance, domain.ServerLifecycleActionStart, "already-active-start"); err != nil {
|
||||
t.Fatalf("queue active start job: %v", err)
|
||||
}
|
||||
registerGeneratedRunForStatusTest(t, svc, instance, plugin.ID)
|
||||
jobs, err := svc.store.Jobs().List(domain.JobFilter{ServerInstanceID: instance.ID})
|
||||
if err != nil || len(jobs) != 1 || jobs[0].Capability != domain.LifecycleCapabilityStart {
|
||||
t.Fatalf("active lifecycle job should suppress status reconciliation, jobs=%+v err=%v", jobs, err)
|
||||
}
|
||||
}
|
||||
|
||||
func createGeneratedRunStatusPlugin(t *testing.T, svc *CoreService) domain.GamePlugin {
|
||||
t.Helper()
|
||||
plugin := scumDeploymentTestPlugin()
|
||||
plugin.Name = "SCUM"
|
||||
plugin.Version = "0.1.1"
|
||||
plugin.ServerType = "scum"
|
||||
plugin.Status = domain.GamePluginStatusInstalled
|
||||
plugin.SupportedOS = []string{"windows"}
|
||||
plugin.RequiredRunCapabilities = []string{domain.LifecycleCapabilityInstall, domain.LifecycleCapabilityStart, domain.LifecycleCapabilityStop, domain.LifecycleCapabilityStatus}
|
||||
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 status plugin fixture: %v", err)
|
||||
}
|
||||
return plugin
|
||||
}
|
||||
|
||||
func registerGeneratedRunForStatusTest(t *testing.T, svc *CoreService, instance domain.ServerInstance, pluginID string) domain.RunControlHelloResult {
|
||||
t.Helper()
|
||||
key, plainKey, err := svc.ensureActiveComponentKey(instance.ID, domain.DistributionComponentRun, "")
|
||||
if err != nil {
|
||||
t.Fatalf("get generated Run key: %v", err)
|
||||
}
|
||||
hello := validRunControlHello()
|
||||
hello.RunEndpointID = instance.RunEndpointID
|
||||
hello.RegistrationToken = plainKey
|
||||
hello.ServerInstanceID = instance.ID
|
||||
hello.PluginID = pluginID
|
||||
hello.ComponentKind = domain.DistributionComponentRun
|
||||
hello.KeyGeneration = key.Generation
|
||||
hello.Platform = "windows"
|
||||
hello.Architecture = "amd64"
|
||||
hello.CapabilityReport.Capabilities = []string{domain.LifecycleCapabilityInstall, domain.LifecycleCapabilityStart, domain.LifecycleCapabilityStop, domain.LifecycleCapabilityStatus}
|
||||
registered, err := svc.RegisterRunHello(hello)
|
||||
if err != nil || !registered.Accepted {
|
||||
t.Fatalf("register generated Run: result=%+v err=%v", registered, err)
|
||||
}
|
||||
return registered
|
||||
}
|
||||
|
||||
func registerDedicatedRunForTest(t *testing.T, svc *CoreService, instance domain.ServerInstance, pluginID string) {
|
||||
t.Helper()
|
||||
key, plainKey, err := svc.ensureActiveComponentKey(instance.ID, domain.DistributionComponentRun, "")
|
||||
|
||||
Reference in New Issue
Block a user