@usine/cloudflare 0.0.0-stage → 0.1.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/LICENSE +661 -0
- package/README.md +95 -2
- package/dist/access.d.ts +12 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +32 -0
- package/dist/access.js.map +1 -0
- package/dist/api/api.d.ts +215 -0
- package/dist/api/api.d.ts.map +1 -0
- package/dist/api/api.js +180 -0
- package/dist/api/api.js.map +1 -0
- package/dist/api/harness.d.ts +49 -0
- package/dist/api/harness.d.ts.map +1 -0
- package/dist/api/harness.js +57 -0
- package/dist/api/harness.js.map +1 -0
- package/dist/api/names.d.ts +28 -0
- package/dist/api/names.d.ts.map +1 -0
- package/dist/api/names.js +31 -0
- package/dist/api/names.js.map +1 -0
- package/dist/api/stream.d.ts +65 -0
- package/dist/api/stream.d.ts.map +1 -0
- package/dist/api/stream.js +70 -0
- package/dist/api/stream.js.map +1 -0
- package/dist/registry.d.ts +28 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +45 -0
- package/dist/registry.js.map +1 -0
- package/dist/sandbox/outbound.d.ts +53 -0
- package/dist/sandbox/outbound.d.ts.map +1 -0
- package/dist/sandbox/outbound.js +109 -0
- package/dist/sandbox/outbound.js.map +1 -0
- package/dist/sandbox/ports.d.ts +99 -0
- package/dist/sandbox/ports.d.ts.map +1 -0
- package/dist/sandbox/ports.js +66 -0
- package/dist/sandbox/ports.js.map +1 -0
- package/dist/sandbox/process.d.ts +55 -0
- package/dist/sandbox/process.d.ts.map +1 -0
- package/dist/sandbox/process.js +172 -0
- package/dist/sandbox/process.js.map +1 -0
- package/dist/sandbox/sandbox.d.ts +36 -0
- package/dist/sandbox/sandbox.d.ts.map +1 -0
- package/dist/sandbox/sandbox.js +387 -0
- package/dist/sandbox/sandbox.js.map +1 -0
- package/dist/sandbox/stream.d.ts +26 -0
- package/dist/sandbox/stream.d.ts.map +1 -0
- package/dist/sandbox/stream.js +167 -0
- package/dist/sandbox/stream.js.map +1 -0
- package/dist/worker.d.ts +77 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +362 -0
- package/dist/worker.js.map +1 -0
- package/image/Dockerfile +43 -0
- package/image/codex-config.toml +10 -0
- package/image/init.sh +17 -0
- package/package.json +59 -3
- package/wrangler.jsonc +32 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { CloudflareImageName } from "@usine/core/worker";
|
|
2
|
+
import { DateTime, Duration, Effect, Layer, Option, Schema, Semaphore } from "effect";
|
|
3
|
+
import { HttpServer } from "effect/http";
|
|
4
|
+
import { HttpApi, HttpApiBuilder } from "effect/http-api";
|
|
5
|
+
import { ContainerUnavailable, Destroyed, HealthReport, healthGroup, ImageNotFound, ProcessExited, ProcessNotFound, ProcessRunning, ProcessStatus, SANDBOX_PROTOCOL, SandboxConflict, sandboxGroup, SandboxNotFound, StaleCredentials, Stopped, } from "../api/api.js";
|
|
6
|
+
import { DRAIN_MARKER } from "../api/harness.js";
|
|
7
|
+
import { interceptionsFor } from "./outbound.js";
|
|
8
|
+
import { ContainerFailure, SandboxContainer, SandboxDirectory, SandboxSettings, SandboxStorage, StoredSandbox, } from "./ports.js";
|
|
9
|
+
import { exitCodeOf, pidOf, processFiles, stopCommand, stoppedCount } from "./process.js";
|
|
10
|
+
/** How long the keepalive alarm keeps a container running after the last create, renewal, or command. */
|
|
11
|
+
export const LEASE = Duration.hours(2);
|
|
12
|
+
/** The interval of the keepalive alarm. */
|
|
13
|
+
const KEEPALIVE_INTERVAL = Duration.minutes(4);
|
|
14
|
+
/** The time without a request after which the platform stops the container. */
|
|
15
|
+
const INACTIVITY_TIMEOUT = Duration.minutes(10);
|
|
16
|
+
const decoder = new TextDecoder();
|
|
17
|
+
const unavailable = (name) => (failure) => new ContainerUnavailable({ name, message: `${failure.operation}: ${failure.message}` });
|
|
18
|
+
/** Returns the bytes of `stream`, or none if it's `null`. */
|
|
19
|
+
const readAll = async (stream) => stream === null ? new Uint8Array() : new Uint8Array(await new Response(stream).arrayBuffer());
|
|
20
|
+
/** Waits for `process` to exit and returns its output bytes, unchanged, and exit code. */
|
|
21
|
+
export const collectBytes = (process) => Effect.tryPromise({
|
|
22
|
+
try: () => Promise.all([readAll(process.stdout), readAll(process.stderr), process.exitCode]),
|
|
23
|
+
catch: (cause) => new ContainerFailure({
|
|
24
|
+
operation: "exec",
|
|
25
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
26
|
+
}),
|
|
27
|
+
}).pipe(Effect.map(([stdout, stderr, exitCode]) => ({ stdout, stderr, exitCode })));
|
|
28
|
+
/** Waits for `process` to exit and returns its output as UTF-8 text, and exit code. */
|
|
29
|
+
const collect = (process) => collectBytes(process).pipe(Effect.map(({ stdout, stderr, exitCode }) => ({
|
|
30
|
+
stdout: decoder.decode(stdout),
|
|
31
|
+
stderr: decoder.decode(stderr),
|
|
32
|
+
exitCode,
|
|
33
|
+
})));
|
|
34
|
+
/**
|
|
35
|
+
* Returns `sandbox`'s summary as its container is now. A drain marker that can't be read is
|
|
36
|
+
* logged and reported as no drain; the executor asks again.
|
|
37
|
+
*/
|
|
38
|
+
const summarize = Effect.fn("Sandbox.summarize")(function* (sandbox) {
|
|
39
|
+
const container = yield* SandboxContainer;
|
|
40
|
+
const running = yield* container.running;
|
|
41
|
+
if (!running)
|
|
42
|
+
return sandbox.summary(false, false);
|
|
43
|
+
const draining = yield* container.size(DRAIN_MARKER).pipe(Effect.map(Option.isSome), Effect.tapError((failure) => Effect.logWarning("Could not read the sandbox's drain marker", {
|
|
44
|
+
sandbox: sandbox.name,
|
|
45
|
+
operation: failure.operation,
|
|
46
|
+
message: failure.message,
|
|
47
|
+
})), Effect.orElseSucceed(() => false));
|
|
48
|
+
if (draining)
|
|
49
|
+
yield* Effect.logWarning("The platform is stopping the sandbox's container, which loses its disk", { sandbox: sandbox.name, attempt: sandbox.attempt });
|
|
50
|
+
return sandbox.summary(true, draining);
|
|
51
|
+
});
|
|
52
|
+
/** Returns the stored sandbox, or fails with `SandboxNotFound`. */
|
|
53
|
+
const stored = Effect.fn("Sandbox.stored")(function* (name) {
|
|
54
|
+
const storage = yield* SandboxStorage;
|
|
55
|
+
const found = yield* storage.load;
|
|
56
|
+
if (Option.isNone(found))
|
|
57
|
+
return yield* new SandboxNotFound({ name });
|
|
58
|
+
return found.value;
|
|
59
|
+
});
|
|
60
|
+
/** Returns the stored sandbox if its container runs, or fails with `ContainerUnavailable`. */
|
|
61
|
+
export const runningSandboxFor = Effect.fn("Sandbox.running")(function* (name) {
|
|
62
|
+
const sandbox = yield* stored(name);
|
|
63
|
+
const container = yield* SandboxContainer;
|
|
64
|
+
if (!(yield* container.running))
|
|
65
|
+
return yield* new ContainerUnavailable({
|
|
66
|
+
name,
|
|
67
|
+
message: "The container isn't running, so its disk is gone",
|
|
68
|
+
});
|
|
69
|
+
return sandbox;
|
|
70
|
+
});
|
|
71
|
+
/** Sends the container's traffic for GitHub, the providers, and the allowlisted hosts to the outbound handlers. */
|
|
72
|
+
const intercept = Effect.fn("Sandbox.intercept")(function* (name, credentials) {
|
|
73
|
+
const container = yield* SandboxContainer;
|
|
74
|
+
const { allowedHosts } = yield* SandboxSettings;
|
|
75
|
+
const interceptions = interceptionsFor(credentials, allowedHosts);
|
|
76
|
+
yield* Effect.forEach(interceptions, container.intercept, { discard: true });
|
|
77
|
+
yield* Effect.logInfo("Registered the sandbox's outbound handlers", {
|
|
78
|
+
sandbox: name,
|
|
79
|
+
hosts: interceptions.map(({ _tag, host }) => `${_tag}:${host}`).join(","),
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
/** Fails with `StaleCredentials` if `sandbox` holds credentials issued after `credentials`. */
|
|
83
|
+
const refuseStale = Effect.fn("Sandbox.refuseStale")(function* (sandbox, credentials) {
|
|
84
|
+
const current = sandbox.credentials.issuedAt;
|
|
85
|
+
if (!DateTime.isLessThan(credentials.issuedAt, current))
|
|
86
|
+
return;
|
|
87
|
+
yield* Effect.logWarning("Refused credentials issued before the ones the sandbox holds", {
|
|
88
|
+
sandbox: sandbox.name,
|
|
89
|
+
attempt: sandbox.attempt,
|
|
90
|
+
issuedAt: DateTime.formatIso(credentials.issuedAt),
|
|
91
|
+
current: DateTime.formatIso(current),
|
|
92
|
+
});
|
|
93
|
+
return yield* new StaleCredentials({
|
|
94
|
+
name: sandbox.name,
|
|
95
|
+
issuedAt: credentials.issuedAt,
|
|
96
|
+
current,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
/**
|
|
100
|
+
* Applies `change` to the sandbox as it's stored now, saves the result, and returns it, or fails
|
|
101
|
+
* with `SandboxNotFound` if the sandbox was destroyed.
|
|
102
|
+
*/
|
|
103
|
+
const updateStored = Effect.fn("Sandbox.updateStored")(function* (name, change) {
|
|
104
|
+
const storage = yield* SandboxStorage;
|
|
105
|
+
const updated = yield* storage.modify((stored) => {
|
|
106
|
+
const changed = Option.map(stored, change);
|
|
107
|
+
return [changed, changed];
|
|
108
|
+
});
|
|
109
|
+
if (Option.isNone(updated))
|
|
110
|
+
return yield* new SandboxNotFound({ name });
|
|
111
|
+
return updated.value;
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Extends the sandbox's lease from now and returns the stored sandbox, or fails with
|
|
115
|
+
* `SandboxNotFound` if it was destroyed.
|
|
116
|
+
*/
|
|
117
|
+
export const extendLease = Effect.fn("Sandbox.extendLease")(function* (name) {
|
|
118
|
+
const now = yield* DateTime.now;
|
|
119
|
+
return yield* updateStored(name, (sandbox) => new StoredSandbox({ ...sandbox, leaseUntil: DateTime.addDuration(now, LEASE) }));
|
|
120
|
+
});
|
|
121
|
+
const create = Effect.fn("Sandbox.create")(function* (name, request) {
|
|
122
|
+
const fields = {
|
|
123
|
+
sandbox: name,
|
|
124
|
+
owner: request.owner,
|
|
125
|
+
attempt: request.attempt,
|
|
126
|
+
image: request.image,
|
|
127
|
+
instanceType: request.instanceType,
|
|
128
|
+
};
|
|
129
|
+
yield* Effect.annotateCurrentSpan(fields);
|
|
130
|
+
const container = yield* SandboxContainer;
|
|
131
|
+
const storage = yield* SandboxStorage;
|
|
132
|
+
const directory = yield* SandboxDirectory;
|
|
133
|
+
const existing = yield* storage.load;
|
|
134
|
+
if (Option.isSome(existing) &&
|
|
135
|
+
(existing.value.owner !== request.owner || existing.value.attempt !== request.attempt)) {
|
|
136
|
+
yield* Effect.logWarning("Refused a sandbox that exists for another Attempt", {
|
|
137
|
+
...fields,
|
|
138
|
+
existingOwner: existing.value.owner,
|
|
139
|
+
existingAttempt: existing.value.attempt,
|
|
140
|
+
});
|
|
141
|
+
return yield* new SandboxConflict({
|
|
142
|
+
name,
|
|
143
|
+
owner: existing.value.owner,
|
|
144
|
+
attempt: existing.value.attempt,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (Option.isSome(existing))
|
|
148
|
+
yield* refuseStale(existing.value, request.credentials);
|
|
149
|
+
const images = yield* container.images;
|
|
150
|
+
const image = images[request.image];
|
|
151
|
+
if (image === undefined) {
|
|
152
|
+
yield* Effect.logWarning("Refused a sandbox for an image the Worker doesn't have", {
|
|
153
|
+
...fields,
|
|
154
|
+
images: Object.keys(images).join(","),
|
|
155
|
+
});
|
|
156
|
+
return yield* new ImageNotFound({ image: request.image, images: Object.keys(images) });
|
|
157
|
+
}
|
|
158
|
+
const now = yield* DateTime.now;
|
|
159
|
+
const wasRunning = yield* container.running;
|
|
160
|
+
if (!wasRunning)
|
|
161
|
+
yield* container.start(image, request.instanceType).pipe(Effect.mapError(unavailable(name)));
|
|
162
|
+
yield* container.keepAlive(INACTIVITY_TIMEOUT).pipe(Effect.mapError(unavailable(name)));
|
|
163
|
+
yield* intercept(name, request.credentials).pipe(Effect.mapError(unavailable(name)));
|
|
164
|
+
// Reads the stored sandbox again: a hold may have arrived while the container started.
|
|
165
|
+
const sandbox = yield* storage.modify((current) => {
|
|
166
|
+
const created = new StoredSandbox({
|
|
167
|
+
name,
|
|
168
|
+
owner: request.owner,
|
|
169
|
+
attempt: request.attempt,
|
|
170
|
+
image: request.image,
|
|
171
|
+
instanceType: request.instanceType,
|
|
172
|
+
createdAt: Option.match(current, { onNone: () => now, onSome: ({ createdAt }) => createdAt }),
|
|
173
|
+
leaseUntil: DateTime.addDuration(now, LEASE),
|
|
174
|
+
// A hold lasts until the sandbox is destroyed, through a reattachment.
|
|
175
|
+
held: Option.exists(current, ({ held }) => held),
|
|
176
|
+
credentials: request.credentials,
|
|
177
|
+
});
|
|
178
|
+
return [created, Option.some(created)];
|
|
179
|
+
});
|
|
180
|
+
yield* storage.alarmAt(DateTime.addDuration(now, KEEPALIVE_INTERVAL));
|
|
181
|
+
const summary = yield* summarize(sandbox);
|
|
182
|
+
yield* directory.record(summary);
|
|
183
|
+
yield* Effect.logInfo(Option.isNone(existing)
|
|
184
|
+
? "Created the Attempt's sandbox"
|
|
185
|
+
: wasRunning
|
|
186
|
+
? "The Attempt's sandbox already runs; renewed its credentials"
|
|
187
|
+
: "Restarted the Attempt's sandbox, whose container had stopped", fields);
|
|
188
|
+
return summary;
|
|
189
|
+
});
|
|
190
|
+
const get = Effect.fn("Sandbox.get")(function* (name) {
|
|
191
|
+
yield* Effect.annotateCurrentSpan({ sandbox: name });
|
|
192
|
+
return yield* summarize(yield* stored(name));
|
|
193
|
+
});
|
|
194
|
+
const credentials = Effect.fn("Sandbox.credentials")(function* (name, renewed) {
|
|
195
|
+
yield* Effect.annotateCurrentSpan({ sandbox: name });
|
|
196
|
+
const sandbox = yield* runningSandboxFor(name);
|
|
197
|
+
yield* refuseStale(sandbox, renewed);
|
|
198
|
+
yield* intercept(name, renewed).pipe(Effect.mapError(unavailable(name)));
|
|
199
|
+
const now = yield* DateTime.now;
|
|
200
|
+
const extended = yield* updateStored(name, (current) => new StoredSandbox({
|
|
201
|
+
...current,
|
|
202
|
+
leaseUntil: DateTime.addDuration(now, LEASE),
|
|
203
|
+
credentials: renewed,
|
|
204
|
+
}));
|
|
205
|
+
yield* Effect.logInfo("Renewed the sandbox's credentials", {
|
|
206
|
+
sandbox: name,
|
|
207
|
+
attempt: sandbox.attempt,
|
|
208
|
+
issuedAt: DateTime.formatIso(renewed.issuedAt),
|
|
209
|
+
github: Option.isSome(renewed.github),
|
|
210
|
+
providers: renewed.providers.map(({ provider }) => provider).join(","),
|
|
211
|
+
leaseUntil: DateTime.formatIso(extended.leaseUntil),
|
|
212
|
+
});
|
|
213
|
+
return yield* summarize(extended);
|
|
214
|
+
});
|
|
215
|
+
/**
|
|
216
|
+
* Holds the sandbox: the keepalive alarm keeps its container running past the lease, until the
|
|
217
|
+
* sandbox is destroyed. usine holds a sandbox whose checkout it keeps, so that the checkout
|
|
218
|
+
* survives until usine reconciles it.
|
|
219
|
+
*/
|
|
220
|
+
const hold = Effect.fn("Sandbox.hold")(function* (name) {
|
|
221
|
+
yield* Effect.annotateCurrentSpan({ sandbox: name });
|
|
222
|
+
const sandbox = yield* runningSandboxFor(name);
|
|
223
|
+
const storage = yield* SandboxStorage;
|
|
224
|
+
const now = yield* DateTime.now;
|
|
225
|
+
const held = yield* updateStored(name, (current) => new StoredSandbox({ ...current, held: true }));
|
|
226
|
+
yield* storage.alarmAt(DateTime.addDuration(now, KEEPALIVE_INTERVAL));
|
|
227
|
+
yield* Effect.logInfo(sandbox.held
|
|
228
|
+
? "The sandbox was already held"
|
|
229
|
+
: "Holding the sandbox until usine reconciles or destroys it", { sandbox: name, attempt: sandbox.attempt });
|
|
230
|
+
return yield* summarize(held);
|
|
231
|
+
});
|
|
232
|
+
const stop = Effect.fn("Sandbox.stop")(function* (name) {
|
|
233
|
+
yield* Effect.annotateCurrentSpan({ sandbox: name });
|
|
234
|
+
const sandbox = yield* stored(name);
|
|
235
|
+
const container = yield* SandboxContainer;
|
|
236
|
+
if (!(yield* container.running)) {
|
|
237
|
+
yield* Effect.logInfo("The sandbox's container isn't running; no command to stop", {
|
|
238
|
+
sandbox: name,
|
|
239
|
+
});
|
|
240
|
+
return new Stopped({ stopped: 0 });
|
|
241
|
+
}
|
|
242
|
+
const process = yield* container
|
|
243
|
+
.exec(stopCommand, "closed")
|
|
244
|
+
.pipe(Effect.mapError(unavailable(name)));
|
|
245
|
+
const result = yield* collect(process).pipe(Effect.mapError(unavailable(name)));
|
|
246
|
+
const count = stoppedCount(result.stdout);
|
|
247
|
+
if (result.exitCode !== 0 || Option.isNone(count)) {
|
|
248
|
+
const message = `The stop script exited with code ${String(result.exitCode)}: ${result.stderr.trim()}`;
|
|
249
|
+
yield* Effect.logError("Could not stop the sandbox's commands", {
|
|
250
|
+
sandbox: name,
|
|
251
|
+
attempt: sandbox.attempt,
|
|
252
|
+
message,
|
|
253
|
+
});
|
|
254
|
+
return yield* new ContainerUnavailable({ name, message });
|
|
255
|
+
}
|
|
256
|
+
yield* Effect.logInfo("Stopped the sandbox's commands", {
|
|
257
|
+
sandbox: name,
|
|
258
|
+
attempt: sandbox.attempt,
|
|
259
|
+
stopped: count.value,
|
|
260
|
+
});
|
|
261
|
+
return new Stopped({ stopped: count.value });
|
|
262
|
+
});
|
|
263
|
+
const destroy = Effect.fn("Sandbox.destroy")(function* (name) {
|
|
264
|
+
yield* Effect.annotateCurrentSpan({ sandbox: name });
|
|
265
|
+
const container = yield* SandboxContainer;
|
|
266
|
+
const storage = yield* SandboxStorage;
|
|
267
|
+
const directory = yield* SandboxDirectory;
|
|
268
|
+
const existing = yield* storage.load;
|
|
269
|
+
if (yield* container.running)
|
|
270
|
+
yield* container.destroy.pipe(Effect.mapError(unavailable(name)));
|
|
271
|
+
yield* storage.clear;
|
|
272
|
+
yield* directory.forget(name);
|
|
273
|
+
yield* Effect.logInfo(Option.isSome(existing) ? "Destroyed the sandbox" : "No sandbox to destroy", {
|
|
274
|
+
sandbox: name,
|
|
275
|
+
attempt: Option.getOrUndefined(Option.map(existing, ({ attempt }) => attempt)),
|
|
276
|
+
});
|
|
277
|
+
return new Destroyed({ destroyed: Option.isSome(existing) });
|
|
278
|
+
});
|
|
279
|
+
const processStatus = Effect.fn("Sandbox.process")(function* (name, process) {
|
|
280
|
+
yield* Effect.annotateCurrentSpan({ sandbox: name, process });
|
|
281
|
+
yield* runningSandboxFor(name);
|
|
282
|
+
const container = yield* SandboxContainer;
|
|
283
|
+
const files = processFiles(process);
|
|
284
|
+
const read = (path) => container
|
|
285
|
+
.read(path)
|
|
286
|
+
.pipe(Effect.mapError(unavailable(name)), Effect.map(Option.map((bytes) => decoder.decode(bytes))));
|
|
287
|
+
const pid = Option.flatMap(yield* read(files.pid), pidOf);
|
|
288
|
+
if (Option.isNone(pid))
|
|
289
|
+
return yield* new ProcessNotFound({ name, process });
|
|
290
|
+
const exitCode = Option.flatMap(yield* read(files.exit), exitCodeOf);
|
|
291
|
+
const size = (path) => container
|
|
292
|
+
.size(path)
|
|
293
|
+
.pipe(Effect.mapError(unavailable(name)), Effect.map(Option.getOrElse(() => 0)));
|
|
294
|
+
return new ProcessStatus({
|
|
295
|
+
process,
|
|
296
|
+
pid: pid.value,
|
|
297
|
+
state: Option.match(exitCode, {
|
|
298
|
+
onNone: () => new ProcessRunning(),
|
|
299
|
+
onSome: (code) => new ProcessExited({ exitCode: code }),
|
|
300
|
+
}),
|
|
301
|
+
stdoutBytes: yield* size(files.stdout),
|
|
302
|
+
stderrBytes: yield* size(files.stderr),
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
const isImageName = Schema.is(CloudflareImageName);
|
|
306
|
+
const health = Effect.fn("Sandbox.health")(function* () {
|
|
307
|
+
const container = yield* SandboxContainer;
|
|
308
|
+
const images = Object.keys(yield* container.images).filter(isImageName);
|
|
309
|
+
return new HealthReport({ protocol: SANDBOX_PROTOCOL, images });
|
|
310
|
+
});
|
|
311
|
+
/**
|
|
312
|
+
* Keeps the container running while the sandbox's lease lasts or usine holds it, then lets the
|
|
313
|
+
* platform stop it after `INACTIVITY_TIMEOUT`. Runs from the Durable Object's alarm, and sets the
|
|
314
|
+
* next one.
|
|
315
|
+
*/
|
|
316
|
+
export const keepAlive = Effect.fn("Sandbox.keepAlive")(function* () {
|
|
317
|
+
const storage = yield* SandboxStorage;
|
|
318
|
+
const container = yield* SandboxContainer;
|
|
319
|
+
const found = yield* storage.load;
|
|
320
|
+
if (Option.isNone(found)) {
|
|
321
|
+
yield* Effect.logDebug("The keepalive alarm found no sandbox; it isn't set again");
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const sandbox = found.value;
|
|
325
|
+
const fields = { sandbox: sandbox.name, attempt: sandbox.attempt };
|
|
326
|
+
const now = yield* DateTime.now;
|
|
327
|
+
if (!sandbox.held && DateTime.isGreaterThan(now, sandbox.leaseUntil)) {
|
|
328
|
+
yield* Effect.logWarning("The sandbox's lease expired without a request from usine; the platform stops its container after the inactivity timeout", { ...fields, leaseUntil: DateTime.formatIso(sandbox.leaseUntil) });
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (!(yield* container.running)) {
|
|
332
|
+
yield* Effect.logInfo("The sandbox's container stopped; the keepalive alarm ends", fields);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
yield* container.keepAlive(INACTIVITY_TIMEOUT).pipe(Effect.tapError((failure) => Effect.logWarning("Could not renew the container's inactivity timeout", {
|
|
336
|
+
...fields,
|
|
337
|
+
operation: failure.operation,
|
|
338
|
+
message: failure.message,
|
|
339
|
+
})), Effect.ignore);
|
|
340
|
+
yield* storage.alarmAt(DateTime.addDuration(now, KEEPALIVE_INTERVAL));
|
|
341
|
+
});
|
|
342
|
+
/**
|
|
343
|
+
* Registers the outbound handlers and sets the inactivity timeout again after the Durable Object
|
|
344
|
+
* restarts while its container runs, since the platform resets both for each instance. Does
|
|
345
|
+
* nothing for a sandbox without a running container.
|
|
346
|
+
*/
|
|
347
|
+
export const restore = Effect.fn("Sandbox.restore")(function* () {
|
|
348
|
+
const storage = yield* SandboxStorage;
|
|
349
|
+
const container = yield* SandboxContainer;
|
|
350
|
+
const found = yield* storage.load;
|
|
351
|
+
if (Option.isNone(found) || !(yield* container.running))
|
|
352
|
+
return;
|
|
353
|
+
const fields = { sandbox: found.value.name, attempt: found.value.attempt };
|
|
354
|
+
yield* container.keepAlive(INACTIVITY_TIMEOUT).pipe(Effect.tapError((failure) => Effect.logError("Could not set the container's inactivity timeout again", {
|
|
355
|
+
...fields,
|
|
356
|
+
operation: failure.operation,
|
|
357
|
+
message: failure.message,
|
|
358
|
+
})), Effect.ignore);
|
|
359
|
+
yield* intercept(found.value.name, found.value.credentials).pipe(Effect.tapError((failure) => Effect.logError("Could not register the sandbox's outbound handlers again", {
|
|
360
|
+
...fields,
|
|
361
|
+
operation: failure.operation,
|
|
362
|
+
message: failure.message,
|
|
363
|
+
})), Effect.ignore);
|
|
364
|
+
yield* Effect.logInfo("Restored the running sandbox after its Durable Object restarted", fields);
|
|
365
|
+
});
|
|
366
|
+
const SandboxServedApi = HttpApi.make("SandboxApi").add(healthGroup, sandboxGroup);
|
|
367
|
+
const healthHandlers = HttpApiBuilder.group(SandboxServedApi, "health", (routes) => routes.handle("health", () => health()));
|
|
368
|
+
const sandboxHandlers = HttpApiBuilder.group(SandboxServedApi, "sandbox", (routes) => Effect.gen(function* () {
|
|
369
|
+
// A create or credential change registers handlers, then stores their credentials. One runs
|
|
370
|
+
// at a time, so another change's handlers never replace them in between.
|
|
371
|
+
const credentialChanges = yield* Semaphore.make(1);
|
|
372
|
+
const oneAtATime = credentialChanges.withPermits(1);
|
|
373
|
+
return routes
|
|
374
|
+
.handle("create", ({ params, payload }) => oneAtATime(create(params.name, payload)))
|
|
375
|
+
.handle("get", ({ params }) => get(params.name))
|
|
376
|
+
.handle("credentials", ({ params, payload }) => oneAtATime(credentials(params.name, payload)))
|
|
377
|
+
.handle("hold", ({ params }) => hold(params.name))
|
|
378
|
+
.handle("stop", ({ params }) => stop(params.name))
|
|
379
|
+
.handle("destroy", ({ params }) => destroy(params.name))
|
|
380
|
+
.handle("process", ({ params }) => processStatus(params.name, params.process));
|
|
381
|
+
}));
|
|
382
|
+
/**
|
|
383
|
+
* The health and sandbox routes of `SandboxApi`, served by each sandbox's Durable Object. Requires
|
|
384
|
+
* `SandboxContainer`, `SandboxStorage`, `SandboxDirectory`, and `SandboxSettings`.
|
|
385
|
+
*/
|
|
386
|
+
export const sandboxRoutes = HttpApiBuilder.layer(SandboxServedApi).pipe(Layer.provide([healthHandlers, sandboxHandlers]), Layer.provide(HttpServer.layerServices));
|
|
387
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/sandbox/sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Cause, Effect, Queue } from "effect";
|
|
2
|
+
import { SandboxContainer } from "./ports.ts";
|
|
3
|
+
/** A WebSocket that the Durable Object accepted. */
|
|
4
|
+
export interface StreamSocket {
|
|
5
|
+
/** Sends a message. Does nothing once the socket is closed. */
|
|
6
|
+
readonly send: (message: string | Uint8Array) => Effect.Effect<void>;
|
|
7
|
+
readonly close: (code: number, reason: string) => Effect.Effect<void>;
|
|
8
|
+
/** The client's messages. Ends when the client closes the socket. */
|
|
9
|
+
readonly messages: Queue.Dequeue<string | Uint8Array, Cause.Done>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Runs one command for the client on `socket`: the first message is `ExecStart`, then binary
|
|
13
|
+
* messages are its stdin, and the sandbox sends `Started`, the output as binary messages, and
|
|
14
|
+
* `Exited`. The command runs through `PROCESS_SCRIPT`, so it keeps running, and its output keeps
|
|
15
|
+
* going to its process files, if the client disconnects.
|
|
16
|
+
*/
|
|
17
|
+
export declare const execStream: (name: string & import("effect/Brand").Brand<"SandboxName">, socket: StreamSocket) => Effect.Effect<void, never, SandboxContainer | import("./ports.ts").SandboxStorage>;
|
|
18
|
+
/**
|
|
19
|
+
* Streams a command's output from its process files, after the bytes in `offsets`, then its exit
|
|
20
|
+
* code, for a client whose exec stream was cut. Nothing reaches the command's stdin.
|
|
21
|
+
*/
|
|
22
|
+
export declare const attachStream: (name: string & import("effect/Brand").Brand<"SandboxName">, process: string & import("effect/Brand").Brand<"ProcessId">, offsets: {
|
|
23
|
+
readonly stdout: number;
|
|
24
|
+
readonly stderr: number;
|
|
25
|
+
}, socket: StreamSocket) => Effect.Effect<void, never, SandboxContainer | import("./ports.ts").SandboxStorage>;
|
|
26
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/sandbox/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAY,MAAM,EAAiB,KAAK,EAAkB,MAAM,QAAQ,CAAC;AAY5F,OAAO,EAAoB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAIhE,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtE,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;CACnE;AAqED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,0KA8ErB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY;;;8GA4DvB,CAAC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Duration, Effect, Fiber, Option, Queue, Result, Stream } from "effect";
|
|
2
|
+
import { decodeClientMessage, encodeServerMessage, Exited, outputFrame, Started, StreamFailed, } from "../api/stream.js";
|
|
3
|
+
import { ContainerFailure, SandboxContainer } from "./ports.js";
|
|
4
|
+
import { exitCodeOf, newProcessId, processCommand, processFiles } from "./process.js";
|
|
5
|
+
import { extendLease, runningSandboxFor } from "./sandbox.js";
|
|
6
|
+
/** The time a client has to send its first message. */
|
|
7
|
+
const START_TIMEOUT = Duration.seconds(30);
|
|
8
|
+
/** The interval at which an attach stream reads the process files again. */
|
|
9
|
+
const ATTACH_POLL = Duration.millis(500);
|
|
10
|
+
/** The maximum bytes in one binary message. */
|
|
11
|
+
const FRAME_BYTES = 64 * 1024;
|
|
12
|
+
const decoder = new TextDecoder();
|
|
13
|
+
/** Sends `message` as a stream failure and closes the socket. */
|
|
14
|
+
const refuse = (socket, message) => socket
|
|
15
|
+
.send(encodeServerMessage(new StreamFailed({ message })))
|
|
16
|
+
.pipe(Effect.andThen(socket.close(1011, "stream failed")));
|
|
17
|
+
/** Sends `bytes` of `channel` in messages of at most `FRAME_BYTES`. */
|
|
18
|
+
const sendOutput = (socket, channel, bytes) => Effect.forEach(Array.from({ length: Math.ceil(bytes.length / FRAME_BYTES) }, (_, index) => bytes.subarray(index * FRAME_BYTES, (index + 1) * FRAME_BYTES)), (chunk) => socket.send(outputFrame(channel, chunk)), { discard: true });
|
|
19
|
+
const pump = (socket, channel, stream) => stream === null
|
|
20
|
+
? Effect.void
|
|
21
|
+
: Stream.fromReadableStream({
|
|
22
|
+
evaluate: () => stream,
|
|
23
|
+
onError: (cause) => new ContainerFailure({
|
|
24
|
+
operation: "exec",
|
|
25
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
26
|
+
}),
|
|
27
|
+
}).pipe(Stream.runForEach((bytes) => sendOutput(socket, channel, bytes)));
|
|
28
|
+
/**
|
|
29
|
+
* Writes the client's binary messages to `stdin`, and closes it on `StdinEnd` or when the client
|
|
30
|
+
* closes the socket, as a dropped SSH connection closes a remote command's stdin.
|
|
31
|
+
*/
|
|
32
|
+
const feed = (socket, stdin) => Effect.gen(function* () {
|
|
33
|
+
if (stdin === null)
|
|
34
|
+
return;
|
|
35
|
+
const writer = stdin.getWriter();
|
|
36
|
+
const close = Effect.promise(() => writer.close().catch(() => undefined));
|
|
37
|
+
yield* Stream.fromQueue(socket.messages).pipe(Stream.takeWhile((message) => {
|
|
38
|
+
if (message instanceof Uint8Array)
|
|
39
|
+
return true;
|
|
40
|
+
return Option.match(decodeClientMessage(message), {
|
|
41
|
+
onNone: () => true,
|
|
42
|
+
onSome: (decoded) => decoded._tag !== "StdinEnd",
|
|
43
|
+
});
|
|
44
|
+
}), Stream.runForEach((message) => message instanceof Uint8Array
|
|
45
|
+
? Effect.promise(() => writer.write(message).catch(() => undefined))
|
|
46
|
+
: Effect.void), Effect.ensuring(close));
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* Runs one command for the client on `socket`: the first message is `ExecStart`, then binary
|
|
50
|
+
* messages are its stdin, and the sandbox sends `Started`, the output as binary messages, and
|
|
51
|
+
* `Exited`. The command runs through `PROCESS_SCRIPT`, so it keeps running, and its output keeps
|
|
52
|
+
* going to its process files, if the client disconnects.
|
|
53
|
+
*/
|
|
54
|
+
export const execStream = Effect.fn("Sandbox.execStream")(function* (name, socket) {
|
|
55
|
+
yield* Effect.annotateCurrentSpan({ sandbox: name });
|
|
56
|
+
const first = yield* Queue.take(socket.messages).pipe(Effect.timeoutOption(START_TIMEOUT), Effect.catchTag("Done", () => Effect.succeedNone));
|
|
57
|
+
const start = Option.flatMap(first, (message) => message instanceof Uint8Array
|
|
58
|
+
? Option.none()
|
|
59
|
+
: Option.filter(decodeClientMessage(message), (decoded) => decoded._tag === "ExecStart"));
|
|
60
|
+
if (Option.isNone(start) || start.value._tag !== "ExecStart") {
|
|
61
|
+
yield* Effect.logWarning("Refused an exec stream that didn't start a command", {
|
|
62
|
+
sandbox: name,
|
|
63
|
+
});
|
|
64
|
+
return yield* refuse(socket, "The first message must be ExecStart");
|
|
65
|
+
}
|
|
66
|
+
const command = start.value.command;
|
|
67
|
+
const sandbox = yield* runningSandboxFor(name).pipe(Effect.andThen(extendLease(name)), Effect.option);
|
|
68
|
+
if (Option.isNone(sandbox)) {
|
|
69
|
+
yield* Effect.logWarning("Refused a command for a sandbox without a running container", {
|
|
70
|
+
sandbox: name,
|
|
71
|
+
});
|
|
72
|
+
return yield* refuse(socket, `Sandbox ${name} doesn't exist or its container isn't running`);
|
|
73
|
+
}
|
|
74
|
+
const container = yield* SandboxContainer;
|
|
75
|
+
const process = newProcessId();
|
|
76
|
+
const fields = { sandbox: name, attempt: sandbox.value.attempt, process };
|
|
77
|
+
const started = yield* container
|
|
78
|
+
.exec(processCommand(process, command), "pipe")
|
|
79
|
+
.pipe(Effect.option);
|
|
80
|
+
if (Option.isNone(started)) {
|
|
81
|
+
yield* Effect.logError("The container didn't start a command", fields);
|
|
82
|
+
return yield* refuse(socket, "The container didn't start the command");
|
|
83
|
+
}
|
|
84
|
+
const handle = started.value;
|
|
85
|
+
yield* socket.send(encodeServerMessage(new Started({ process })));
|
|
86
|
+
yield* Effect.logInfo("Started a command in the sandbox", {
|
|
87
|
+
...fields,
|
|
88
|
+
commandBytes: command.length,
|
|
89
|
+
});
|
|
90
|
+
const input = yield* Effect.forkChild(feed(socket, handle.stdin));
|
|
91
|
+
const output = yield* Effect.all([pump(socket, "stdout", handle.stdout), pump(socket, "stderr", handle.stderr)], { concurrency: 2, discard: true }).pipe(Effect.forkChild);
|
|
92
|
+
const exitCode = yield* Fiber.join(output).pipe(Effect.andThen(Effect.tryPromise({
|
|
93
|
+
try: () => handle.exitCode,
|
|
94
|
+
catch: (cause) => new ContainerFailure({
|
|
95
|
+
operation: "exec",
|
|
96
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
97
|
+
}),
|
|
98
|
+
})), Effect.result);
|
|
99
|
+
yield* Fiber.interrupt(input);
|
|
100
|
+
if (Result.isFailure(exitCode)) {
|
|
101
|
+
// Closes without `Exited`, as a dropped connection does: the client then attaches to the
|
|
102
|
+
// process files from the bytes it received, and gets the rest of the output and the exit code.
|
|
103
|
+
yield* Effect.logError("Lost the command's output or exit code; the client attaches", {
|
|
104
|
+
...fields,
|
|
105
|
+
error: exitCode.failure.message,
|
|
106
|
+
});
|
|
107
|
+
return yield* socket.close(1011, "container stream failed");
|
|
108
|
+
}
|
|
109
|
+
yield* socket.send(encodeServerMessage(new Exited({ exitCode: exitCode.success })));
|
|
110
|
+
yield* socket.close(1000, "exited");
|
|
111
|
+
yield* Effect.logInfo("The sandbox's command exited", { ...fields, exitCode: exitCode.success });
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Streams a command's output from its process files, after the bytes in `offsets`, then its exit
|
|
115
|
+
* code, for a client whose exec stream was cut. Nothing reaches the command's stdin.
|
|
116
|
+
*/
|
|
117
|
+
export const attachStream = Effect.fn("Sandbox.attachStream")(function* (name, process, offsets, socket) {
|
|
118
|
+
const fields = { sandbox: name, process };
|
|
119
|
+
yield* Effect.annotateCurrentSpan(fields);
|
|
120
|
+
const sandbox = yield* runningSandboxFor(name).pipe(Effect.option);
|
|
121
|
+
if (Option.isNone(sandbox))
|
|
122
|
+
return yield* refuse(socket, `Sandbox ${name} isn't running`);
|
|
123
|
+
const container = yield* SandboxContainer;
|
|
124
|
+
const files = processFiles(process);
|
|
125
|
+
const known = yield* container.read(files.pid).pipe(Effect.option);
|
|
126
|
+
if (Option.isNone(Option.flatten(known))) {
|
|
127
|
+
yield* Effect.logWarning("Refused to attach to an unknown command", fields);
|
|
128
|
+
return yield* refuse(socket, `Sandbox ${name} has no command ${process}`);
|
|
129
|
+
}
|
|
130
|
+
if (Option.isNone(yield* extendLease(name).pipe(Effect.option))) {
|
|
131
|
+
yield* Effect.logWarning("Refused to attach: the sandbox was destroyed", fields);
|
|
132
|
+
return yield* refuse(socket, `Sandbox ${name} was destroyed`);
|
|
133
|
+
}
|
|
134
|
+
yield* Effect.logInfo("Attached to a command's output", { ...fields, ...offsets });
|
|
135
|
+
let sent = { stdout: offsets.stdout, stderr: offsets.stderr };
|
|
136
|
+
const forward = (channel) => container.readFrom(files[channel], sent[channel]).pipe(Effect.flatMap((bytes) => {
|
|
137
|
+
const read = Option.getOrElse(bytes, () => new Uint8Array());
|
|
138
|
+
sent = { ...sent, [channel]: sent[channel] + read.length };
|
|
139
|
+
return sendOutput(socket, channel, read);
|
|
140
|
+
}));
|
|
141
|
+
const follow = Effect.gen(function* () {
|
|
142
|
+
while (true) {
|
|
143
|
+
// Reads the exit file first, so the output read after it is complete when it exists.
|
|
144
|
+
const exit = Option.flatMap(yield* container.read(files.exit), (bytes) => exitCodeOf(decoder.decode(bytes)));
|
|
145
|
+
yield* forward("stdout");
|
|
146
|
+
yield* forward("stderr");
|
|
147
|
+
if (Option.isSome(exit))
|
|
148
|
+
return exit.value;
|
|
149
|
+
yield* Effect.sleep(ATTACH_POLL);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
const disconnected = Stream.fromQueue(socket.messages).pipe(Stream.runDrain);
|
|
153
|
+
const outcome = yield* Effect.raceFirst(follow.pipe(Effect.map(Option.some)), disconnected.pipe(Effect.as(Option.none()))).pipe(Effect.option);
|
|
154
|
+
const exitCode = Option.flatten(outcome);
|
|
155
|
+
if (Option.isNone(outcome)) {
|
|
156
|
+
yield* Effect.logError("Could not read the command's process files", fields);
|
|
157
|
+
return yield* refuse(socket, "The container failed to read the command's process files");
|
|
158
|
+
}
|
|
159
|
+
if (Option.isNone(exitCode)) {
|
|
160
|
+
yield* Effect.logInfo("The client left the attach stream", fields);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
yield* socket.send(encodeServerMessage(new Exited({ exitCode: exitCode.value })));
|
|
164
|
+
yield* socket.close(1000, "exited");
|
|
165
|
+
yield* Effect.logInfo("The attached command exited", { ...fields, exitCode: exitCode.value });
|
|
166
|
+
});
|
|
167
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/sandbox/stream.ts"],"names":[],"mappings":"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"}
|