Remove legacy client-manager platform path
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"browser.local/platform/dto"
|
||||
"browser.local/platform/repo"
|
||||
"browser.local/platform/service"
|
||||
"browser.local/platform/validator"
|
||||
)
|
||||
|
||||
func TestAuthorizedRouterEnforcesAdminAndCrossOwnerBoundaries(t *testing.T) {
|
||||
@@ -165,6 +166,71 @@ func TestRunHTTPEnvelopeRequiresValidSignatureAndRejectsReplay(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignedRunArtifactChunkUploadUsesHeaderEnvelope(t *testing.T) {
|
||||
store := repo.NewMemoryStore()
|
||||
core := service.NewCoreService(store)
|
||||
if _, err := core.CreateGamePlugin(validGamePluginRequest().ToDomain()); err != nil {
|
||||
t.Fatalf("create plugin: %v", err)
|
||||
}
|
||||
if _, err := core.CreateRunEndpoint(validRunEndpointRequest().ToDomain()); err != nil {
|
||||
t.Fatalf("create endpoint: %v", err)
|
||||
}
|
||||
if _, err := core.CreateServerInstance(domain.ServerInstance{ID: "server-signed-artifact", PluginID: "server.scum", RunEndpointID: "run-local", Name: "Signed Artifact", State: domain.ServerInstanceStateReady, ConfigVersion: 1}); err != nil {
|
||||
t.Fatalf("create server: %v", err)
|
||||
}
|
||||
if _, err := core.CreateJob(domain.Job{ID: "job-signed-artifact", ServerInstanceID: "server-signed-artifact", RunEndpointID: "run-local", Capability: "process.start", IdempotencyKey: "job-signed-artifact"}); err != nil {
|
||||
t.Fatalf("create job: %v", err)
|
||||
}
|
||||
token := "run-artifact-session-secret"
|
||||
stamp := time.Now().UTC()
|
||||
hash := sha256.Sum256([]byte(token))
|
||||
if err := store.RunControlSessions().Create(domain.RunControlSession{RunEndpointID: "run-local", SessionTokenHash: hex.EncodeToString(hash[:]), Status: domain.AuthSessionStatusActive, Generation: 1, CapabilityFingerprint: "cap-v1", HeartbeatIntervalSeconds: 15, CreatedAt: stamp, UpdatedAt: stamp, ExpiresAt: stamp.Add(time.Hour), RequireSignedRequests: true}); err != nil {
|
||||
t.Fatalf("create Run session: %v", err)
|
||||
}
|
||||
payload := []byte("signed raw artifact chunk")
|
||||
opened, err := core.OpenArtifactTransfer(domain.ArtifactTransferOpen{RunEndpointID: "run-local", SessionToken: token, ArtifactID: "artifact-signed-raw", Direction: domain.ArtifactTransferDirectionUpload, OwnerKind: domain.ArtifactOwnerKindJob, OwnerID: "job-signed-artifact", SizeBytes: int64(len(payload)), ChunkSizeBytes: len(payload), Checksum: validator.BytesChecksum(payload), IdempotencyKey: "signed-raw-artifact"})
|
||||
if err != nil {
|
||||
t.Fatalf("open transfer: %v", err)
|
||||
}
|
||||
router := NewTestRouterWithCore(core)
|
||||
unsigned := rawArtifactChunkRequest(t, router, token, opened.TransferID, payload, "nonce-unsigned-raw", stamp, false)
|
||||
assertErrorResponse(t, unsigned, http.StatusUnauthorized, errorCodeUnauthorized)
|
||||
signed := rawArtifactChunkRequest(t, router, token, opened.TransferID, payload, "nonce-signed-raw", stamp, true)
|
||||
assertStatus(t, signed, http.StatusOK)
|
||||
response := decodeBody[dto.ArtifactChunkUploadResponse](t, signed)
|
||||
if !response.Accepted || response.TransferID != opened.TransferID || response.NextMissingChunkIndex != 1 {
|
||||
t.Fatalf("unexpected signed chunk response: %+v", response)
|
||||
}
|
||||
}
|
||||
|
||||
func rawArtifactChunkRequest(t *testing.T, router http.Handler, token string, transferID string, payload []byte, nonce string, stamp time.Time, signed bool) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
path := "/api/v1/run/artifacts/chunks"
|
||||
timestamp := strconv.FormatInt(stamp.Unix(), 10)
|
||||
req := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(payload))
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
req.Header.Set("X-Run-Endpoint", "run-local")
|
||||
req.Header.Set("X-Run-Session-Token", token)
|
||||
req.Header.Set("X-Run-Timestamp", timestamp)
|
||||
req.Header.Set("X-Run-Nonce", nonce)
|
||||
req.Header.Set("X-Artifact-Transfer-Id", transferID)
|
||||
req.Header.Set("X-Artifact-Id", "artifact-signed-raw")
|
||||
req.Header.Set("X-Artifact-Chunk-Index", "0")
|
||||
req.Header.Set("X-Artifact-Offset", "0")
|
||||
req.Header.Set("X-Artifact-Size", strconv.Itoa(len(payload)))
|
||||
req.Header.Set("X-Artifact-Checksum", validator.BytesChecksum(payload))
|
||||
if signed {
|
||||
bodyHash := sha256.Sum256(payload)
|
||||
canonical := strings.Join([]string{http.MethodPost, path, timestamp, nonce, hex.EncodeToString(bodyHash[:])}, "\n")
|
||||
mac := hmac.New(sha256.New, []byte(token))
|
||||
_, _ = mac.Write([]byte(canonical))
|
||||
req.Header.Set("X-Run-Signature", hex.EncodeToString(mac.Sum(nil)))
|
||||
}
|
||||
recorder := httptest.NewRecorder()
|
||||
router.ServeHTTP(recorder, req)
|
||||
return recorder
|
||||
}
|
||||
|
||||
func TestAuthorizedRouterAllowsRunLifecycleReportWithoutBearer(t *testing.T) {
|
||||
store := repo.NewMemoryStore()
|
||||
core := service.NewCoreService(store)
|
||||
|
||||
@@ -17,7 +17,6 @@ type componentLogServerContextKey struct{}
|
||||
|
||||
const (
|
||||
logEventHeartbeatInterval = 15 * time.Second
|
||||
liveLogSourceClockSkew = 90 * time.Second
|
||||
managedLogSessionIDPrefix = "log-session:"
|
||||
)
|
||||
|
||||
@@ -27,7 +26,6 @@ func (h *coreHandlers) serverLogEvents(w http.ResponseWriter, r *http.Request) {
|
||||
writeMethodNotAllowed(w, http.MethodGet)
|
||||
return
|
||||
}
|
||||
liveBoundary := time.Now().UTC().Add(-liveLogSourceClockSkew)
|
||||
instance, streams, liveEligible, subscription, err := h.openLogEventSubscription(r)
|
||||
if err != nil {
|
||||
writeServiceError(w, err)
|
||||
@@ -150,12 +148,6 @@ func (h *coreHandlers) serverLogEvents(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !sourceLogEntryIsLive(event.Entry, liveBoundary) {
|
||||
if event.Entry.Seq > emittedThrough[event.Stream.ID] {
|
||||
emittedThrough[event.Stream.ID] = event.Entry.Seq
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !active.hasStream(event.Stream.ID) {
|
||||
active.streams = append(active.streams, event.Stream)
|
||||
if err := writeSSEJSON(w, "stream", "", dto.LogStreamFromDomain(event.Stream)); err != nil {
|
||||
@@ -179,10 +171,6 @@ func (h *coreHandlers) serverLogEvents(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func sourceLogEntryIsLive(entry domain.LogEntry, liveBoundary time.Time) bool {
|
||||
return !entry.Timestamp.IsZero() && !entry.Timestamp.Before(liveBoundary)
|
||||
}
|
||||
|
||||
type supervisedLogSession struct {
|
||||
sessionID string
|
||||
startedAt time.Time
|
||||
|
||||
@@ -153,7 +153,7 @@ func TestLogEventsSSELiveOnlyStartsAfterSnapshotTail(t *testing.T) {
|
||||
assertSSEEvent(t, reader, "log", `"seq":2`)
|
||||
}
|
||||
|
||||
func TestLogEventsSSESkipsBufferedBackfillAfterOpen(t *testing.T) {
|
||||
func TestLogEventsSSEStreamsBufferedAppendAfterOpen(t *testing.T) {
|
||||
router := newTestRouter()
|
||||
hello := createLogIngestAPIFixtures(t, router)
|
||||
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", validLogBatchRequest(t, hello.SessionToken, 1, 1)), http.StatusOK)
|
||||
@@ -180,11 +180,12 @@ func TestLogEventsSSESkipsBufferedBackfillAfterOpen(t *testing.T) {
|
||||
assertSSEEvent(t, reader, "stream", `"id":"log-1"`)
|
||||
assertSSEEvent(t, reader, "ready", `"streamCount":1`)
|
||||
|
||||
stale := validLogBatchRequest(t, hello.SessionToken, 2, 2)
|
||||
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", stale), http.StatusOK)
|
||||
buffered := validLogBatchRequest(t, hello.SessionToken, 2, 2)
|
||||
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", buffered), http.StatusOK)
|
||||
fresh := validLogBatchRequest(t, hello.SessionToken, 3, 3)
|
||||
retimestampLogBatchRequest(t, &fresh, time.Now().UTC().Add(time.Second))
|
||||
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", fresh), http.StatusOK)
|
||||
assertSSEEvent(t, reader, "log", `"seq":2`)
|
||||
assertSSEEvent(t, reader, "log", `"seq":3`)
|
||||
}
|
||||
|
||||
|
||||
@@ -944,6 +944,10 @@ func (h *coreHandlers) gamePluginManifestRegistration(w http.ResponseWriter, r *
|
||||
writeDecodeError(w, err)
|
||||
return
|
||||
}
|
||||
if violations := request.Manifest.RuntimeProfiles.UnsupportedLegacyClientManagerViolations("manifest.runtimeProfiles"); len(violations) > 0 {
|
||||
writeServiceError(w, validator.ValidationError{Violations: violations})
|
||||
return
|
||||
}
|
||||
plugin, err := h.core.RegisterGamePluginManifest(request.ToDomain())
|
||||
if err != nil {
|
||||
writeServiceError(w, err)
|
||||
|
||||
Reference in New Issue
Block a user