Add run lifecycle report projection
This commit is contained in:
@@ -142,6 +142,7 @@ func TestRunHTTPEnvelopeRequiresValidSignatureAndRejectsReplay(t *testing.T) {
|
||||
assertErrorResponse(t, staleClaim, http.StatusUnauthorized, errorCodeUnauthorized)
|
||||
|
||||
privateUpdateBodies := map[string]any{
|
||||
"/api/v1/run/lifecycle/report": dto.RunLifecycleReportRequest{RunEndpointID: "run-local", SessionToken: token, ServerInstanceID: "server-signed", Capability: domain.LifecycleCapabilityStart, State: domain.JobStateSucceeded, Progress: dto.JobProgressBody{Percent: 100}, ExecutionResult: dto.RunJobExecutionResultBody{Kind: "process", ProcessState: "running"}},
|
||||
"/api/v1/run/jobs/dependency-input": dto.DependencyExecutionInputRequest{RunEndpointID: "run-local", SessionToken: token, JobID: "job-signed", LeaseToken: "lease", Attempt: 1},
|
||||
"/api/v1/run/jobs/protected-request-input": dto.ProtectedRequestExecutionInputRequest{RunEndpointID: "run-local", SessionToken: token, JobID: "job-signed", LeaseToken: "lease", Attempt: 1, FencingToken: 1},
|
||||
"/api/v1/run/jobs/source-rcon-input": dto.SourceRCONExecutionInputRequest{RunEndpointID: "run-local", SessionToken: token, JobID: "job-signed", LeaseToken: "lease", Attempt: 1},
|
||||
|
||||
@@ -42,6 +42,28 @@ func TestRunControlAPIHelloHeartbeatWorkflow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunLifecycleReportAPIProjectsServerState(t *testing.T) {
|
||||
router := newTestRouter()
|
||||
adminSession := createAdminSession(t, router)
|
||||
postJSON[dto.GamePluginResponse](t, router, "/api/v1/game-plugins", validGamePluginRequest())
|
||||
helloRequest := validRunControlHelloRequest()
|
||||
helloRequest.CapabilityReport.Capabilities = append(helloRequest.CapabilityReport.Capabilities, domain.LifecycleCapabilityInstall, domain.LifecycleCapabilityStart, domain.LifecycleCapabilityStop, "logs.read")
|
||||
helloRequest.CapabilityReport.Fingerprint = "cap-lifecycle-report"
|
||||
hello := decodeBody[dto.RunControlHelloResponse](t, performRunControlHello(t, router, helloRequest))
|
||||
server := postJSONWithAuth[dto.ServerInstanceResponse](t, router, "/api/v1/server-instances", dto.ServerInstanceCreateRequest{ID: "server-lifecycle-report-api", PluginID: "server.scum", RunEndpointID: "run-local", Name: "Lifecycle Report API", State: domain.ServerInstanceStateReady}, adminSession)
|
||||
|
||||
recorder := performJSON(t, router, http.MethodPost, "/api/v1/run/lifecycle/report", dto.RunLifecycleReportRequest{RunEndpointID: "run-local", SessionToken: hello.SessionToken, ServerInstanceID: server.ID, Capability: domain.LifecycleCapabilityStart, State: domain.JobStateSucceeded, Progress: dto.JobProgressBody{Percent: 100, Message: "autonomous start complete"}, Message: "autonomous start complete", ExecutionResult: dto.RunJobExecutionResultBody{Kind: "process", ProcessState: "running", AuditSummary: "private supervised process identity"}})
|
||||
assertStatus(t, recorder, http.StatusOK)
|
||||
response := decodeBody[dto.RunLifecycleReportResponse](t, recorder)
|
||||
if !response.Accepted || response.ProjectedState != domain.ServerInstanceStateRunning {
|
||||
t.Fatalf("expected lifecycle report projection, got %+v", response)
|
||||
}
|
||||
updated := getJSONWithAuth[dto.ServerInstanceResponse](t, router, "/api/v1/server-instances/"+server.ID, adminSession)
|
||||
if updated.State != domain.ServerInstanceStateRunning {
|
||||
t.Fatalf("expected server state projected running, got %+v", updated)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunControlAPIReRegistrationRotatesToken(t *testing.T) {
|
||||
router := newTestRouter()
|
||||
first := decodeBody[dto.RunControlHelloResponse](t, performRunControlHello(t, router, validRunControlHelloRequest()))
|
||||
|
||||
@@ -120,6 +120,7 @@ func (h *coreHandlers) register(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/api/v1/server-instances/{id}", h.serverInstanceDetail)
|
||||
mux.HandleFunc("/api/v1/run/control/hello", h.runControlHello)
|
||||
mux.HandleFunc("/api/v1/run/control/heartbeat", h.requireRunSignature(h.runControlHeartbeat))
|
||||
mux.HandleFunc("/api/v1/run/lifecycle/report", h.requireRunSignature(h.runLifecycleReport))
|
||||
mux.HandleFunc("/api/v1/run/jobs/claim", h.requireRunSignature(h.runJobClaim))
|
||||
mux.HandleFunc("/api/v1/run/jobs/ack", h.requireRunSignature(h.runJobAck))
|
||||
mux.HandleFunc("/api/v1/run/jobs/progress", h.requireRunSignature(h.runJobProgress))
|
||||
@@ -1697,6 +1698,35 @@ func (h *coreHandlers) runControlHeartbeat(w http.ResponseWriter, r *http.Reques
|
||||
writeJSON(w, http.StatusOK, dto.RunControlHeartbeatFromDomain(result))
|
||||
}
|
||||
|
||||
// runLifecycleReport godoc
|
||||
// @Summary Report autonomous run lifecycle result
|
||||
// @Description Lets a registered run endpoint report an observed lifecycle terminal result without a platform-assigned job lease.
|
||||
// @Tags run
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body dto.RunLifecycleReportRequest true "Run lifecycle report request"
|
||||
// @Success 200 {object} dto.RunLifecycleReportResponse
|
||||
// @Failure 400 {object} dto.ErrorResponse
|
||||
// @Failure 405 {object} dto.ErrorResponse
|
||||
// @Router /api/v1/run/lifecycle/report [post]
|
||||
func (h *coreHandlers) runLifecycleReport(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
writeMethodNotAllowed(w, http.MethodPost)
|
||||
return
|
||||
}
|
||||
request, err := decodeJSON[dto.RunLifecycleReportRequest](r)
|
||||
if err != nil {
|
||||
writeDecodeError(w, err)
|
||||
return
|
||||
}
|
||||
result, err := h.core.ReportRunLifecycle(request.ToDomain())
|
||||
if err != nil {
|
||||
writeServiceError(w, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, dto.RunLifecycleReportFromDomain(result))
|
||||
}
|
||||
|
||||
// runJobClaim godoc
|
||||
// @Summary Claim one run job
|
||||
// @Description Lets a registered run endpoint claim one queued job assigned to it using the active session token.
|
||||
|
||||
Reference in New Issue
Block a user