Add run lifecycle report projection
This commit is contained in:
@@ -432,6 +432,38 @@ func TestCoreServiceGeneratedSCUMRunRegistrationDoesNotQueueGuidedStart(t *testi
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoreServiceRunLifecycleReportProjectsGeneratedRunFacts(t *testing.T) {
|
||||
svc := newTestCoreService()
|
||||
plugin := createGeneratedRunStatusPlugin(t, svc)
|
||||
instance := domain.ServerInstance{ID: "managed-autonomous-start", PluginID: plugin.ID, PluginVersion: plugin.Version, RunEndpointID: dedicatedRunEndpointID("managed-autonomous-start"), Name: "Managed Autonomous Start", State: domain.ServerInstanceStateDraft, ConfigVersion: 1, Deployment: domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided, ProfileKey: "run-local", ServerRoot: `D:\scum-autonomous`, Revision: 1}}
|
||||
if err := svc.store.ServerInstances().Create(instance); err != nil {
|
||||
t.Fatalf("create autonomous server: %v", err)
|
||||
}
|
||||
registered := registerGeneratedRunForStatusTest(t, svc, instance, plugin.ID)
|
||||
|
||||
reported, err := svc.ReportRunLifecycle(domain.RunLifecycleReport{RunEndpointID: instance.RunEndpointID, SessionToken: registered.SessionToken, ServerInstanceID: instance.ID, Capability: domain.LifecycleCapabilityStart, State: domain.JobStateSucceeded, Progress: domain.RunJobProgressReport{Percent: 100, Message: "autonomous start complete"}, Message: "autonomous start complete", ExecutionResult: domain.JobExecutionResult{Kind: "process", ProcessState: "running", AuditSummary: "private supervised process identity"}})
|
||||
if err != nil || !reported.Accepted || reported.ProjectedState != domain.ServerInstanceStateRunning {
|
||||
t.Fatalf("expected accepted lifecycle report projected running, result=%+v err=%v", reported, err)
|
||||
}
|
||||
stored, err := svc.GetServerInstance(instance.ID)
|
||||
if err != nil || stored.State != domain.ServerInstanceStateRunning {
|
||||
t.Fatalf("expected Run report to project server running, server=%+v err=%v", stored, err)
|
||||
}
|
||||
jobs, err := svc.store.Jobs().List(domain.JobFilter{ServerInstanceID: instance.ID})
|
||||
if err != nil || len(jobs) != 0 {
|
||||
t.Fatalf("autonomous lifecycle report must not create platform jobs, jobs=%+v err=%v", jobs, err)
|
||||
}
|
||||
|
||||
other := domain.ServerInstance{ID: "managed-autonomous-other", PluginID: plugin.ID, PluginVersion: plugin.Version, RunEndpointID: dedicatedRunEndpointID("managed-autonomous-other"), Name: "Managed Autonomous Other", State: domain.ServerInstanceStateDraft, ConfigVersion: 1}
|
||||
if err := svc.store.ServerInstances().Create(other); err != nil {
|
||||
t.Fatalf("create other server: %v", err)
|
||||
}
|
||||
_, err = svc.ReportRunLifecycle(domain.RunLifecycleReport{RunEndpointID: instance.RunEndpointID, SessionToken: registered.SessionToken, ServerInstanceID: other.ID, Capability: domain.LifecycleCapabilityStart, State: domain.JobStateSucceeded, Progress: domain.RunJobProgressReport{Percent: 100}, ExecutionResult: domain.JobExecutionResult{Kind: "process", ProcessState: "running"}})
|
||||
if err == nil || !strings.Contains(err.Error(), "runEndpointId must match server instance") {
|
||||
t.Fatalf("expected report for another server binding to be rejected, err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoreServiceGeneratedRunRegistrationDoesNotDispatchStatusReconciliation(t *testing.T) {
|
||||
svc := newTestCoreService()
|
||||
plugin := createGeneratedRunStatusPlugin(t, svc)
|
||||
|
||||
@@ -139,6 +139,7 @@ type Core interface {
|
||||
AckRunJob(domain.RunJobAck) (domain.RunJobAckResult, error)
|
||||
UpdateRunJobProgress(domain.RunJobProgress) (domain.RunJobProgressResult, error)
|
||||
CompleteRunJob(domain.RunJobResult) (domain.RunJobResultResult, error)
|
||||
ReportRunLifecycle(domain.RunLifecycleReport) (domain.RunLifecycleReportResult, error)
|
||||
GetDistributionBuildInput(domain.DistributionBuildInputRequest) (domain.DistributionBuildInput, error)
|
||||
GetDependencyExecutionInput(domain.DependencyExecutionInputRequest) (domain.DependencyExecutionInput, error)
|
||||
DispatchSourceRCONCommandForSession(string, domain.SourceRCONCommandRequest) (domain.SourceRCONCommandDispatch, error)
|
||||
|
||||
@@ -2,13 +2,65 @@ package service
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
"browser.local/platform/validator"
|
||||
)
|
||||
|
||||
func (svc *CoreService) ReportRunLifecycle(report domain.RunLifecycleReport) (domain.RunLifecycleReportResult, error) {
|
||||
report = domain.CopyRunLifecycleReport(report)
|
||||
if err := validator.ValidateRunLifecycleReport(report); err != nil {
|
||||
return domain.RunLifecycleReportResult{}, err
|
||||
}
|
||||
if _, err := svc.validatedRunSession(report.RunEndpointID, report.SessionToken); err != nil {
|
||||
return domain.RunLifecycleReportResult{}, err
|
||||
}
|
||||
instance, err := svc.store.ServerInstances().Get(report.ServerInstanceID)
|
||||
if err != nil {
|
||||
return domain.RunLifecycleReportResult{}, err
|
||||
}
|
||||
if instance.RunEndpointID != report.RunEndpointID {
|
||||
return domain.RunLifecycleReportResult{}, validationError("runEndpointId must match server instance")
|
||||
}
|
||||
if instance.State == domain.ServerInstanceStateDeleted {
|
||||
return domain.RunLifecycleReportResult{}, validationError("server instance must not be deleted")
|
||||
}
|
||||
|
||||
stamp := svc.now()
|
||||
nextState, projected := lifecycleProjectedState(report.Capability, report.State, report.ExecutionResult)
|
||||
if projected {
|
||||
instance.State = nextState
|
||||
instance.UpdatedAt = stamp
|
||||
if err := validator.ValidateServerInstance(instance); err != nil {
|
||||
return domain.RunLifecycleReportResult{}, err
|
||||
}
|
||||
if err := svc.store.ServerInstances().Update(instance); err != nil {
|
||||
return domain.RunLifecycleReportResult{}, err
|
||||
}
|
||||
}
|
||||
auditResult := domain.AuditResultSuccess
|
||||
if report.State == domain.JobStateFailed || report.State == domain.JobStateCancelled {
|
||||
auditResult = domain.AuditResultFailed
|
||||
}
|
||||
if err := svc.recordAuditEvent("run:"+report.RunEndpointID, "lifecycle.report", "server-instance", instance.ID, auditResult, lifecycleReportSummary(report, nextState, projected)); err != nil {
|
||||
return domain.RunLifecycleReportResult{}, err
|
||||
}
|
||||
return domain.CopyRunLifecycleReportResult(domain.RunLifecycleReportResult{Accepted: true, RunEndpointID: report.RunEndpointID, ServerInstanceID: report.ServerInstanceID, ProjectedState: nextState, ServerTime: stamp}), nil
|
||||
}
|
||||
|
||||
func lifecycleReportSummary(report domain.RunLifecycleReport, projectedState domain.ServerInstanceState, projected bool) string {
|
||||
for _, candidate := range []string{report.ExecutionResult.AuditSummary, report.Progress.Message, report.Message, report.ErrorCode} {
|
||||
if strings.TrimSpace(candidate) != "" {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
if projected {
|
||||
return "run reported " + report.Capability + " " + string(report.State) + "; projected server state " + string(projectedState)
|
||||
}
|
||||
return "run reported " + report.Capability + " " + string(report.State)
|
||||
}
|
||||
|
||||
func (svc *CoreService) projectRemoteAdapterJobResult(job domain.Job, stamp time.Time) error {
|
||||
if !strings.HasPrefix(job.Capability, "remote.") || job.ServerInstanceID == "" || !isTerminalJobState(job.State) {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user