@telorun/k8s-runner 0.10.2 → 0.11.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/README.md +197 -0
- package/dist/capabilities.d.ts +5 -0
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +9 -1
- package/dist/capabilities.js.map +1 -1
- package/dist/k8s/backend.d.ts +1 -8
- package/dist/k8s/backend.d.ts.map +1 -1
- package/dist/k8s/backend.js +40 -152
- package/dist/k8s/backend.js.map +1 -1
- package/dist/k8s/pod-spec.d.ts +56 -1
- package/dist/k8s/pod-spec.d.ts.map +1 -1
- package/dist/k8s/pod-spec.js +275 -0
- package/dist/k8s/pod-spec.js.map +1 -1
- package/dist/k8s/pod-status.d.ts +28 -0
- package/dist/k8s/pod-status.d.ts.map +1 -0
- package/dist/k8s/pod-status.js +137 -0
- package/dist/k8s/pod-status.js.map +1 -0
- package/dist/k8s/watch-session.d.ts +19 -0
- package/dist/k8s/watch-session.d.ts.map +1 -0
- package/dist/k8s/watch-session.js +565 -0
- package/dist/k8s/watch-session.js.map +1 -0
- package/dist/k8s/workspace-configmap.d.ts +24 -0
- package/dist/k8s/workspace-configmap.d.ts.map +1 -0
- package/dist/k8s/workspace-configmap.js +124 -0
- package/dist/k8s/workspace-configmap.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +11 -1
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/src/capabilities.ts +14 -1
- package/src/k8s/backend.ts +54 -156
- package/src/k8s/pod-spec.ts +316 -2
- package/src/k8s/pod-status.ts +143 -0
- package/src/k8s/watch-pod-spec.test.ts +195 -0
- package/src/k8s/watch-session.ts +662 -0
- package/src/k8s/workspace-configmap.ts +133 -0
- package/src/server.ts +11 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pod-status.js","sourceRoot":"","sources":["../../src/k8s/pod-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BackendSession, BackendStartSpec } from "@telorun/runner-core";
|
|
2
|
+
import type { K8sRunnerConfig } from "../config.js";
|
|
3
|
+
import type { KubeClient } from "./client.js";
|
|
4
|
+
export interface WatchSessionDeps {
|
|
5
|
+
kube: KubeClient;
|
|
6
|
+
config: K8sRunnerConfig;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A watch session: one pod, one workspace volume, one container per running
|
|
10
|
+
* application, and the session outliving every run inside it.
|
|
11
|
+
*
|
|
12
|
+
* What it does NOT do is as load-bearing as what it does: it never builds an
|
|
13
|
+
* image. The build path exists to put a dependency closure on disk before boot;
|
|
14
|
+
* a watch session resolves its own into a per-app cache directory that lives as
|
|
15
|
+
* long as the pod, so the download happens once per app per session and every
|
|
16
|
+
* later reload resolves from local disk.
|
|
17
|
+
*/
|
|
18
|
+
export declare function startWatchSession(deps: WatchSessionDeps, spec: BackendStartSpec): Promise<BackendSession>;
|
|
19
|
+
//# sourceMappingURL=watch-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-session.d.ts","sourceRoot":"","sources":["../../src/k8s/watch-session.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,cAAc,EACd,gBAAgB,EAKjB,MAAM,sBAAsB,CAAC;AAW9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsB9C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,cAAc,CAAC,CA8iBzB"}
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
import { PassThrough, Writable } from "node:stream";
|
|
2
|
+
import { portKey, portsResolvedFrom, relayDebugStream, SessionStartError, watchReachability, workspaceMarkerWrite, WorkspaceClient, } from "@telorun/runner-core";
|
|
3
|
+
import { clampLimits } from "../limits.js";
|
|
4
|
+
import { buildSessionIngress, buildSessionService, endpointsFor } from "./ingress.js";
|
|
5
|
+
import { buildWatchPod, inspectPortFor, WORKSPACE_PORT } from "./pod-spec.js";
|
|
6
|
+
import { deletePod, is404, msg, podPhase, podStatus, provisionMessage } from "./pod-status.js";
|
|
7
|
+
import { ensureWorkspaceConfigMap } from "./workspace-configmap.js";
|
|
8
|
+
/** How long the pod may take to reach Running before the start is abandoned.
|
|
9
|
+
* `activeDeadlineSeconds` only bounds an already-running pod. */
|
|
10
|
+
const START_DEADLINE_MS = 180_000;
|
|
11
|
+
const WATCH_REARM_DELAY_MS = 2_000;
|
|
12
|
+
/** How long to wait for the workspace container's HTTP surface to answer before
|
|
13
|
+
* giving up on the seed. It is a kernel boot plus a module resolve, so it is
|
|
14
|
+
* slower than the pod reaching Running. */
|
|
15
|
+
const WORKSPACE_READY_TIMEOUT_MS = 120_000;
|
|
16
|
+
const WORKSPACE_POLL_MS = 500;
|
|
17
|
+
const RESIZE_CHANNEL = 4;
|
|
18
|
+
/**
|
|
19
|
+
* A watch session: one pod, one workspace volume, one container per running
|
|
20
|
+
* application, and the session outliving every run inside it.
|
|
21
|
+
*
|
|
22
|
+
* What it does NOT do is as load-bearing as what it does: it never builds an
|
|
23
|
+
* image. The build path exists to put a dependency closure on disk before boot;
|
|
24
|
+
* a watch session resolves its own into a per-app cache directory that lives as
|
|
25
|
+
* long as the pod, so the download happens once per app per session and every
|
|
26
|
+
* later reload resolves from local disk.
|
|
27
|
+
*/
|
|
28
|
+
export async function startWatchSession(deps, spec) {
|
|
29
|
+
const { kube, config } = deps;
|
|
30
|
+
const ns = config.sessionNamespace;
|
|
31
|
+
const limits = clampLimits(config.appLimits, undefined);
|
|
32
|
+
let apps = spec.apps;
|
|
33
|
+
let podName = freshPodName(spec.sessionId);
|
|
34
|
+
let userStopped = false;
|
|
35
|
+
const workspaceAppConfigMap = await ensureWorkspaceConfigMap(kube, config);
|
|
36
|
+
let runtime = null;
|
|
37
|
+
let resolveDone;
|
|
38
|
+
const done = new Promise((r) => (resolveDone = r));
|
|
39
|
+
let settled = false;
|
|
40
|
+
const settle = (status) => {
|
|
41
|
+
if (settled)
|
|
42
|
+
return;
|
|
43
|
+
settled = true;
|
|
44
|
+
spec.onStatus(status);
|
|
45
|
+
resolveDone();
|
|
46
|
+
};
|
|
47
|
+
async function bringUp(seed) {
|
|
48
|
+
const pod = buildWatchPod({
|
|
49
|
+
config,
|
|
50
|
+
sessionId: spec.sessionId,
|
|
51
|
+
podName,
|
|
52
|
+
env: spec.env,
|
|
53
|
+
apps,
|
|
54
|
+
agent: spec.agent,
|
|
55
|
+
limits,
|
|
56
|
+
image: spec.config.image || config.defaultImage,
|
|
57
|
+
pullPolicy: spec.config.pullPolicy,
|
|
58
|
+
workspaceAppConfigMap,
|
|
59
|
+
});
|
|
60
|
+
let created;
|
|
61
|
+
try {
|
|
62
|
+
created = await kube.core.createNamespacedPod({ namespace: ns, body: pod });
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
throw new SessionStartError("start_failed", "create", `failed to create pod: ${msg(err)}`, msg(err));
|
|
66
|
+
}
|
|
67
|
+
const abort = new AbortController();
|
|
68
|
+
const ip = await waitForRunning(podName, abort);
|
|
69
|
+
const workspace = new WorkspaceClient(`http://${ip}:${WORKSPACE_PORT}`);
|
|
70
|
+
await waitForWorkspace(workspace, abort.signal);
|
|
71
|
+
// The bundle reaches the volume through the SAME surface every later write
|
|
72
|
+
// takes — which is what lets the body-fetch init container and the tokenized
|
|
73
|
+
// tarball go away. App containers wait for their entry manifest to exist, so
|
|
74
|
+
// none of them has failed a load in the meantime.
|
|
75
|
+
if (seed) {
|
|
76
|
+
await workspace.apply({
|
|
77
|
+
write: [
|
|
78
|
+
...spec.bundle.files.map((f) => ({
|
|
79
|
+
path: f.relativePath,
|
|
80
|
+
content: f.contents,
|
|
81
|
+
encoding: f.encoding ?? "utf8",
|
|
82
|
+
})),
|
|
83
|
+
// The workspace-root marker, so every app in this session anchors its
|
|
84
|
+
// module cache at ONE place instead of one per entry directory.
|
|
85
|
+
...workspaceMarkerWrite(spec.bundle),
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
endedApps.clear();
|
|
90
|
+
runtime = {
|
|
91
|
+
name: podName,
|
|
92
|
+
uid: created.metadata?.uid ?? "",
|
|
93
|
+
ip,
|
|
94
|
+
workspace,
|
|
95
|
+
sockets: new Map(),
|
|
96
|
+
stdins: new Map(),
|
|
97
|
+
abort,
|
|
98
|
+
stopWatch: () => { },
|
|
99
|
+
};
|
|
100
|
+
await attachApps(runtime);
|
|
101
|
+
await publishEndpoints(runtime, apps);
|
|
102
|
+
relayDebug(runtime);
|
|
103
|
+
watchPorts(runtime);
|
|
104
|
+
armPodWatch(runtime);
|
|
105
|
+
spec.onStatus({
|
|
106
|
+
kind: "running",
|
|
107
|
+
endpoints: endpointsFor(config, spec.sessionId, allPorts(apps)),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/** Resolve once the pod is Running, streaming provisioning messages meanwhile.
|
|
111
|
+
* Rejects with the pod's own failure detail rather than a timeout when the
|
|
112
|
+
* pod itself failed — the two send a reader to different places. */
|
|
113
|
+
async function waitForRunning(name, abort) {
|
|
114
|
+
let lastProvision;
|
|
115
|
+
const deadline = Date.now() + START_DEADLINE_MS;
|
|
116
|
+
for (;;) {
|
|
117
|
+
if (abort.signal.aborted)
|
|
118
|
+
throw new Error("session stopped while the pod was coming up");
|
|
119
|
+
let current;
|
|
120
|
+
try {
|
|
121
|
+
current = await kube.core.readNamespacedPod({ name, namespace: ns });
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
if (is404(err))
|
|
125
|
+
throw new Error("pod disappeared before reaching Running");
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
const phase = podPhase(current);
|
|
129
|
+
if (phase === "Running") {
|
|
130
|
+
const ip = podStatus(current)?.podIP;
|
|
131
|
+
if (ip)
|
|
132
|
+
return ip;
|
|
133
|
+
}
|
|
134
|
+
else if (phase === "Failed" || phase === "Succeeded") {
|
|
135
|
+
throw new Error(`pod reached ${phase} before serving`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const provision = provisionMessage(current);
|
|
139
|
+
if (provision && provision !== lastProvision) {
|
|
140
|
+
lastProvision = provision;
|
|
141
|
+
spec.onProgress("provision", provision);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (Date.now() > deadline) {
|
|
145
|
+
throw new Error("pod did not reach Running within the start deadline");
|
|
146
|
+
}
|
|
147
|
+
await sleep(WORKSPACE_POLL_MS, abort.signal);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** The workspace container is a kernel boot plus a module resolve, so being
|
|
151
|
+
* Running is not being ready. Every write — the seed above all — depends on
|
|
152
|
+
* it, so this waits rather than letting the first write fail. */
|
|
153
|
+
async function waitForWorkspace(client, signal) {
|
|
154
|
+
const deadline = Date.now() + WORKSPACE_READY_TIMEOUT_MS;
|
|
155
|
+
spec.onProgress("boot", "Starting workspace");
|
|
156
|
+
let lastError = "no response";
|
|
157
|
+
for (;;) {
|
|
158
|
+
if (signal.aborted)
|
|
159
|
+
throw new Error("session stopped while the workspace was coming up");
|
|
160
|
+
try {
|
|
161
|
+
await client.tree();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
lastError = msg(err);
|
|
166
|
+
}
|
|
167
|
+
if (Date.now() > deadline) {
|
|
168
|
+
throw new SessionStartError("start_failed", "start", `workspace container did not become ready: ${lastError}`, lastError);
|
|
169
|
+
}
|
|
170
|
+
await sleep(WORKSPACE_POLL_MS, signal);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** One attach per app container: each has its own terminal, so the byte
|
|
174
|
+
* channel is keyed `(session, app)` rather than labelled on a merged stream. */
|
|
175
|
+
async function attachApps(rt) {
|
|
176
|
+
for (const app of apps) {
|
|
177
|
+
const stdin = new PassThrough();
|
|
178
|
+
const stdout = new Writable({
|
|
179
|
+
write(chunk, encoding, cb) {
|
|
180
|
+
if (chunk?.byteLength)
|
|
181
|
+
spec.onOutput(app.name, Buffer.from(chunk), tagFor(app, "stdout"));
|
|
182
|
+
cb();
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
// Under `io: "streams"` the kubernetes attach subresource gives separate
|
|
186
|
+
// stdout and stderr channels — the demux is already there, and a TTY is
|
|
187
|
+
// what collapses it. So the split is real, not a label.
|
|
188
|
+
const stderr = app.io === "streams"
|
|
189
|
+
? new Writable({
|
|
190
|
+
write(chunk, encoding, cb) {
|
|
191
|
+
if (chunk?.byteLength)
|
|
192
|
+
spec.onOutput(app.name, Buffer.from(chunk), "stderr");
|
|
193
|
+
cb();
|
|
194
|
+
},
|
|
195
|
+
})
|
|
196
|
+
: null;
|
|
197
|
+
try {
|
|
198
|
+
const ws = await kube.attach.attach(ns, rt.name, `app-${app.name}`, stdout, stderr, stdin, app.io === "tty");
|
|
199
|
+
rt.sockets.set(app.name, ws);
|
|
200
|
+
rt.stdins.set(app.name, stdin);
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
// A degraded terminal is not a failed session — status, run events and
|
|
204
|
+
// the workspace all still work — so it is reported on that app's own
|
|
205
|
+
// channel rather than aborting the start.
|
|
206
|
+
spec.onOutput(app.name, Buffer.from(`\r\n[runner] failed to attach: ${msg(err)}\r\n`), tagFor(app, "stderr"));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function relayDebug(rt) {
|
|
211
|
+
apps.forEach((app, index) => {
|
|
212
|
+
void relayDebugStream({
|
|
213
|
+
url: `http://${rt.ip}:${inspectPortFor(index)}/events`,
|
|
214
|
+
onFrame: (frame) => {
|
|
215
|
+
void applyPortsResolved(app.name, frame);
|
|
216
|
+
spec.onDebug(app.name, frame);
|
|
217
|
+
},
|
|
218
|
+
signal: rt.abort.signal,
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Re-route an app whose declared port set changed on reload.
|
|
224
|
+
*
|
|
225
|
+
* The kernel re-resolves its `ports:` block on every load and says so on the
|
|
226
|
+
* stream the runner is already reading, so nothing here parses a manifest —
|
|
227
|
+
* which matters, because a manifest the runner could not parse would otherwise
|
|
228
|
+
* have to leave routing alone and report, on the hot path of every save.
|
|
229
|
+
*
|
|
230
|
+
* A pod's `containerPort` list is documentation; the Service and the Ingress
|
|
231
|
+
* are what make a port reachable, so this needs no pod recreate.
|
|
232
|
+
*/
|
|
233
|
+
async function applyPortsResolved(appName, frame) {
|
|
234
|
+
const declared = portsResolvedFrom(frame);
|
|
235
|
+
if (!declared)
|
|
236
|
+
return;
|
|
237
|
+
const rt = runtime;
|
|
238
|
+
const app = apps.find((a) => a.name === appName);
|
|
239
|
+
if (!rt || !app)
|
|
240
|
+
return;
|
|
241
|
+
const before = new Map(app.ports.map((p) => [portKey(p), p]));
|
|
242
|
+
const after = new Map(declared.map((p) => [portKey(p), p]));
|
|
243
|
+
const added = declared.filter((p) => !before.has(portKey(p)));
|
|
244
|
+
const removed = app.ports.filter((p) => !after.has(portKey(p)));
|
|
245
|
+
if (added.length === 0 && removed.length === 0)
|
|
246
|
+
return;
|
|
247
|
+
// A port another app in this session already owns cannot be routed: session
|
|
248
|
+
// hosts are `<port>-<sessionId>`, a single label carrying no app name. It is
|
|
249
|
+
// reported against the app that asked for it, never dropped.
|
|
250
|
+
const taken = new Set(apps.filter((a) => a.name !== appName).flatMap((a) => a.ports.map(portKey)));
|
|
251
|
+
const rejected = added.filter((p) => taken.has(portKey(p)));
|
|
252
|
+
const accepted = added.filter((p) => !taken.has(portKey(p)));
|
|
253
|
+
app.ports = [...app.ports.filter((p) => after.has(portKey(p))), ...accepted];
|
|
254
|
+
try {
|
|
255
|
+
await publishEndpoints(rt, apps);
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
spec.onEndpoints(appName, {
|
|
259
|
+
rejected: accepted.map((p) => ({ port: p.port, reason: msg(err) })),
|
|
260
|
+
});
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
spec.onEndpoints(appName, {
|
|
264
|
+
...(accepted.length > 0
|
|
265
|
+
? { added: endpointsFor(config, spec.sessionId, accepted) }
|
|
266
|
+
: {}),
|
|
267
|
+
...(removed.length > 0
|
|
268
|
+
? { removed: endpointsFor(config, spec.sessionId, removed) }
|
|
269
|
+
: {}),
|
|
270
|
+
...(rejected.length > 0
|
|
271
|
+
? {
|
|
272
|
+
rejected: rejected.map((p) => ({
|
|
273
|
+
port: p.port,
|
|
274
|
+
reason: `another app in this session already declares ${p.protocol} port ${p.port}`,
|
|
275
|
+
})),
|
|
276
|
+
}
|
|
277
|
+
: {}),
|
|
278
|
+
});
|
|
279
|
+
const tcp = accepted.filter((p) => p.protocol === "tcp").map((p) => p.port);
|
|
280
|
+
if (tcp.length > 0) {
|
|
281
|
+
void watchReachability({
|
|
282
|
+
host: rt.ip,
|
|
283
|
+
ports: tcp,
|
|
284
|
+
onState: (port, state) => spec.onReachability(appName, port, state),
|
|
285
|
+
signal: rt.abort.signal,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function watchPorts(rt) {
|
|
290
|
+
for (const app of apps) {
|
|
291
|
+
const tcp = app.ports.filter((p) => p.protocol === "tcp").map((p) => p.port);
|
|
292
|
+
if (tcp.length === 0)
|
|
293
|
+
continue;
|
|
294
|
+
void watchReachability({
|
|
295
|
+
host: rt.ip,
|
|
296
|
+
ports: tcp,
|
|
297
|
+
onState: (port, state) => spec.onReachability(app.name, port, state),
|
|
298
|
+
signal: rt.abort.signal,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/** Apps whose container has already been reported ended, so a repeated watch
|
|
303
|
+
* event does not re-report it. Cleared when a pod is replaced. */
|
|
304
|
+
const endedApps = new Set();
|
|
305
|
+
/**
|
|
306
|
+
* Report an `app-<name>` container that has terminated.
|
|
307
|
+
*
|
|
308
|
+
* `restartPolicy: Never` plus a `workspace` container that runs forever means
|
|
309
|
+
* a pod whose application container died stays **Running** — so pod phase
|
|
310
|
+
* alone never notices, and the app is silently dead while the editor still
|
|
311
|
+
* shows it running. The container statuses are where that fact lives.
|
|
312
|
+
*
|
|
313
|
+
* A run ENDING is not the session ending: the rest of the session is up and
|
|
314
|
+
* the next edit starts the next generation, which is why this reports a run
|
|
315
|
+
* outcome rather than a status.
|
|
316
|
+
*/
|
|
317
|
+
function noteEndedContainers(obj) {
|
|
318
|
+
if (userStopped)
|
|
319
|
+
return;
|
|
320
|
+
for (const status of podStatus(obj)?.containerStatuses ?? []) {
|
|
321
|
+
const name = status.name ?? "";
|
|
322
|
+
if (!name.startsWith("app-"))
|
|
323
|
+
continue;
|
|
324
|
+
const terminated = status.state?.terminated;
|
|
325
|
+
if (!terminated)
|
|
326
|
+
continue;
|
|
327
|
+
const appName = name.slice("app-".length);
|
|
328
|
+
if (endedApps.has(appName))
|
|
329
|
+
continue;
|
|
330
|
+
endedApps.add(appName);
|
|
331
|
+
spec.onRunEnded(appName, {
|
|
332
|
+
reason: terminated.reason && terminated.reason !== "Completed"
|
|
333
|
+
? `application container ${terminated.reason} (exit code ${terminated.exitCode ?? "unknown"})`
|
|
334
|
+
: `application container exited (code ${terminated.exitCode ?? "unknown"})`,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/** A watch on the pod, so a container that dies unrecoverably fails the
|
|
339
|
+
* session instead of leaving a stream that has simply gone quiet. */
|
|
340
|
+
function armPodWatch(rt) {
|
|
341
|
+
let stopped = false;
|
|
342
|
+
const arm = async () => {
|
|
343
|
+
if (stopped)
|
|
344
|
+
return;
|
|
345
|
+
try {
|
|
346
|
+
const req = await kube.watch.watch(`/api/v1/namespaces/${ns}/pods`, { fieldSelector: `metadata.name=${rt.name}` }, (type, obj) => {
|
|
347
|
+
if (stopped)
|
|
348
|
+
return;
|
|
349
|
+
noteEndedContainers(obj);
|
|
350
|
+
const phase = podPhase(obj);
|
|
351
|
+
if (phase === "Failed" || phase === "Succeeded") {
|
|
352
|
+
// A watch session's pod reaching a terminal phase is never a
|
|
353
|
+
// normal run ending — a run ending leaves the pod up. So it is a
|
|
354
|
+
// session failure unless the user asked for it.
|
|
355
|
+
settle(userStopped
|
|
356
|
+
? { kind: "stopped" }
|
|
357
|
+
: { kind: "failed", message: `session pod reached ${phase}` });
|
|
358
|
+
}
|
|
359
|
+
}, () => {
|
|
360
|
+
if (stopped)
|
|
361
|
+
return;
|
|
362
|
+
setTimeout(() => void arm(), WATCH_REARM_DELAY_MS).unref?.();
|
|
363
|
+
});
|
|
364
|
+
rt.stopWatch = () => {
|
|
365
|
+
stopped = true;
|
|
366
|
+
try {
|
|
367
|
+
req.abort?.();
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
/* already gone */
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
if (!stopped)
|
|
376
|
+
setTimeout(() => void arm(), WATCH_REARM_DELAY_MS).unref?.();
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
void arm();
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Create or re-patch the Service and Ingress for the session's whole declared
|
|
383
|
+
* port set. Adding a `ports:` entry is as ordinary an edit as adding an
|
|
384
|
+
* import, and a container may bind any port regardless of what the pod spec
|
|
385
|
+
* declares — so without this the app listens and is simply unreachable: no
|
|
386
|
+
* ingress, no error, no event. The pod's `containerPort` list is
|
|
387
|
+
* documentation; the Service and Ingress are what make a port reachable, which
|
|
388
|
+
* is why this needs no pod recreate.
|
|
389
|
+
*/
|
|
390
|
+
async function publishEndpoints(rt, forApps) {
|
|
391
|
+
if (!config.sessionIngressBaseDomain)
|
|
392
|
+
return;
|
|
393
|
+
const ports = allPorts(forApps);
|
|
394
|
+
if (ports.length === 0)
|
|
395
|
+
return;
|
|
396
|
+
const service = buildSessionService(config, spec.sessionId, rt.name, rt.uid, ports);
|
|
397
|
+
const serviceName = service.metadata.name;
|
|
398
|
+
await upsert(() => kube.core.createNamespacedService({ namespace: ns, body: service }), async () => {
|
|
399
|
+
// A Service replace must carry the assigned `clusterIP` and the current
|
|
400
|
+
// `resourceVersion`: the first is immutable and an empty one is rejected
|
|
401
|
+
// outright, the second is what makes the write conflict rather than
|
|
402
|
+
// clobber. Reading first is not optional here.
|
|
403
|
+
const existing = await kube.core.readNamespacedService({ name: serviceName, namespace: ns });
|
|
404
|
+
await kube.core.replaceNamespacedService({
|
|
405
|
+
name: serviceName,
|
|
406
|
+
namespace: ns,
|
|
407
|
+
body: {
|
|
408
|
+
...service,
|
|
409
|
+
metadata: {
|
|
410
|
+
...service.metadata,
|
|
411
|
+
resourceVersion: existing.metadata?.resourceVersion,
|
|
412
|
+
},
|
|
413
|
+
spec: {
|
|
414
|
+
...service.spec,
|
|
415
|
+
clusterIP: existing.spec?.clusterIP,
|
|
416
|
+
clusterIPs: existing.spec?.clusterIPs,
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
const { ingress } = buildSessionIngress(config, spec.sessionId, serviceName, rt.name, rt.uid, ports);
|
|
422
|
+
if (!ingress.spec?.rules?.length)
|
|
423
|
+
return;
|
|
424
|
+
const ingressName = ingress.metadata.name;
|
|
425
|
+
await upsert(() => kube.networking.createNamespacedIngress({ namespace: ns, body: ingress }), async () => {
|
|
426
|
+
const existing = await kube.networking.readNamespacedIngress({
|
|
427
|
+
name: ingressName,
|
|
428
|
+
namespace: ns,
|
|
429
|
+
});
|
|
430
|
+
await kube.networking.replaceNamespacedIngress({
|
|
431
|
+
name: ingressName,
|
|
432
|
+
namespace: ns,
|
|
433
|
+
body: {
|
|
434
|
+
...ingress,
|
|
435
|
+
metadata: {
|
|
436
|
+
...ingress.metadata,
|
|
437
|
+
resourceVersion: existing.metadata?.resourceVersion,
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
async function teardownPod() {
|
|
444
|
+
const rt = runtime;
|
|
445
|
+
runtime = null;
|
|
446
|
+
if (!rt)
|
|
447
|
+
return;
|
|
448
|
+
rt.stopWatch();
|
|
449
|
+
rt.abort.abort();
|
|
450
|
+
for (const socket of rt.sockets.values()) {
|
|
451
|
+
try {
|
|
452
|
+
socket.close();
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
/* already closed */
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
await deletePod(kube, ns, rt.name);
|
|
459
|
+
}
|
|
460
|
+
await bringUp(true);
|
|
461
|
+
return {
|
|
462
|
+
writeStdin(app, bytes) {
|
|
463
|
+
try {
|
|
464
|
+
runtime?.stdins.get(app)?.write(Buffer.from(bytes));
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
/* stream ended */
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
resize(app, cols, rows) {
|
|
471
|
+
const socket = runtime?.sockets.get(app);
|
|
472
|
+
if (!socket)
|
|
473
|
+
return;
|
|
474
|
+
try {
|
|
475
|
+
const payload = Buffer.from(JSON.stringify({ Width: cols, Height: rows }));
|
|
476
|
+
socket.send(Buffer.concat([Buffer.from([RESIZE_CHANNEL]), payload]));
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
/* socket gone */
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
done,
|
|
483
|
+
get workspace() {
|
|
484
|
+
return runtime?.workspace;
|
|
485
|
+
},
|
|
486
|
+
async reload(app) {
|
|
487
|
+
const rt = runtime;
|
|
488
|
+
const spec_ = apps.find((a) => a.name === app);
|
|
489
|
+
if (!rt || !spec_)
|
|
490
|
+
return;
|
|
491
|
+
await rt.workspace.touch(spec_.entryRelativePath);
|
|
492
|
+
},
|
|
493
|
+
async setApps(next) {
|
|
494
|
+
// A pod's container list is fixed at creation, so this is suspend+resume
|
|
495
|
+
// with a different set — the same path, reused because it has to be. The
|
|
496
|
+
// alternatives were a pod per app (needing ReadWriteMany storage for the
|
|
497
|
+
// shared workspace), pre-allocated slots, or a supervisor owning child
|
|
498
|
+
// kernels inside one container; all three cost more.
|
|
499
|
+
const files = await runtime?.workspace.snapshot();
|
|
500
|
+
await teardownPod();
|
|
501
|
+
apps = next;
|
|
502
|
+
podName = freshPodName(spec.sessionId);
|
|
503
|
+
await bringUp(false);
|
|
504
|
+
if (files && files.length > 0) {
|
|
505
|
+
await runtime.workspace.apply({
|
|
506
|
+
write: files.map((f) => ({ path: f.path, content: f.content, encoding: f.encoding })),
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
async suspend() {
|
|
511
|
+
// `teardownPod` stops the pod watch before deleting, so the delete is not
|
|
512
|
+
// reported as the session failing.
|
|
513
|
+
await teardownPod();
|
|
514
|
+
},
|
|
515
|
+
async stop() {
|
|
516
|
+
userStopped = true;
|
|
517
|
+
await teardownPod();
|
|
518
|
+
settle({ kind: "stopped" });
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
function tagFor(app, real) {
|
|
522
|
+
return app.io === "tty" ? "tty" : real;
|
|
523
|
+
}
|
|
524
|
+
async function upsert(create, replace) {
|
|
525
|
+
try {
|
|
526
|
+
await create();
|
|
527
|
+
}
|
|
528
|
+
catch (err) {
|
|
529
|
+
if (!isConflict(err))
|
|
530
|
+
throw err;
|
|
531
|
+
await replace();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function allPorts(apps) {
|
|
536
|
+
return apps.flatMap((a) => a.ports);
|
|
537
|
+
}
|
|
538
|
+
/** Every pod a session ever creates gets its own name. A resumed session reuses
|
|
539
|
+
* its ID but must not reuse the name: the pod it replaces may still be
|
|
540
|
+
* terminating, and creating over a terminating name races the API server's own
|
|
541
|
+
* delete. Reaping and session lookup key on the label, never on the name.
|
|
542
|
+
*
|
|
543
|
+
* The counter is what makes two recreates in the same millisecond distinct;
|
|
544
|
+
* the clock is what makes a name from a LATER closure (a resume builds a fresh
|
|
545
|
+
* one) distinct from an earlier closure's. Neither alone is enough. */
|
|
546
|
+
let podSequence = 0;
|
|
547
|
+
function freshPodName(sessionId) {
|
|
548
|
+
podSequence += 1;
|
|
549
|
+
return `telo-watch-${sessionId}-${Date.now().toString(36)}${podSequence.toString(36)}`;
|
|
550
|
+
}
|
|
551
|
+
function isConflict(err) {
|
|
552
|
+
const e = err;
|
|
553
|
+
return (e?.statusCode ?? e?.code ?? e?.response?.statusCode) === 409;
|
|
554
|
+
}
|
|
555
|
+
function sleep(ms, signal) {
|
|
556
|
+
return new Promise((resolve, reject) => {
|
|
557
|
+
const timer = setTimeout(resolve, ms);
|
|
558
|
+
timer.unref?.();
|
|
559
|
+
signal?.addEventListener("abort", () => {
|
|
560
|
+
clearTimeout(timer);
|
|
561
|
+
reject(new Error("aborted"));
|
|
562
|
+
}, { once: true });
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
//# sourceMappingURL=watch-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-session.js","sourceRoot":"","sources":["../../src/k8s/watch-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { K8sRunnerConfig } from "../config.js";
|
|
2
|
+
import type { KubeClient } from "./client.js";
|
|
3
|
+
export declare function workspaceConfigMapName(): string;
|
|
4
|
+
/** Create the ConfigMap if it is not already there. Content-addressed, so an
|
|
5
|
+
* existing one with this name already holds exactly these bytes and a 409 is
|
|
6
|
+
* success rather than a conflict to resolve. */
|
|
7
|
+
export declare function ensureWorkspaceConfigMap(kube: KubeClient, config: K8sRunnerConfig): Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Delete workspace-app ConfigMaps no live pod mounts.
|
|
10
|
+
*
|
|
11
|
+
* The name is content-addressed, so every release that changes the manifest
|
|
12
|
+
* leaves the previous one behind — permanently, since nothing else removes it.
|
|
13
|
+
* Run at boot, alongside the orphan pod reap: a session that survives a runner
|
|
14
|
+
* restart is impossible (the registry is in memory), so anything a live pod
|
|
15
|
+
* still references is a pod this process is about to reap anyway.
|
|
16
|
+
*
|
|
17
|
+
* Keeps the CURRENT hash unconditionally, and skips any map a pod still lists —
|
|
18
|
+
* a mounted ConfigMap that disappears makes the kubelet fail the pod.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sweepWorkspaceConfigMaps(kube: KubeClient, config: K8sRunnerConfig, log: {
|
|
21
|
+
info(obj: unknown, msg: string): void;
|
|
22
|
+
warn(obj: unknown, msg: string): void;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=workspace-configmap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-configmap.d.ts","sourceRoot":"","sources":["../../src/k8s/workspace-configmap.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAmB9C,wBAAgB,sBAAsB,IAAI,MAAM,CAG/C;AAED;;iDAEiD;AACjD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAeD;;;;;;;;;;;GAWG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE;IAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACpF,OAAO,CAAC,IAAI,CAAC,CAqCf"}
|