@descryy/runtime-controller 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capability-registry.d.ts +24 -0
- package/dist/capability-registry.d.ts.map +1 -0
- package/dist/capability-registry.js +42 -0
- package/dist/capability-registry.js.map +1 -0
- package/dist/collector-version.d.ts +10 -0
- package/dist/collector-version.d.ts.map +1 -0
- package/dist/collector-version.js +12 -0
- package/dist/collector-version.js.map +1 -0
- package/dist/container-sandbox.d.ts +188 -0
- package/dist/container-sandbox.d.ts.map +1 -0
- package/dist/container-sandbox.js +233 -0
- package/dist/container-sandbox.js.map +1 -0
- package/dist/controller.d.ts +162 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +433 -0
- package/dist/controller.js.map +1 -0
- package/dist/dependency-version-check.d.ts +90 -0
- package/dist/dependency-version-check.d.ts.map +1 -0
- package/dist/dependency-version-check.js +121 -0
- package/dist/dependency-version-check.js.map +1 -0
- package/dist/env.d.ts +16 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +18 -0
- package/dist/env.js.map +1 -0
- package/dist/environment-metadata.d.ts +23 -0
- package/dist/environment-metadata.d.ts.map +1 -0
- package/dist/environment-metadata.js +80 -0
- package/dist/environment-metadata.js.map +1 -0
- package/dist/environment-version-check.d.ts +83 -0
- package/dist/environment-version-check.d.ts.map +1 -0
- package/dist/environment-version-check.js +218 -0
- package/dist/environment-version-check.js.map +1 -0
- package/dist/execution-safety.d.ts +154 -0
- package/dist/execution-safety.d.ts.map +1 -0
- package/dist/execution-safety.js +194 -0
- package/dist/execution-safety.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestration.d.ts +70 -0
- package/dist/orchestration.d.ts.map +1 -0
- package/dist/orchestration.js +255 -0
- package/dist/orchestration.js.map +1 -0
- package/dist/process-collector.d.ts +34 -0
- package/dist/process-collector.d.ts.map +1 -0
- package/dist/process-collector.js +148 -0
- package/dist/process-collector.js.map +1 -0
- package/dist/process-manager.d.ts +133 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +333 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/readiness.d.ts +120 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +179 -0
- package/dist/readiness.js.map +1 -0
- package/dist/sandbox.d.ts +139 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +271 -0
- package/dist/sandbox.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime controller (plan §4, §5, §6; plan §45's "Core" bucket: runtime
|
|
3
|
+
* controller, execution IDs, process lifecycle, readiness, timeout
|
|
4
|
+
* handling, cleanup, configuration). Owns Execution lifecycle, spawns every
|
|
5
|
+
* service declared in configuration, waits for each one's real readiness
|
|
6
|
+
* (never "process exists"), and guarantees cleanup — including on partial
|
|
7
|
+
* or total failure and on timeout. Builds no collectors and knows nothing
|
|
8
|
+
* about any specific language or framework (plan principle 8).
|
|
9
|
+
*/
|
|
10
|
+
import { type Execution, type ProcessHandle } from "@descryy/runtime-contracts";
|
|
11
|
+
import { type ReadinessCheck, type ReadinessResult } from "./readiness.ts";
|
|
12
|
+
import type { ExecutionConfiguration } from "@descryy/runtime-contracts";
|
|
13
|
+
export interface CreateExecutionInput {
|
|
14
|
+
readonly application: string;
|
|
15
|
+
readonly repository: string;
|
|
16
|
+
readonly commit: string;
|
|
17
|
+
readonly configuration: ExecutionConfiguration;
|
|
18
|
+
}
|
|
19
|
+
export interface ServiceReadinessSpec {
|
|
20
|
+
readonly checks: (info: {
|
|
21
|
+
readonly port: number;
|
|
22
|
+
}) => readonly ReadinessCheck[];
|
|
23
|
+
readonly timeoutMs: number;
|
|
24
|
+
}
|
|
25
|
+
export interface RunOptions {
|
|
26
|
+
/** One entry required per key in `configuration.services` — RT-032 refuses rather than default a missing one to "spawn success is enough" (RT-024's own rule, applied per service). */
|
|
27
|
+
readonly readiness: Readonly<Record<string, ServiceReadinessSpec>>;
|
|
28
|
+
/**
|
|
29
|
+
* Optional live observer for `ProcessLifecycleEvent`. Called synchronously
|
|
30
|
+
* at each transition, in order, for the lifetime of this execution
|
|
31
|
+
* (including after `run()` itself has returned, for a service that exits
|
|
32
|
+
* later). A throwing observer is not caught here — the same "must not
|
|
33
|
+
* hang, may not silently swallow its own failure" discipline `Collector`
|
|
34
|
+
* already imposes on every callback shape in this repo; a caller wiring a
|
|
35
|
+
* `ProcessCollector`'s emit path in here that itself throws deserves to
|
|
36
|
+
* see that, not have it disappear into a promise this module owns.
|
|
37
|
+
*/
|
|
38
|
+
readonly onProcessLifecycleEvent?: (event: ProcessLifecycleEvent) => void;
|
|
39
|
+
}
|
|
40
|
+
export interface ServiceStartResult {
|
|
41
|
+
readonly serviceName: string;
|
|
42
|
+
readonly stage: "spawn" | "readiness" | "not-attempted";
|
|
43
|
+
readonly succeeded: boolean;
|
|
44
|
+
/** Set when `stage === "readiness"`. RT-024/RT-031 apply to each entry independently — N of these is N liveness claims about N addresses, never a joint identity proof. */
|
|
45
|
+
readonly readiness: ReadinessResult | null;
|
|
46
|
+
/** Set when `stage === "spawn"` and spawning itself threw. */
|
|
47
|
+
readonly error: string | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Fires at the exact moments `run()` itself observes a service's process
|
|
51
|
+
* lifecycle — spawned, readiness settled (either way), reaped. This is a
|
|
52
|
+
* pure notification channel: `ExecutionController` stays exactly as
|
|
53
|
+
* ignorant of Evidence/Collector as its own module doc already claims
|
|
54
|
+
* ("knows nothing about any specific language or framework") — nothing
|
|
55
|
+
* here decides whether a lifecycle transition becomes Evidence, only that
|
|
56
|
+
* a caller who wants to know *when* one happened, live, has a way to ask
|
|
57
|
+
* that isn't "reconstruct it later from `Execution.processes`."
|
|
58
|
+
*
|
|
59
|
+
* That reconstruction is precisely what doesn't work for the case this
|
|
60
|
+
* exists to make observable: `Execution.processes` and
|
|
61
|
+
* `ServiceStartResults` are only complete once `run()` has already
|
|
62
|
+
* returned, but a service that is spawned, then *running* (READY reached),
|
|
63
|
+
* has not exited yet at that point — its own eventual `process-exited`
|
|
64
|
+
* event lies in the future relative to `run()` resolving. A consumer that
|
|
65
|
+
* only reads the final `Execution` after `run()` settles can see "started"
|
|
66
|
+
* and "ready" (or "ready-failed") for every service, but can never see
|
|
67
|
+
* "exited" for one still healthy when `run()` returned — only this live
|
|
68
|
+
* channel can.
|
|
69
|
+
*/
|
|
70
|
+
export type ProcessLifecycleEvent = {
|
|
71
|
+
readonly kind: "process-started";
|
|
72
|
+
readonly serviceName: string;
|
|
73
|
+
readonly handle: ProcessHandle;
|
|
74
|
+
} | {
|
|
75
|
+
readonly kind: "process-ready";
|
|
76
|
+
readonly serviceName: string;
|
|
77
|
+
readonly handle: ProcessHandle;
|
|
78
|
+
readonly readiness: ReadinessResult;
|
|
79
|
+
} | {
|
|
80
|
+
readonly kind: "process-ready-failed";
|
|
81
|
+
readonly serviceName: string;
|
|
82
|
+
readonly handle: ProcessHandle;
|
|
83
|
+
readonly readiness: ReadinessResult;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `phase` distinguishes "this service's own readiness had not yet
|
|
87
|
+
* succeeded when it exited" (`"starting"`) from "it had already reached
|
|
88
|
+
* READY at least once" (`"running"`) — the process-level echo of
|
|
89
|
+
* `FAILED_START` vs `FAILED` on `ExecutionState` itself (§30). Without
|
|
90
|
+
* it, `PROCESS_EXITED` alone cannot tell "crashed on boot" from
|
|
91
|
+
* "finished/was stopped after running successfully," which collapses
|
|
92
|
+
* exactly the distinction the execution lifecycle already exists to
|
|
93
|
+
* carry. Computed from THIS service's own readiness history, not from
|
|
94
|
+
* `Execution.state` at the moment of exit — a crash mid-readiness-poll
|
|
95
|
+
* fires before `run()` has itself observed the failure and moved the
|
|
96
|
+
* execution to `FAILED_START`, so reading execution state at exit time
|
|
97
|
+
* would misreport `"starting"` as whatever the execution's not-yet-
|
|
98
|
+
* updated state still says.
|
|
99
|
+
*/
|
|
100
|
+
| {
|
|
101
|
+
readonly kind: "process-exited";
|
|
102
|
+
readonly serviceName: string;
|
|
103
|
+
readonly handle: ProcessHandle;
|
|
104
|
+
readonly phase: "starting" | "running";
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* `run()`'s timeout timer and a caller's `stop()`/`cancel()` can both
|
|
108
|
+
* attempt to move a finishing execution to a terminal state at nearly the
|
|
109
|
+
* same instant. Every transition attempted after an `await` goes through
|
|
110
|
+
* `#tryTransition` (returns false instead of throwing) rather than the
|
|
111
|
+
* throwing `#transition`, so whichever path loses the race degrades to a
|
|
112
|
+
* no-op instead of an unhandled rejection from a fire-and-forget timer
|
|
113
|
+
* callback.
|
|
114
|
+
*/
|
|
115
|
+
export declare class ExecutionController {
|
|
116
|
+
#private;
|
|
117
|
+
private constructor();
|
|
118
|
+
static create(input: CreateExecutionInput): ExecutionController;
|
|
119
|
+
get execution(): Execution;
|
|
120
|
+
/** One entry per service configured, in spawn order — including services never attempted because an earlier one failed (`stage: "not-attempted"`). Empty before `run()` returns its first result. */
|
|
121
|
+
get serviceStartResults(): readonly ServiceStartResult[];
|
|
122
|
+
/** Non-null only when `run()` refused before spawning anything — a bad `dependsOn` reference, a cycle, an undeclared placeholder dependency, or a service with no matching `RunOptions.readiness` entry. */
|
|
123
|
+
get validationError(): string | null;
|
|
124
|
+
/** Combined stdout+stderr of the named service's process so far, or "" if it was never spawned. */
|
|
125
|
+
readProcessOutput(serviceName: string): string;
|
|
126
|
+
/**
|
|
127
|
+
* Starts every service in `configuration.services`, in the dependency
|
|
128
|
+
* order `validateServicesConfiguration` computes — spawned, each on its
|
|
129
|
+
* own ephemeral port unless it opts into a fixed one, or attached to
|
|
130
|
+
* (`service.attach`, `attachManagedProcess`) when it names an already-
|
|
131
|
+
* running pid/log file instead of a `command`. Reaches RUNNING only if
|
|
132
|
+
* every service starts and reaches its own readiness. On the first
|
|
133
|
+
* failure (spawn/attach throw, or readiness never succeeding), every
|
|
134
|
+
* already-started service is killed in reverse order (a no-op for an
|
|
135
|
+
* attached one — see `attachManagedProcess`'s own doc) and every
|
|
136
|
+
* not-yet-attempted service is recorded as such — a caller can always
|
|
137
|
+
* tell "never tried" from "tried and failed" via `serviceStartResults`.
|
|
138
|
+
*
|
|
139
|
+
* Configuration itself is validated first, atomically: a bad `dependsOn`
|
|
140
|
+
* reference, a cycle, an undeclared placeholder dependency, a service
|
|
141
|
+
* declaring neither or both of `command`/`attach`, or a missing
|
|
142
|
+
* `RunOptions.readiness` entry refuses before anything is spawned, never
|
|
143
|
+
* discovered mid-sequence with other services already running.
|
|
144
|
+
*
|
|
145
|
+
* Every **spawned** service receives `PORT` (its own resolved port,
|
|
146
|
+
* ephemeral or fixed) in its environment unconditionally, on top of
|
|
147
|
+
* `service.env` — a service has no other way to learn the port RT-024
|
|
148
|
+
* chose for it. This is a real, load-bearing convention (matches
|
|
149
|
+
* `broken-invoices`'s fixture apps), not an implementation detail: a
|
|
150
|
+
* `ServiceConfiguration` whose command reads its listen port from
|
|
151
|
+
* anywhere other than `PORT` will not be reachable through readiness
|
|
152
|
+
* checks built from the same `port` this function resolves. An
|
|
153
|
+
* **attached** service receives no env and no port allocation — it is
|
|
154
|
+
* already running on whatever port it bound at its own launch, and
|
|
155
|
+
* `service.port` (when the caller supplies it) is a declaration of that
|
|
156
|
+
* fact, not a request this function can act on.
|
|
157
|
+
*/
|
|
158
|
+
run(options: RunOptions): Promise<Execution>;
|
|
159
|
+
stop(): Promise<Execution>;
|
|
160
|
+
cancel(): Promise<Execution>;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime controller (plan §4, §5, §6; plan §45's "Core" bucket: runtime
|
|
3
|
+
* controller, execution IDs, process lifecycle, readiness, timeout
|
|
4
|
+
* handling, cleanup, configuration). Owns Execution lifecycle, spawns every
|
|
5
|
+
* service declared in configuration, waits for each one's real readiness
|
|
6
|
+
* (never "process exists"), and guarantees cleanup — including on partial
|
|
7
|
+
* or total failure and on timeout. Builds no collectors and knows nothing
|
|
8
|
+
* about any specific language or framework (plan principle 8).
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
import { isTerminalExecutionState, isValidExecutionTransition, } from "@descryy/runtime-contracts";
|
|
12
|
+
import { spawnProcess, attachManagedProcess, allocateEphemeralPort } from "./process-manager.js";
|
|
13
|
+
import { awaitReadiness } from "./readiness.js";
|
|
14
|
+
import { withDefaultProcessEnv } from "./env.js";
|
|
15
|
+
import { validateServicesConfiguration, substitutePortPlaceholders, tokenizeCommand } from "./orchestration.js";
|
|
16
|
+
import { resourceLimitCapability, networkPolicyCapability } from "./execution-safety.js";
|
|
17
|
+
import { filesystemIsolationCapability, unsupportedNetworkPolicyReason } from "./sandbox.js";
|
|
18
|
+
import { containerFilesystemIsolationCapability, containerNetworkIsolationCapability, unsupportedContainerNetworkPolicyReason, } from "./container-sandbox.js";
|
|
19
|
+
import { captureEnvironmentMetadata } from "./environment-metadata.js";
|
|
20
|
+
/**
|
|
21
|
+
* `run()`'s timeout timer and a caller's `stop()`/`cancel()` can both
|
|
22
|
+
* attempt to move a finishing execution to a terminal state at nearly the
|
|
23
|
+
* same instant. Every transition attempted after an `await` goes through
|
|
24
|
+
* `#tryTransition` (returns false instead of throwing) rather than the
|
|
25
|
+
* throwing `#transition`, so whichever path loses the race degrades to a
|
|
26
|
+
* no-op instead of an unhandled rejection from a fire-and-forget timer
|
|
27
|
+
* callback.
|
|
28
|
+
*/
|
|
29
|
+
export class ExecutionController {
|
|
30
|
+
#execution;
|
|
31
|
+
#processes = new Map();
|
|
32
|
+
#timeoutHandle = null;
|
|
33
|
+
#serviceStartResults = [];
|
|
34
|
+
#validationError = null;
|
|
35
|
+
constructor(execution) {
|
|
36
|
+
this.#execution = execution;
|
|
37
|
+
}
|
|
38
|
+
static create(input) {
|
|
39
|
+
const execution = {
|
|
40
|
+
executionId: randomUUID(),
|
|
41
|
+
application: input.application,
|
|
42
|
+
repository: input.repository,
|
|
43
|
+
commit: input.commit,
|
|
44
|
+
environmentMetadata: null,
|
|
45
|
+
configuration: input.configuration,
|
|
46
|
+
state: "CREATED",
|
|
47
|
+
startedAt: null,
|
|
48
|
+
endedAt: null,
|
|
49
|
+
processes: [],
|
|
50
|
+
browserSessions: [],
|
|
51
|
+
eventIds: [],
|
|
52
|
+
traceIds: [],
|
|
53
|
+
failureIds: [],
|
|
54
|
+
correlationIds: [],
|
|
55
|
+
findingIds: [],
|
|
56
|
+
};
|
|
57
|
+
return new ExecutionController(execution);
|
|
58
|
+
}
|
|
59
|
+
get execution() {
|
|
60
|
+
return this.#execution;
|
|
61
|
+
}
|
|
62
|
+
/** One entry per service configured, in spawn order — including services never attempted because an earlier one failed (`stage: "not-attempted"`). Empty before `run()` returns its first result. */
|
|
63
|
+
get serviceStartResults() {
|
|
64
|
+
return this.#serviceStartResults;
|
|
65
|
+
}
|
|
66
|
+
/** Non-null only when `run()` refused before spawning anything — a bad `dependsOn` reference, a cycle, an undeclared placeholder dependency, or a service with no matching `RunOptions.readiness` entry. */
|
|
67
|
+
get validationError() {
|
|
68
|
+
return this.#validationError;
|
|
69
|
+
}
|
|
70
|
+
/** Combined stdout+stderr of the named service's process so far, or "" if it was never spawned. */
|
|
71
|
+
readProcessOutput(serviceName) {
|
|
72
|
+
return this.#processes.get(serviceName)?.readOutput() ?? "";
|
|
73
|
+
}
|
|
74
|
+
#transition(to) {
|
|
75
|
+
if (!isValidExecutionTransition(this.#execution.state, to)) {
|
|
76
|
+
throw new Error(`invalid execution transition: ${this.#execution.state} -> ${to}`);
|
|
77
|
+
}
|
|
78
|
+
this.#execution = { ...this.#execution, state: to };
|
|
79
|
+
}
|
|
80
|
+
#tryTransition(to) {
|
|
81
|
+
if (!isValidExecutionTransition(this.#execution.state, to)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
this.#transition(to);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
#recordProcesses() {
|
|
88
|
+
const handles = [];
|
|
89
|
+
for (const managed of this.#processes.values()) {
|
|
90
|
+
handles.push(managed.handle());
|
|
91
|
+
}
|
|
92
|
+
this.#execution = { ...this.#execution, processes: handles };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Starts every service in `configuration.services`, in the dependency
|
|
96
|
+
* order `validateServicesConfiguration` computes — spawned, each on its
|
|
97
|
+
* own ephemeral port unless it opts into a fixed one, or attached to
|
|
98
|
+
* (`service.attach`, `attachManagedProcess`) when it names an already-
|
|
99
|
+
* running pid/log file instead of a `command`. Reaches RUNNING only if
|
|
100
|
+
* every service starts and reaches its own readiness. On the first
|
|
101
|
+
* failure (spawn/attach throw, or readiness never succeeding), every
|
|
102
|
+
* already-started service is killed in reverse order (a no-op for an
|
|
103
|
+
* attached one — see `attachManagedProcess`'s own doc) and every
|
|
104
|
+
* not-yet-attempted service is recorded as such — a caller can always
|
|
105
|
+
* tell "never tried" from "tried and failed" via `serviceStartResults`.
|
|
106
|
+
*
|
|
107
|
+
* Configuration itself is validated first, atomically: a bad `dependsOn`
|
|
108
|
+
* reference, a cycle, an undeclared placeholder dependency, a service
|
|
109
|
+
* declaring neither or both of `command`/`attach`, or a missing
|
|
110
|
+
* `RunOptions.readiness` entry refuses before anything is spawned, never
|
|
111
|
+
* discovered mid-sequence with other services already running.
|
|
112
|
+
*
|
|
113
|
+
* Every **spawned** service receives `PORT` (its own resolved port,
|
|
114
|
+
* ephemeral or fixed) in its environment unconditionally, on top of
|
|
115
|
+
* `service.env` — a service has no other way to learn the port RT-024
|
|
116
|
+
* chose for it. This is a real, load-bearing convention (matches
|
|
117
|
+
* `broken-invoices`'s fixture apps), not an implementation detail: a
|
|
118
|
+
* `ServiceConfiguration` whose command reads its listen port from
|
|
119
|
+
* anywhere other than `PORT` will not be reachable through readiness
|
|
120
|
+
* checks built from the same `port` this function resolves. An
|
|
121
|
+
* **attached** service receives no env and no port allocation — it is
|
|
122
|
+
* already running on whatever port it bound at its own launch, and
|
|
123
|
+
* `service.port` (when the caller supplies it) is a declaration of that
|
|
124
|
+
* fact, not a request this function can act on.
|
|
125
|
+
*/
|
|
126
|
+
async run(options) {
|
|
127
|
+
this.#transition("STARTING");
|
|
128
|
+
this.#armTimeout();
|
|
129
|
+
const services = this.#execution.configuration.services;
|
|
130
|
+
const validation = validateServicesConfiguration(services);
|
|
131
|
+
if (!validation.valid) {
|
|
132
|
+
return this.#refuseToStart(validation.reason, Object.keys(services));
|
|
133
|
+
}
|
|
134
|
+
// §21's execution boundary (RT-070): the same "refuse atomically before
|
|
135
|
+
// anything is spawned" discipline `tokenizeCommand` already gets, one
|
|
136
|
+
// check up. Requesting a limit this platform cannot enforce and finding
|
|
137
|
+
// out only when the first `spawnProcess()` throws would mean earlier
|
|
138
|
+
// services in `validation.order` are already running by the time the
|
|
139
|
+
// refusal happens -- recoverable via the existing cleanup path, but
|
|
140
|
+
// needlessly, since nothing about this check needs a process to exist.
|
|
141
|
+
const resourceLimits = this.#execution.configuration.resourceLimits;
|
|
142
|
+
if (resourceLimits !== undefined) {
|
|
143
|
+
const capability = resourceLimitCapability();
|
|
144
|
+
if (capability.availability !== "available") {
|
|
145
|
+
return this.#refuseToStart(`resourceLimits was configured but cannot be enforced on this platform: ${capability.reason}`, Object.keys(services));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Which real mechanism backs filesystemPolicy/networkPolicy for this
|
|
149
|
+
// execution -- absent means "bwrap", exactly today's behavior. See
|
|
150
|
+
// ExecutionConfiguration.sandboxBackend's own comment.
|
|
151
|
+
const sandboxBackend = this.#execution.configuration.sandboxBackend ?? "bwrap";
|
|
152
|
+
// RT-193, same atomic-refusal placement as resourceLimits above.
|
|
153
|
+
// `networkPolicyCapability()` is real on Linux now (sandbox-isolation
|
|
154
|
+
// lane: a bwrap network namespace) but still `unavailable` on
|
|
155
|
+
// macOS/Windows, and even where the mechanism exists it only
|
|
156
|
+
// implements one shape (full denial) -- both refusals happen here,
|
|
157
|
+
// atomically, before anything is spawned, never silently accepted and
|
|
158
|
+
// left unenforced. `sandboxBackend: "container"` checks the container
|
|
159
|
+
// mechanism's own capability/shape functions instead -- same refusal
|
|
160
|
+
// discipline, different real mechanism underneath.
|
|
161
|
+
const networkPolicy = this.#execution.configuration.networkPolicy;
|
|
162
|
+
if (networkPolicy !== undefined) {
|
|
163
|
+
const shapeReason = sandboxBackend === "container" ? unsupportedContainerNetworkPolicyReason(networkPolicy) : unsupportedNetworkPolicyReason(networkPolicy);
|
|
164
|
+
if (shapeReason !== null) {
|
|
165
|
+
return this.#refuseToStart(`networkPolicy was configured but cannot be enforced: ${shapeReason}`, Object.keys(services));
|
|
166
|
+
}
|
|
167
|
+
const capability = sandboxBackend === "container" ? containerNetworkIsolationCapability() : networkPolicyCapability();
|
|
168
|
+
if (capability.availability !== "available") {
|
|
169
|
+
return this.#refuseToStart(`networkPolicy was configured but cannot be enforced on this platform: ${capability.reason}`, Object.keys(services));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Sandbox-isolation lane, filesystem half: same atomic-refusal
|
|
173
|
+
// placement and precedent -- real on Linux via `applySandbox`'s bwrap
|
|
174
|
+
// mount namespace, refused rather than silently unconstrained where
|
|
175
|
+
// that mechanism doesn't exist. `sandboxBackend: "container"` checks
|
|
176
|
+
// Docker's own capability instead.
|
|
177
|
+
const filesystemPolicy = this.#execution.configuration.filesystemPolicy;
|
|
178
|
+
if (filesystemPolicy !== undefined) {
|
|
179
|
+
const capability = sandboxBackend === "container" ? containerFilesystemIsolationCapability() : filesystemIsolationCapability();
|
|
180
|
+
if (capability.availability !== "available") {
|
|
181
|
+
return this.#refuseToStart(`filesystemPolicy was configured but cannot be enforced on this platform: ${capability.reason}`, Object.keys(services));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (const name of validation.order) {
|
|
185
|
+
if (options.readiness[name] === undefined) {
|
|
186
|
+
return this.#refuseToStart(`service "${name}" has no matching RunOptions.readiness entry`, validation.order);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// RT-193: captured once real spawning is committed to happen, from the
|
|
190
|
+
// real host -- see `EnvironmentMetadata`'s own comment for why `commit`
|
|
191
|
+
// alone does not make a run reproducible.
|
|
192
|
+
const environmentMetadata = await captureEnvironmentMetadata(services);
|
|
193
|
+
this.#execution = { ...this.#execution, startedAt: new Date().toISOString(), environmentMetadata };
|
|
194
|
+
const resolvedPorts = new Map();
|
|
195
|
+
for (let i = 0; i < validation.order.length; i++) {
|
|
196
|
+
const name = validation.order[i];
|
|
197
|
+
if (name === undefined)
|
|
198
|
+
continue;
|
|
199
|
+
if (isTerminalExecutionState(this.#execution.state)) {
|
|
200
|
+
// A concurrent timeout/stop/cancel already finished this execution
|
|
201
|
+
// while we were mid-sequence.
|
|
202
|
+
return this.#execution;
|
|
203
|
+
}
|
|
204
|
+
const service = services[name];
|
|
205
|
+
if (service === undefined)
|
|
206
|
+
continue;
|
|
207
|
+
const readinessSpec = options.readiness[name];
|
|
208
|
+
if (readinessSpec === undefined)
|
|
209
|
+
continue;
|
|
210
|
+
let managed;
|
|
211
|
+
let port;
|
|
212
|
+
if (service.attach !== undefined) {
|
|
213
|
+
// Attach mode (RT-attach-mode-service-configuration): the target is
|
|
214
|
+
// already running and already bound to whatever it bound at its own
|
|
215
|
+
// launch -- nothing here can allocate a port on its behalf, only
|
|
216
|
+
// record the one the caller already knows about. `0` (never a real
|
|
217
|
+
// listening port) when they did not supply one, so a readiness
|
|
218
|
+
// check that actually needs a port fails legibly against it
|
|
219
|
+
// ("nothing answered") instead of this function inventing an
|
|
220
|
+
// ephemeral port that has nothing to do with the real target.
|
|
221
|
+
// `resourceLimits`/`filesystemPolicy`/`networkPolicy`/`sandboxBackend`
|
|
222
|
+
// are deliberately never passed through — see `ServiceConfiguration.attach`'s
|
|
223
|
+
// own comment on why they cannot apply to a process this runtime
|
|
224
|
+
// did not spawn.
|
|
225
|
+
port = service.port ?? 0;
|
|
226
|
+
try {
|
|
227
|
+
managed = attachManagedProcess({
|
|
228
|
+
processId: randomUUID(),
|
|
229
|
+
attach: service.attach,
|
|
230
|
+
serviceName: name,
|
|
231
|
+
...(service.port === undefined ? {} : { port: service.port }),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
this.#serviceStartResults.push({
|
|
236
|
+
serviceName: name,
|
|
237
|
+
stage: "spawn",
|
|
238
|
+
succeeded: false,
|
|
239
|
+
readiness: null,
|
|
240
|
+
error: err instanceof Error ? err.message : String(err),
|
|
241
|
+
});
|
|
242
|
+
this.#markRemainingNotAttempted(validation.order, i + 1);
|
|
243
|
+
this.#tryTransition("FAILED_START");
|
|
244
|
+
await this.#cleanup();
|
|
245
|
+
this.#finish();
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
port = service.port ?? (await allocateEphemeralPort());
|
|
251
|
+
const env = withDefaultProcessEnv({
|
|
252
|
+
PORT: String(port),
|
|
253
|
+
...substitutePortPlaceholders(service.env, resolvedPorts),
|
|
254
|
+
});
|
|
255
|
+
// `validateServicesConfiguration` already proved every service
|
|
256
|
+
// declares exactly one of `command`/`attach` and, when it's
|
|
257
|
+
// `command`, that it tokenizes -- both branches below are
|
|
258
|
+
// unreachable in practice, kept because the alternative is a
|
|
259
|
+
// non-null assertion that would become a real crash if the two
|
|
260
|
+
// ever drift apart.
|
|
261
|
+
if (service.command === undefined) {
|
|
262
|
+
return this.#refuseToStart(`service "${name}": neither "command" nor "attach" is set`, validation.order);
|
|
263
|
+
}
|
|
264
|
+
const tokenized = tokenizeCommand(service.command);
|
|
265
|
+
if (!tokenized.ok) {
|
|
266
|
+
return this.#refuseToStart(`service "${name}": ${tokenized.reason}`, validation.order);
|
|
267
|
+
}
|
|
268
|
+
const { command, args } = tokenized;
|
|
269
|
+
try {
|
|
270
|
+
managed = spawnProcess({
|
|
271
|
+
processId: randomUUID(),
|
|
272
|
+
command,
|
|
273
|
+
args,
|
|
274
|
+
cwd: service.cwd,
|
|
275
|
+
env,
|
|
276
|
+
serviceName: name,
|
|
277
|
+
port,
|
|
278
|
+
...(resourceLimits === undefined ? {} : { resourceLimits }),
|
|
279
|
+
...(filesystemPolicy === undefined ? {} : { filesystemPolicy }),
|
|
280
|
+
...(networkPolicy === undefined ? {} : { networkPolicy }),
|
|
281
|
+
...(sandboxBackend === "bwrap" ? {} : { sandboxBackend }),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
this.#serviceStartResults.push({
|
|
286
|
+
serviceName: name,
|
|
287
|
+
stage: "spawn",
|
|
288
|
+
succeeded: false,
|
|
289
|
+
readiness: null,
|
|
290
|
+
error: err instanceof Error ? err.message : String(err),
|
|
291
|
+
});
|
|
292
|
+
this.#markRemainingNotAttempted(validation.order, i + 1);
|
|
293
|
+
this.#tryTransition("FAILED_START");
|
|
294
|
+
await this.#cleanup();
|
|
295
|
+
this.#finish();
|
|
296
|
+
throw err;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
this.#processes.set(name, managed);
|
|
300
|
+
resolvedPorts.set(name, port);
|
|
301
|
+
this.#recordProcesses();
|
|
302
|
+
options.onProcessLifecycleEvent?.({ kind: "process-started", serviceName: name, handle: managed.handle() });
|
|
303
|
+
// Read by the exit watcher below, whenever it actually fires — true
|
|
304
|
+
// only once THIS service's own readiness has succeeded, which is
|
|
305
|
+
// what lets `phase` distinguish "died before ever confirmed up" from
|
|
306
|
+
// "died after running" regardless of how much later exit occurs.
|
|
307
|
+
let reachedReady = false;
|
|
308
|
+
// Fire-and-forget: exit can happen at any point in this process's
|
|
309
|
+
// life, including long after `run()` itself has returned (a healthy
|
|
310
|
+
// service that later crashes, or the eventual `#cleanup()` reap) —
|
|
311
|
+
// it must not be awaited inline here, which would block every
|
|
312
|
+
// service after it in `validation.order` on this one's exit.
|
|
313
|
+
void managed.waitForExit().then(() => {
|
|
314
|
+
options.onProcessLifecycleEvent?.({
|
|
315
|
+
kind: "process-exited",
|
|
316
|
+
serviceName: name,
|
|
317
|
+
handle: managed.handle(),
|
|
318
|
+
phase: reachedReady ? "running" : "starting",
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
const readinessResult = await awaitReadiness(readinessSpec.checks({ port }), {
|
|
322
|
+
timeoutMs: readinessSpec.timeoutMs,
|
|
323
|
+
});
|
|
324
|
+
if (readinessResult.ready)
|
|
325
|
+
reachedReady = true;
|
|
326
|
+
this.#serviceStartResults.push({
|
|
327
|
+
serviceName: name,
|
|
328
|
+
stage: "readiness",
|
|
329
|
+
succeeded: readinessResult.ready,
|
|
330
|
+
readiness: readinessResult,
|
|
331
|
+
error: null,
|
|
332
|
+
});
|
|
333
|
+
options.onProcessLifecycleEvent?.({
|
|
334
|
+
kind: readinessResult.ready ? "process-ready" : "process-ready-failed",
|
|
335
|
+
serviceName: name,
|
|
336
|
+
handle: managed.handle(),
|
|
337
|
+
readiness: readinessResult,
|
|
338
|
+
});
|
|
339
|
+
if (isTerminalExecutionState(this.#execution.state)) {
|
|
340
|
+
return this.#execution;
|
|
341
|
+
}
|
|
342
|
+
if (!readinessResult.ready) {
|
|
343
|
+
this.#markRemainingNotAttempted(validation.order, i + 1);
|
|
344
|
+
if (this.#tryTransition("FAILED_START")) {
|
|
345
|
+
await this.#cleanup();
|
|
346
|
+
}
|
|
347
|
+
return this.#finish();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (this.#tryTransition("READY")) {
|
|
351
|
+
this.#tryTransition("RUNNING");
|
|
352
|
+
}
|
|
353
|
+
return this.#execution;
|
|
354
|
+
}
|
|
355
|
+
#refuseToStart(reason, allServiceNames) {
|
|
356
|
+
this.#validationError = reason;
|
|
357
|
+
this.#serviceStartResults = allServiceNames.map((serviceName) => ({
|
|
358
|
+
serviceName,
|
|
359
|
+
stage: "not-attempted",
|
|
360
|
+
succeeded: false,
|
|
361
|
+
readiness: null,
|
|
362
|
+
error: null,
|
|
363
|
+
}));
|
|
364
|
+
this.#tryTransition("FAILED_START");
|
|
365
|
+
return this.#finish();
|
|
366
|
+
}
|
|
367
|
+
#markRemainingNotAttempted(order, fromIndex) {
|
|
368
|
+
for (let i = fromIndex; i < order.length; i++) {
|
|
369
|
+
const serviceName = order[i];
|
|
370
|
+
if (serviceName === undefined)
|
|
371
|
+
continue;
|
|
372
|
+
this.#serviceStartResults.push({
|
|
373
|
+
serviceName,
|
|
374
|
+
stage: "not-attempted",
|
|
375
|
+
succeeded: false,
|
|
376
|
+
readiness: null,
|
|
377
|
+
error: null,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
async stop() {
|
|
382
|
+
if (isTerminalExecutionState(this.#execution.state) || this.#execution.state === "STOPPING") {
|
|
383
|
+
return this.#execution;
|
|
384
|
+
}
|
|
385
|
+
if (this.#tryTransition("STOPPING")) {
|
|
386
|
+
await this.#cleanup();
|
|
387
|
+
this.#tryTransition("COMPLETED");
|
|
388
|
+
}
|
|
389
|
+
return this.#finish();
|
|
390
|
+
}
|
|
391
|
+
async cancel() {
|
|
392
|
+
if (isTerminalExecutionState(this.#execution.state) || this.#execution.state === "STOPPING") {
|
|
393
|
+
return this.#execution;
|
|
394
|
+
}
|
|
395
|
+
await this.#cleanup();
|
|
396
|
+
this.#tryTransition("CANCELLED");
|
|
397
|
+
return this.#finish();
|
|
398
|
+
}
|
|
399
|
+
#armTimeout() {
|
|
400
|
+
this.#timeoutHandle = setTimeout(() => {
|
|
401
|
+
void this.#onTimeout();
|
|
402
|
+
}, this.#execution.configuration.timeoutMs);
|
|
403
|
+
}
|
|
404
|
+
async #onTimeout() {
|
|
405
|
+
if (isTerminalExecutionState(this.#execution.state)) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
await this.#cleanup();
|
|
409
|
+
this.#tryTransition("TIMED_OUT");
|
|
410
|
+
this.#finish();
|
|
411
|
+
}
|
|
412
|
+
/** Kills every spawned service in reverse start order — last up, first down. */
|
|
413
|
+
async #cleanup() {
|
|
414
|
+
if (this.#timeoutHandle !== null) {
|
|
415
|
+
clearTimeout(this.#timeoutHandle);
|
|
416
|
+
this.#timeoutHandle = null;
|
|
417
|
+
}
|
|
418
|
+
const managed = [...this.#processes.values()].reverse();
|
|
419
|
+
for (const proc of managed) {
|
|
420
|
+
await proc.kill();
|
|
421
|
+
}
|
|
422
|
+
if (managed.length > 0) {
|
|
423
|
+
this.#recordProcesses();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
#finish() {
|
|
427
|
+
if (this.#execution.endedAt === null) {
|
|
428
|
+
this.#execution = { ...this.#execution, endedAt: new Date().toISOString() };
|
|
429
|
+
}
|
|
430
|
+
return this.#execution;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
//# sourceMappingURL=controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
|