Fix guided lifecycle job scoping
This commit is contained in:
@@ -302,6 +302,10 @@ type JobExecutionInput struct {
|
|||||||
DLLExtensions []domain.RuntimeDLLExtensionPlan `json:"dllExtensions,omitempty" db:"dll_extensions"`
|
DLLExtensions []domain.RuntimeDLLExtensionPlan `json:"dllExtensions,omitempty" db:"dll_extensions"`
|
||||||
// SourceRCON is secret-free connection metadata for a one-time Run command.
|
// SourceRCON is secret-free connection metadata for a one-time Run command.
|
||||||
SourceRCON *domain.RuntimeSourceRCONPlan `json:"sourceRcon,omitempty" db:"source_rcon"`
|
SourceRCON *domain.RuntimeSourceRCONPlan `json:"sourceRcon,omitempty" db:"source_rcon"`
|
||||||
|
// Deployment is protected lifecycle execution material delivered only to Run.
|
||||||
|
Deployment *domain.ServerDeploymentDefinition `json:"deployment,omitempty" db:"deployment"`
|
||||||
|
// ServerDeploymentPlan is the legacy generic deployment-plan payload.
|
||||||
|
ServerDeploymentPlan *domain.ServerDeploymentPlan `json:"serverDeploymentPlan,omitempty" db:"server_deployment_plan"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JobExecutionResult struct {
|
type JobExecutionResult struct {
|
||||||
@@ -895,11 +899,21 @@ func (job Job) ToDomain() domain.Job {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func executionInputFromDomain(input domain.JobExecutionInput) JobExecutionInput {
|
func executionInputFromDomain(input domain.JobExecutionInput) JobExecutionInput {
|
||||||
return JobExecutionInput{WorkspaceScope: input.WorkspaceScope, Content: input.Content, ExpectedVersion: input.ExpectedVersion, ExpectedChecksum: input.ExpectedChecksum, MaxReadBytes: input.MaxReadBytes, RemoteAdapterKey: input.RemoteAdapterKey, RemoteAdapterKind: input.RemoteAdapterKind, TimeoutSeconds: input.TimeoutSeconds, PluginID: input.PluginID, LifecycleOperation: input.LifecycleOperation, TargetVersion: input.TargetVersion, Inputs: domain.CopyStringMap(input.Inputs), DLLExtensions: append([]domain.RuntimeDLLExtensionPlan(nil), input.DLLExtensions...), SourceRCON: domain.CopyRuntimeSourceRCONPlan(input.SourceRCON)}
|
var deployment *domain.ServerDeploymentDefinition
|
||||||
|
if input.Deployment != nil {
|
||||||
|
copy := domain.CopyServerDeploymentDefinition(*input.Deployment)
|
||||||
|
deployment = ©
|
||||||
|
}
|
||||||
|
return JobExecutionInput{WorkspaceScope: input.WorkspaceScope, Content: input.Content, ExpectedVersion: input.ExpectedVersion, ExpectedChecksum: input.ExpectedChecksum, MaxReadBytes: input.MaxReadBytes, RemoteAdapterKey: input.RemoteAdapterKey, RemoteAdapterKind: input.RemoteAdapterKind, TimeoutSeconds: input.TimeoutSeconds, PluginID: input.PluginID, LifecycleOperation: input.LifecycleOperation, TargetVersion: input.TargetVersion, Inputs: domain.CopyStringMap(input.Inputs), DLLExtensions: append([]domain.RuntimeDLLExtensionPlan(nil), input.DLLExtensions...), SourceRCON: domain.CopyRuntimeSourceRCONPlan(input.SourceRCON), Deployment: deployment, ServerDeploymentPlan: domain.CopyServerDeploymentPlan(input.ServerDeploymentPlan)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (input JobExecutionInput) ToDomain() domain.JobExecutionInput {
|
func (input JobExecutionInput) ToDomain() domain.JobExecutionInput {
|
||||||
return domain.JobExecutionInput{WorkspaceScope: input.WorkspaceScope, Content: input.Content, ExpectedVersion: input.ExpectedVersion, ExpectedChecksum: input.ExpectedChecksum, MaxReadBytes: input.MaxReadBytes, RemoteAdapterKey: input.RemoteAdapterKey, RemoteAdapterKind: input.RemoteAdapterKind, TimeoutSeconds: input.TimeoutSeconds, PluginID: input.PluginID, LifecycleOperation: input.LifecycleOperation, TargetVersion: input.TargetVersion, Inputs: domain.CopyStringMap(input.Inputs), DLLExtensions: append([]domain.RuntimeDLLExtensionPlan(nil), input.DLLExtensions...), SourceRCON: domain.CopyRuntimeSourceRCONPlan(input.SourceRCON)}
|
var deployment *domain.ServerDeploymentDefinition
|
||||||
|
if input.Deployment != nil {
|
||||||
|
copy := domain.CopyServerDeploymentDefinition(*input.Deployment)
|
||||||
|
deployment = ©
|
||||||
|
}
|
||||||
|
return domain.JobExecutionInput{WorkspaceScope: input.WorkspaceScope, Content: input.Content, ExpectedVersion: input.ExpectedVersion, ExpectedChecksum: input.ExpectedChecksum, MaxReadBytes: input.MaxReadBytes, RemoteAdapterKey: input.RemoteAdapterKey, RemoteAdapterKind: input.RemoteAdapterKind, TimeoutSeconds: input.TimeoutSeconds, PluginID: input.PluginID, LifecycleOperation: input.LifecycleOperation, TargetVersion: input.TargetVersion, Inputs: domain.CopyStringMap(input.Inputs), DLLExtensions: append([]domain.RuntimeDLLExtensionPlan(nil), input.DLLExtensions...), SourceRCON: domain.CopyRuntimeSourceRCONPlan(input.SourceRCON), Deployment: deployment, ServerDeploymentPlan: domain.CopyServerDeploymentPlan(input.ServerDeploymentPlan)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func executionResultFromDomain(result domain.JobExecutionResult) JobExecutionResult {
|
func executionResultFromDomain(result domain.JobExecutionResult) JobExecutionResult {
|
||||||
|
|||||||
@@ -101,6 +101,24 @@ func TestJobExecutionInputModelRoundTripPreservesLifecycleMetadata(t *testing.T)
|
|||||||
LifecycleOperation: "upgrade",
|
LifecycleOperation: "upgrade",
|
||||||
TargetVersion: "2.0.0",
|
TargetVersion: "2.0.0",
|
||||||
Inputs: map[string]string{"templateKey": "players.by-id", "playerId": "steam-123"},
|
Inputs: map[string]string{"templateKey": "players.by-id", "playerId": "steam-123"},
|
||||||
|
Deployment: &domain.ServerDeploymentDefinition{
|
||||||
|
Mode: domain.ServerDeploymentModeGuided,
|
||||||
|
ProfileKey: "run-local",
|
||||||
|
RuntimeBindings: map[string]string{"installRoot": "D:\\scumserver"},
|
||||||
|
CreateInputs: map[string]string{"gamePort": "27000", "maxPlayers": "128"},
|
||||||
|
ServerRoot: "D:\\scumserver",
|
||||||
|
WorkingDirectory: "D:\\scumserver",
|
||||||
|
InstallCommand: "install.cmd",
|
||||||
|
StartCommand: "start.cmd",
|
||||||
|
Shell: domain.ServerCommandShellCmd,
|
||||||
|
Revision: 3,
|
||||||
|
},
|
||||||
|
ServerDeploymentPlan: &domain.ServerDeploymentPlan{
|
||||||
|
SchemaVersion: "1",
|
||||||
|
Operation: "install",
|
||||||
|
PluginID: "game.scum",
|
||||||
|
Prerequisites: []domain.RuntimeServerPrerequisite{{Key: "steamcmd", Kind: "tool"}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
row := executionInputFromDomain(source)
|
row := executionInputFromDomain(source)
|
||||||
@@ -108,20 +126,31 @@ func TestJobExecutionInputModelRoundTripPreservesLifecycleMetadata(t *testing.T)
|
|||||||
if row.Inputs["playerId"] != "steam-123" {
|
if row.Inputs["playerId"] != "steam-123" {
|
||||||
t.Fatalf("expected model execution inputs to be isolated from source mutation, row=%+v", row.Inputs)
|
t.Fatalf("expected model execution inputs to be isolated from source mutation, row=%+v", row.Inputs)
|
||||||
}
|
}
|
||||||
|
source.Deployment.CreateInputs["maxPlayers"] = "64"
|
||||||
|
if row.Deployment == nil || row.Deployment.CreateInputs["maxPlayers"] != "128" {
|
||||||
|
t.Fatalf("expected model deployment inputs to be isolated from source mutation, row=%+v", row.Deployment)
|
||||||
|
}
|
||||||
source.Inputs["playerId"] = "steam-123"
|
source.Inputs["playerId"] = "steam-123"
|
||||||
|
source.Deployment.CreateInputs["maxPlayers"] = "128"
|
||||||
row.Inputs["playerId"] = "row-mutated"
|
row.Inputs["playerId"] = "row-mutated"
|
||||||
if source.Inputs["playerId"] != "steam-123" {
|
if source.Inputs["playerId"] != "steam-123" {
|
||||||
t.Fatalf("expected source execution inputs to be isolated from model mutation, source=%+v", source.Inputs)
|
t.Fatalf("expected source execution inputs to be isolated from model mutation, source=%+v", source.Inputs)
|
||||||
}
|
}
|
||||||
|
row.Deployment.CreateInputs["maxPlayers"] = "32"
|
||||||
|
if source.Deployment.CreateInputs["maxPlayers"] != "128" {
|
||||||
|
t.Fatalf("expected source deployment inputs to be isolated from model mutation, source=%+v", source.Deployment)
|
||||||
|
}
|
||||||
row.Inputs["playerId"] = "steam-123"
|
row.Inputs["playerId"] = "steam-123"
|
||||||
|
row.Deployment.CreateInputs["maxPlayers"] = "128"
|
||||||
|
|
||||||
roundTrip := row.ToDomain()
|
roundTrip := row.ToDomain()
|
||||||
if !reflect.DeepEqual(roundTrip, source) {
|
if !reflect.DeepEqual(roundTrip, source) {
|
||||||
t.Fatalf("expected lifecycle execution metadata to round-trip, got %+v", roundTrip)
|
t.Fatalf("expected lifecycle execution metadata to round-trip, got %+v", roundTrip)
|
||||||
}
|
}
|
||||||
roundTrip.Inputs["playerId"] = "mutated"
|
roundTrip.Inputs["playerId"] = "mutated"
|
||||||
if source.Inputs["playerId"] != "steam-123" || row.Inputs["playerId"] != "steam-123" {
|
roundTrip.Deployment.CreateInputs["maxPlayers"] = "16"
|
||||||
t.Fatalf("expected execution inputs to round-trip without aliasing, source=%+v row=%+v", source.Inputs, row.Inputs)
|
if source.Inputs["playerId"] != "steam-123" || row.Inputs["playerId"] != "steam-123" || source.Deployment.CreateInputs["maxPlayers"] != "128" || row.Deployment.CreateInputs["maxPlayers"] != "128" {
|
||||||
|
t.Fatalf("expected execution inputs to round-trip without aliasing, source=%+v row=%+v", source, row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,22 @@ func TestFileStorePersistsAndReloadsResources(t *testing.T) {
|
|||||||
LastReconciledAt: stamp,
|
LastReconciledAt: stamp,
|
||||||
ReconcileCount: 2,
|
ReconcileCount: 2,
|
||||||
ReconcileOutcome: "confirmed active attempt",
|
ReconcileOutcome: "confirmed active attempt",
|
||||||
ExecutionInput: domain.JobExecutionInput{WorkspaceScope: "local", Content: "name=approved\n", ExpectedVersion: 1, ExpectedChecksum: "sha256:" + strings.Repeat("1", 64), MaxReadBytes: 64 * 1024, Inputs: map[string]string{"templateKey": "players.by-id", "playerId": "steam-123"}},
|
ExecutionInput: domain.JobExecutionInput{
|
||||||
|
WorkspaceScope: "local",
|
||||||
|
Content: "name=approved\n",
|
||||||
|
ExpectedVersion: 1,
|
||||||
|
ExpectedChecksum: "sha256:" + strings.Repeat("1", 64),
|
||||||
|
MaxReadBytes: 64 * 1024,
|
||||||
|
Inputs: map[string]string{"templateKey": "players.by-id", "playerId": "steam-123"},
|
||||||
|
Deployment: &domain.ServerDeploymentDefinition{
|
||||||
|
Mode: domain.ServerDeploymentModeGuided,
|
||||||
|
ProfileKey: "local",
|
||||||
|
CreateInputs: map[string]string{"gamePort": "27000", "maxPlayers": "128"},
|
||||||
|
ServerRoot: "D:\\scumserver",
|
||||||
|
Revision: 2,
|
||||||
|
},
|
||||||
|
ServerDeploymentPlan: &domain.ServerDeploymentPlan{SchemaVersion: "1", Operation: "install", PluginID: "game.runtime", Prerequisites: []domain.RuntimeServerPrerequisite{{Key: "steamcmd", Kind: "tool"}}},
|
||||||
|
},
|
||||||
ExecutionResult: domain.JobExecutionResult{Kind: "file.read", Version: 2, Checksum: "sha256:" + strings.Repeat("2", 64), SizeBytes: 15, AuditSummary: "bounded read", Content: "private-read"},
|
ExecutionResult: domain.JobExecutionResult{Kind: "file.read", Version: 2, Checksum: "sha256:" + strings.Repeat("2", 64), SizeBytes: 15, AuditSummary: "bounded read", Content: "private-read"},
|
||||||
}
|
}
|
||||||
if err := store.Jobs().Create(job); err != nil {
|
if err := store.Jobs().Create(job); err != nil {
|
||||||
@@ -194,7 +209,7 @@ func TestFileStorePersistsAndReloadsResources(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("get reloaded job by idempotency: %v", err)
|
t.Fatalf("get reloaded job by idempotency: %v", err)
|
||||||
}
|
}
|
||||||
if gotJob.ID != "job-1" || gotJob.ServerInstanceID != "server-1" || gotJob.Attempt != 2 || gotJob.RetryPolicy.MaxAttempts != 4 || gotJob.LeaseTokenHash != strings.Repeat("d", 64) || gotJob.ReconcileCount != 2 || gotJob.ExecutionInput.Content != "name=approved\n" || gotJob.ExecutionInput.Inputs["templateKey"] != "players.by-id" || gotJob.ExecutionInput.Inputs["playerId"] != "steam-123" || gotJob.ExecutionResult.Content != "private-read" {
|
if gotJob.ID != "job-1" || gotJob.ServerInstanceID != "server-1" || gotJob.Attempt != 2 || gotJob.RetryPolicy.MaxAttempts != 4 || gotJob.LeaseTokenHash != strings.Repeat("d", 64) || gotJob.ReconcileCount != 2 || gotJob.ExecutionInput.Content != "name=approved\n" || gotJob.ExecutionInput.Inputs["templateKey"] != "players.by-id" || gotJob.ExecutionInput.Inputs["playerId"] != "steam-123" || gotJob.ExecutionInput.Deployment == nil || gotJob.ExecutionInput.Deployment.CreateInputs["gamePort"] != "27000" || gotJob.ExecutionInput.ServerDeploymentPlan == nil || gotJob.ExecutionInput.ServerDeploymentPlan.Prerequisites[0].Key != "steamcmd" || gotJob.ExecutionResult.Content != "private-read" {
|
||||||
t.Fatalf("unexpected reloaded job: %+v", gotJob)
|
t.Fatalf("unexpected reloaded job: %+v", gotJob)
|
||||||
}
|
}
|
||||||
gotJob.ExecutionInput.Inputs["playerId"] = "mutated"
|
gotJob.ExecutionInput.Inputs["playerId"] = "mutated"
|
||||||
@@ -248,7 +263,20 @@ func TestMySQLSnapshotRoundTripsDurableJobSchedulingMetadata(t *testing.T) {
|
|||||||
Attempt: 2, QueueEligibleAt: stamp, LeaseTokenHash: strings.Repeat("e", 64), LeaseSessionGen: 4,
|
Attempt: 2, QueueEligibleAt: stamp, LeaseTokenHash: strings.Repeat("e", 64), LeaseSessionGen: 4,
|
||||||
LeaseExpiresAt: stamp.Add(time.Minute), LastProgressSeq: 8, CancelReason: "stop", CancelRequestedAt: stamp,
|
LeaseExpiresAt: stamp.Add(time.Minute), LastProgressSeq: 8, CancelReason: "stop", CancelRequestedAt: stamp,
|
||||||
LastReconciledAt: stamp, ReconcileCount: 3, ReconcileOutcome: "confirmed active attempt", CreatedAt: stamp, UpdatedAt: stamp,
|
LastReconciledAt: stamp, ReconcileCount: 3, ReconcileOutcome: "confirmed active attempt", CreatedAt: stamp, UpdatedAt: stamp,
|
||||||
ExecutionInput: domain.JobExecutionInput{WorkspaceScope: "local", Content: "mysql-approved", ExpectedVersion: 1, ExpectedChecksum: "sha256:" + strings.Repeat("3", 64), MaxReadBytes: 64 * 1024, Inputs: map[string]string{"templateKey": "players.by-id", "playerId": "steam-456"}},
|
ExecutionInput: domain.JobExecutionInput{
|
||||||
|
WorkspaceScope: "local",
|
||||||
|
Content: "mysql-approved",
|
||||||
|
ExpectedVersion: 1,
|
||||||
|
ExpectedChecksum: "sha256:" + strings.Repeat("3", 64),
|
||||||
|
MaxReadBytes: 64 * 1024,
|
||||||
|
Inputs: map[string]string{"templateKey": "players.by-id", "playerId": "steam-456"},
|
||||||
|
Deployment: &domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided, ProfileKey: "local", CreateInputs: map[string]string{"gamePort": "27000"}, ServerRoot: "D:\\scumserver", Revision: 3},
|
||||||
|
ServerDeploymentPlan: &domain.ServerDeploymentPlan{
|
||||||
|
SchemaVersion: "1",
|
||||||
|
Operation: "install",
|
||||||
|
PluginID: "game.runtime",
|
||||||
|
},
|
||||||
|
},
|
||||||
ExecutionResult: domain.JobExecutionResult{Kind: "file.write", Version: 2, Checksum: "sha256:" + strings.Repeat("4", 64), SizeBytes: 14, AuditSummary: "atomic write"},
|
ExecutionResult: domain.JobExecutionResult{Kind: "file.write", Version: 2, Checksum: "sha256:" + strings.Repeat("4", 64), SizeBytes: 14, AuditSummary: "atomic write"},
|
||||||
}
|
}
|
||||||
if err := source.MemoryStore.Jobs().Create(job); err != nil {
|
if err := source.MemoryStore.Jobs().Create(job); err != nil {
|
||||||
@@ -265,7 +293,7 @@ func TestMySQLSnapshotRoundTripsDurableJobSchedulingMetadata(t *testing.T) {
|
|||||||
target := &MySQLStore{MemoryStore: NewMemoryStore()}
|
target := &MySQLStore{MemoryStore: NewMemoryStore()}
|
||||||
target.loadSnapshot(snapshot)
|
target.loadSnapshot(snapshot)
|
||||||
got, err := target.MemoryStore.Jobs().Get(job.ID)
|
got, err := target.MemoryStore.Jobs().Get(job.ID)
|
||||||
if err != nil || got.Attempt != job.Attempt || got.LeaseTokenHash != job.LeaseTokenHash || got.LastProgressSeq != job.LastProgressSeq || got.ReconcileCount != job.ReconcileCount || got.ExecutionInput.Content != job.ExecutionInput.Content || got.ExecutionInput.Inputs["templateKey"] != "players.by-id" || got.ExecutionInput.Inputs["playerId"] != "steam-456" || got.ExecutionResult.Checksum != job.ExecutionResult.Checksum {
|
if err != nil || got.Attempt != job.Attempt || got.LeaseTokenHash != job.LeaseTokenHash || got.LastProgressSeq != job.LastProgressSeq || got.ReconcileCount != job.ReconcileCount || got.ExecutionInput.Content != job.ExecutionInput.Content || got.ExecutionInput.Inputs["templateKey"] != "players.by-id" || got.ExecutionInput.Inputs["playerId"] != "steam-456" || got.ExecutionInput.Deployment == nil || got.ExecutionInput.Deployment.CreateInputs["gamePort"] != "27000" || got.ExecutionInput.ServerDeploymentPlan == nil || got.ExecutionInput.ServerDeploymentPlan.PluginID != "game.runtime" || got.ExecutionResult.Checksum != job.ExecutionResult.Checksum {
|
||||||
t.Fatalf("unexpected MySQL snapshot job: job=%+v err=%v", got, err)
|
t.Fatalf("unexpected MySQL snapshot job: job=%+v err=%v", got, err)
|
||||||
}
|
}
|
||||||
got.ExecutionInput.Inputs["playerId"] = "mutated"
|
got.ExecutionInput.Inputs["playerId"] = "mutated"
|
||||||
|
|||||||
@@ -322,8 +322,8 @@ func TestCoreServiceDedicatedRunRegistrationAutomaticallyDeploysGuidedDraftOnly(
|
|||||||
t.Fatalf("generated Run registration should queue install without deployment target, server=%+v err=%v", storedGenerated, err)
|
t.Fatalf("generated Run registration should queue install without deployment target, server=%+v err=%v", storedGenerated, err)
|
||||||
}
|
}
|
||||||
jobs, err = svc.store.Jobs().List(domain.JobFilter{ServerInstanceID: generatedRunDraft.ID})
|
jobs, err = svc.store.Jobs().List(domain.JobFilter{ServerInstanceID: generatedRunDraft.ID})
|
||||||
if err != nil || len(jobs) != 1 || jobs[0].Capability != domain.LifecycleCapabilityInstall {
|
if err != nil || len(jobs) != 1 || jobs[0].Capability != domain.LifecycleCapabilityInstall || jobs[0].ExecutionInput.WorkspaceScope != "local" {
|
||||||
t.Fatalf("expected one automatic generated Run install job, jobs=%+v err=%v", jobs, err)
|
t.Fatalf("expected one scoped automatic generated Run install job, jobs=%+v err=%v", jobs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
existing, err := svc.CreateServerInstanceWorkflowForSession(owner, domain.ServerLifecycleCreate{ID: "managed-existing", PluginID: plugin.ID, DeploymentTargetID: "run-local", Name: "Managed Existing", IdempotencyKey: "managed-existing-create", ProfileKey: "local", Deployment: domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeExisting, ServerRoot: "C:\\existing-scum"}})
|
existing, err := svc.CreateServerInstanceWorkflowForSession(owner, domain.ServerLifecycleCreate{ID: "managed-existing", PluginID: plugin.ID, DeploymentTargetID: "run-local", Name: "Managed Existing", IdempotencyKey: "managed-existing-create", ProfileKey: "local", Deployment: domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeExisting, ServerRoot: "C:\\existing-scum"}})
|
||||||
@@ -431,15 +431,15 @@ func TestCoreServiceGeneratedSCUMRunRegistrationQueuesGuidedInstall(t *testing.T
|
|||||||
t.Fatalf("expected one SCUM install job, jobs=%+v err=%v", jobs, err)
|
t.Fatalf("expected one SCUM install job, jobs=%+v err=%v", jobs, err)
|
||||||
}
|
}
|
||||||
job := jobs[0]
|
job := jobs[0]
|
||||||
if job.Capability != domain.LifecycleCapabilityInstall || job.TargetKey != "actions/install.json" || job.ExecutionInput.Deployment == nil || job.ExecutionInput.ServerDeploymentPlan != nil {
|
if job.Capability != domain.LifecycleCapabilityInstall || job.TargetKey != "actions/install.json" || job.ExecutionInput.WorkspaceScope != "run-local" || job.ExecutionInput.Deployment == nil || job.ExecutionInput.ServerDeploymentPlan != nil {
|
||||||
t.Fatalf("expected SCUM install job with plugin action and generic deployment inputs, job=%+v", job)
|
t.Fatalf("expected SCUM install job with scoped plugin action and generic deployment inputs, job=%+v", job)
|
||||||
}
|
}
|
||||||
if job.ExecutionInput.Deployment.CreateInputs["gamePort"] != "27000" || job.ExecutionInput.Deployment.CreateInputs["maxPlayers"] != "128" {
|
if job.ExecutionInput.Deployment.CreateInputs["gamePort"] != "27000" || job.ExecutionInput.Deployment.CreateInputs["maxPlayers"] != "128" {
|
||||||
t.Fatalf("SCUM install job lost create inputs: %+v", job.ExecutionInput.Deployment.CreateInputs)
|
t.Fatalf("SCUM install job lost create inputs: %+v", job.ExecutionInput.Deployment.CreateInputs)
|
||||||
}
|
}
|
||||||
claim, err := svc.ClaimRunJob(domain.RunJobClaim{RunEndpointID: instance.RunEndpointID, SessionToken: registered.SessionToken, Capabilities: hello.CapabilityReport.Capabilities, Capacity: domain.RunCapacity{MaxJobs: 1}})
|
claim, err := svc.ClaimRunJob(domain.RunJobClaim{RunEndpointID: instance.RunEndpointID, SessionToken: registered.SessionToken, Capabilities: hello.CapabilityReport.Capabilities, Capacity: domain.RunCapacity{MaxJobs: 1}})
|
||||||
if err != nil || !claim.HasJob || claim.Job.TargetKey != "actions/install.json" || claim.Job.ExecutionInput.ServerDeploymentPlan != nil {
|
if err != nil || !claim.HasJob || claim.Job.TargetKey != "actions/install.json" || claim.Job.ExecutionInput.WorkspaceScope != "run-local" || claim.Job.ExecutionInput.ServerDeploymentPlan != nil {
|
||||||
t.Fatalf("generated SCUM Run should claim plugin-owned install action, claim=%+v err=%v", claim, err)
|
t.Fatalf("generated SCUM Run should claim scoped plugin-owned install action, claim=%+v err=%v", claim, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func TestRuntimeBindingValidationAndLifecycleGating(t *testing.T) {
|
|||||||
t.Fatalf("unexpected complete binding: view=%+v err=%v", view, err)
|
t.Fatalf("unexpected complete binding: view=%+v err=%v", view, err)
|
||||||
}
|
}
|
||||||
result, err := svc.StartServerInstanceForSession(ownerSession, domain.ServerLifecycleCommand{ServerInstanceID: instance.ID, ExpectedConfigVersion: instance.ConfigVersion, IdempotencyKey: "start-complete-binding"})
|
result, err := svc.StartServerInstanceForSession(ownerSession, domain.ServerLifecycleCommand{ServerInstanceID: instance.ID, ExpectedConfigVersion: instance.ConfigVersion, IdempotencyKey: "start-complete-binding"})
|
||||||
if err != nil || result.Job.TargetKey != "local" {
|
if err != nil || result.Job.TargetKey != "actions/start.json" || result.Job.ExecutionInput.WorkspaceScope != "local" {
|
||||||
t.Fatalf("expected complete binding to permit start, result=%+v err=%v", result, err)
|
t.Fatalf("expected complete binding to permit start, result=%+v err=%v", result, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,19 +291,20 @@ func (svc *CoreService) dispatchLifecycleJob(instance domain.ServerInstance, act
|
|||||||
return domain.Job{}, err
|
return domain.Job{}, err
|
||||||
}
|
}
|
||||||
profileKey := ""
|
profileKey := ""
|
||||||
if err == nil {
|
if err == nil && strings.TrimSpace(binding.ProfileKey) != "" {
|
||||||
profileKey = binding.ProfileKey
|
profileKey = binding.ProfileKey
|
||||||
} else {
|
} else {
|
||||||
profileKey = instance.Deployment.ProfileKey
|
profileKey = instance.Deployment.ProfileKey
|
||||||
}
|
}
|
||||||
actionRef := profileKey
|
actionRef := ""
|
||||||
profile, hasProfile := runtimeLifecycleProfileForKey(plugin.RuntimeProfiles, profileKey)
|
profile, hasProfile := runtimeLifecycleProfileForKey(plugin.RuntimeProfiles, profileKey)
|
||||||
if hasProfile {
|
if hasProfile {
|
||||||
if ref := runtimeProfileActionRef(profile.ActionRefs, action); ref != "" {
|
if ref := runtimeProfileActionRef(profile.ActionRefs, action); ref != "" {
|
||||||
actionRef = ref
|
actionRef = ref
|
||||||
}
|
}
|
||||||
} else if ref := lifecycleActionRef(plugin, action); ref != "" {
|
}
|
||||||
actionRef = ref
|
if strings.TrimSpace(actionRef) == "" {
|
||||||
|
actionRef = lifecycleActionRef(plugin, action)
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(actionRef) == "" {
|
if strings.TrimSpace(actionRef) == "" {
|
||||||
return domain.Job{}, validationError(fmt.Sprintf("plugin %s lifecycle action is required", action))
|
return domain.Job{}, validationError(fmt.Sprintf("plugin %s lifecycle action is required", action))
|
||||||
|
|||||||
Reference in New Issue
Block a user