@telorun/k8s-runner 0.6.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 +17 -0
- package/README.md +206 -0
- package/dist/bundle-store.d.ts +32 -0
- package/dist/bundle-store.d.ts.map +1 -0
- package/dist/bundle-store.js +86 -0
- package/dist/bundle-store.js.map +1 -0
- package/dist/capabilities.d.ts +15 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +30 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/config.d.ts +95 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +76 -0
- package/dist/config.js.map +1 -0
- package/dist/k8s/backend.d.ts +19 -0
- package/dist/k8s/backend.d.ts.map +1 -0
- package/dist/k8s/backend.js +488 -0
- package/dist/k8s/backend.js.map +1 -0
- package/dist/k8s/client.d.ts +16 -0
- package/dist/k8s/client.d.ts.map +1 -0
- package/dist/k8s/client.js +24 -0
- package/dist/k8s/client.js.map +1 -0
- package/dist/k8s/image-build.d.ts +105 -0
- package/dist/k8s/image-build.d.ts.map +1 -0
- package/dist/k8s/image-build.js +432 -0
- package/dist/k8s/image-build.js.map +1 -0
- package/dist/k8s/ingress.d.ts +15 -0
- package/dist/k8s/ingress.d.ts.map +1 -0
- package/dist/k8s/ingress.js +102 -0
- package/dist/k8s/ingress.js.map +1 -0
- package/dist/k8s/pod-spec.d.ts +41 -0
- package/dist/k8s/pod-spec.d.ts.map +1 -0
- package/dist/k8s/pod-spec.js +146 -0
- package/dist/k8s/pod-spec.js.map +1 -0
- package/dist/limits.d.ts +25 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +58 -0
- package/dist/limits.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +93 -0
- package/dist/server.js.map +1 -0
- package/dist/tar.d.ts +10 -0
- package/dist/tar.d.ts.map +1 -0
- package/dist/tar.js +63 -0
- package/dist/tar.js.map +1 -0
- package/package.json +41 -0
- package/src/bundle-store.ts +101 -0
- package/src/capabilities.ts +40 -0
- package/src/config.ts +201 -0
- package/src/k8s/backend-failure.test.ts +148 -0
- package/src/k8s/backend.ts +535 -0
- package/src/k8s/client.ts +39 -0
- package/src/k8s/image-build.test.ts +244 -0
- package/src/k8s/image-build.ts +547 -0
- package/src/k8s/ingress.test.ts +146 -0
- package/src/k8s/ingress.ts +127 -0
- package/src/k8s/pod-spec.ts +180 -0
- package/src/limits.test.ts +59 -0
- package/src/limits.ts +90 -0
- package/src/server.ts +124 -0
- package/src/tar.test.ts +55 -0
- package/src/tar.ts +68 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { PassThrough, Writable } from "node:stream";
|
|
2
|
+
import { relayDebugStream, SessionStartError } from "@telorun/runner-core";
|
|
3
|
+
import { clampLimits } from "../limits.js";
|
|
4
|
+
import { ensureSessionImage } from "./image-build.js";
|
|
5
|
+
import { buildSessionIngress, buildSessionService, endpointsFor } from "./ingress.js";
|
|
6
|
+
import { buildSessionPod, INSPECT_PORT } from "./pod-spec.js";
|
|
7
|
+
const RESIZE_CHANNEL = 4;
|
|
8
|
+
/** How long a Pod may take to reach Running before the start is abandoned.
|
|
9
|
+
* activeDeadlineSeconds only bounds an already-running Pod, so a stuck
|
|
10
|
+
* Pending/unschedulable Pod needs this separate runner-side deadline. */
|
|
11
|
+
const START_DEADLINE_MS = 120_000;
|
|
12
|
+
const WATCH_REARM_DELAY_MS = 2_000;
|
|
13
|
+
export function createKubernetesBackend(deps) {
|
|
14
|
+
const { kube, config, bundleStore } = deps;
|
|
15
|
+
const ns = config.sessionNamespace;
|
|
16
|
+
async function probe(_probe) {
|
|
17
|
+
try {
|
|
18
|
+
await kube.core.readNamespace({ name: ns });
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
const reachable = await clusterReachable(kube);
|
|
22
|
+
if (!reachable) {
|
|
23
|
+
return {
|
|
24
|
+
status: "unavailable",
|
|
25
|
+
message: "Kubernetes API server not reachable from the runner.",
|
|
26
|
+
remediation: "Check the runner's in-cluster ServiceAccount and RBAC.",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
status: "unavailable",
|
|
31
|
+
message: `Session namespace '${ns}' does not exist.`,
|
|
32
|
+
remediation: `Install the runner's Helm chart, which provisions the '${ns}' namespace.`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return { status: "ready" };
|
|
36
|
+
}
|
|
37
|
+
async function start(spec) {
|
|
38
|
+
const podName = `telo-run-${spec.sessionId}`;
|
|
39
|
+
// The /v1 contract carries no per-request limits yet, so `requested` is
|
|
40
|
+
// undefined and the configured ceiling is always the effective limit. When
|
|
41
|
+
// a control plane begins passing limits, plumb them here — the clamp is
|
|
42
|
+
// already min(requested, ceiling).
|
|
43
|
+
const limits = clampLimits(config.limits, undefined);
|
|
44
|
+
// Prebuild a self-contained per-app image on-cluster (controllers + module
|
|
45
|
+
// manifests baked in) and run it directly. Controller resolution never
|
|
46
|
+
// happens on the session start path, so a slow or unreachable package
|
|
47
|
+
// registry can't stall a session. Throws SessionStartError on a build
|
|
48
|
+
// failure, carrying the build pod's log tail.
|
|
49
|
+
const image = await ensureSessionImage({
|
|
50
|
+
kube,
|
|
51
|
+
build: config.build,
|
|
52
|
+
bundleStore,
|
|
53
|
+
initImage: config.initImage,
|
|
54
|
+
managedByLabel: config.managedByLabel,
|
|
55
|
+
}, {
|
|
56
|
+
bundle: spec.bundle,
|
|
57
|
+
entryRelativePath: spec.entryRelativePath,
|
|
58
|
+
baseImage: spec.config.image || config.defaultImage,
|
|
59
|
+
pullPolicy: spec.config.pullPolicy,
|
|
60
|
+
onProgress: (message, done) => spec.onProgress("build", message, done),
|
|
61
|
+
});
|
|
62
|
+
// The image is keyed on the dependency closure only, so deliver the
|
|
63
|
+
// per-session body to the Pod's /app at boot via a tokenized, single-use URL.
|
|
64
|
+
const bundleUrl = await bundleStore.stageSessionBundle(spec.sessionId, spec.bundle);
|
|
65
|
+
const pod = buildSessionPod({
|
|
66
|
+
config,
|
|
67
|
+
sessionId: spec.sessionId,
|
|
68
|
+
podName,
|
|
69
|
+
entryRelativePath: spec.entryRelativePath,
|
|
70
|
+
env: spec.env,
|
|
71
|
+
ports: spec.ports,
|
|
72
|
+
limits,
|
|
73
|
+
image,
|
|
74
|
+
bundleUrl,
|
|
75
|
+
inspect: spec.inspect,
|
|
76
|
+
});
|
|
77
|
+
let podUid;
|
|
78
|
+
try {
|
|
79
|
+
const created = await kube.core.createNamespacedPod({ namespace: ns, body: pod });
|
|
80
|
+
podUid = created.metadata?.uid ?? "";
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
throw new SessionStartError("start_failed", "create", `failed to create pod: ${msg(err)}`, msg(err));
|
|
84
|
+
}
|
|
85
|
+
let finished = false;
|
|
86
|
+
let runningSeen = false;
|
|
87
|
+
let readyFlipped = false;
|
|
88
|
+
let lastProvision;
|
|
89
|
+
let resolveDone;
|
|
90
|
+
const done = new Promise((r) => (resolveDone = r));
|
|
91
|
+
let socket;
|
|
92
|
+
let abortWatch = () => { };
|
|
93
|
+
let startDeadline;
|
|
94
|
+
let podIP;
|
|
95
|
+
const debugAbort = new AbortController();
|
|
96
|
+
const stdin = new PassThrough();
|
|
97
|
+
const stdout = new Writable({
|
|
98
|
+
write(chunk, _enc, cb) {
|
|
99
|
+
if (chunk?.byteLength)
|
|
100
|
+
spec.onOutput(Buffer.from(chunk));
|
|
101
|
+
cb();
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const clearStartDeadline = () => {
|
|
105
|
+
if (startDeadline) {
|
|
106
|
+
clearTimeout(startDeadline);
|
|
107
|
+
startDeadline = undefined;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const finish = (status) => {
|
|
111
|
+
if (finished)
|
|
112
|
+
return;
|
|
113
|
+
finished = true;
|
|
114
|
+
clearStartDeadline();
|
|
115
|
+
abortWatch();
|
|
116
|
+
debugAbort.abort();
|
|
117
|
+
bundleStore.drop(spec.sessionId);
|
|
118
|
+
spec.onStatus(status);
|
|
119
|
+
try {
|
|
120
|
+
socket?.close();
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
/* already closed */
|
|
124
|
+
}
|
|
125
|
+
resolveDone();
|
|
126
|
+
};
|
|
127
|
+
// Flip the session to `running` when the pod reaches Running. Deterministic
|
|
128
|
+
// and independent of the session image's telo version — a readiness signal
|
|
129
|
+
// would couple this to the in-image CLI (and a stale base image would never
|
|
130
|
+
// flip). The build/provision progress (streamed before this) covers the slow
|
|
131
|
+
// part; the brief post-Running validation runs while already `running`.
|
|
132
|
+
const flipRunning = () => {
|
|
133
|
+
if (readyFlipped || finished)
|
|
134
|
+
return;
|
|
135
|
+
readyFlipped = true;
|
|
136
|
+
spec.onStatus({ kind: "running", endpoints: endpointsFor(config, spec.sessionId, spec.ports) });
|
|
137
|
+
};
|
|
138
|
+
let resolveRunning;
|
|
139
|
+
let rejectRunning;
|
|
140
|
+
const running = new Promise((res, rej) => {
|
|
141
|
+
resolveRunning = res;
|
|
142
|
+
rejectRunning = rej;
|
|
143
|
+
});
|
|
144
|
+
const handlePhase = (obj) => {
|
|
145
|
+
if (finished)
|
|
146
|
+
return;
|
|
147
|
+
const phase = podPhase(obj);
|
|
148
|
+
// Coming-up feed: scheduling / pulling / body delivery / container create.
|
|
149
|
+
if (!runningSeen) {
|
|
150
|
+
const provision = provisionMessage(obj);
|
|
151
|
+
if (provision && provision !== lastProvision) {
|
|
152
|
+
lastProvision = provision;
|
|
153
|
+
spec.onProgress("provision", provision);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (phase === "Running" && !runningSeen) {
|
|
157
|
+
runningSeen = true;
|
|
158
|
+
podIP = podStatus(obj)?.podIP;
|
|
159
|
+
clearStartDeadline();
|
|
160
|
+
resolveRunning();
|
|
161
|
+
flipRunning();
|
|
162
|
+
}
|
|
163
|
+
else if (phase === "Succeeded") {
|
|
164
|
+
finish(terminalStatus(obj, spec.isUserStopped()));
|
|
165
|
+
}
|
|
166
|
+
else if (phase === "Failed") {
|
|
167
|
+
if (!runningSeen) {
|
|
168
|
+
clearStartDeadline();
|
|
169
|
+
rejectRunning(new Error(podFailureMessage(obj)));
|
|
170
|
+
}
|
|
171
|
+
finish(terminalStatus(obj, spec.isUserStopped()));
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
// One-shot reconcile — covers terminal transitions that landed during a
|
|
175
|
+
// watch-reconnect gap, or a Pod that vanished entirely.
|
|
176
|
+
const reconcileOnce = async () => {
|
|
177
|
+
if (finished)
|
|
178
|
+
return;
|
|
179
|
+
try {
|
|
180
|
+
const current = await kube.core.readNamespacedPod({ name: podName, namespace: ns });
|
|
181
|
+
handlePhase(current);
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
if (!is404(err))
|
|
185
|
+
return;
|
|
186
|
+
if (!runningSeen) {
|
|
187
|
+
clearStartDeadline();
|
|
188
|
+
rejectRunning(new Error("pod disappeared before reaching Running"));
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
finish({ kind: "failed", message: "pod disappeared" });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
// k8s watches expire routinely; re-arm on clean close so a healthy
|
|
196
|
+
// long-lived session (TTL up to 1h) isn't failed by a watch rollover.
|
|
197
|
+
const armWatch = async () => {
|
|
198
|
+
if (finished)
|
|
199
|
+
return;
|
|
200
|
+
try {
|
|
201
|
+
const req = await kube.watch.watch(`/api/v1/namespaces/${ns}/pods`, { fieldSelector: `metadata.name=${podName}` }, (_type, obj) => handlePhase(obj), () => {
|
|
202
|
+
if (finished)
|
|
203
|
+
return;
|
|
204
|
+
void reconcileOnce().then(() => {
|
|
205
|
+
if (!finished)
|
|
206
|
+
void armWatch();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
abortWatch = () => {
|
|
210
|
+
try {
|
|
211
|
+
req.abort?.();
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
/* ignore */
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
if (!runningSeen) {
|
|
220
|
+
clearStartDeadline();
|
|
221
|
+
rejectRunning(new Error(`failed to watch pod: ${msg(err)}`));
|
|
222
|
+
}
|
|
223
|
+
else if (!finished) {
|
|
224
|
+
setTimeout(() => void armWatch(), WATCH_REARM_DELAY_MS).unref?.();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
startDeadline = setTimeout(() => {
|
|
229
|
+
if (!runningSeen && !finished) {
|
|
230
|
+
rejectRunning(new Error("pod did not reach Running within the start deadline"));
|
|
231
|
+
}
|
|
232
|
+
}, START_DEADLINE_MS);
|
|
233
|
+
startDeadline.unref?.();
|
|
234
|
+
await armWatch();
|
|
235
|
+
try {
|
|
236
|
+
await running;
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
clearStartDeadline();
|
|
240
|
+
abortWatch();
|
|
241
|
+
bundleStore.drop(spec.sessionId);
|
|
242
|
+
await deletePod(kube, ns, podName);
|
|
243
|
+
throw new SessionStartError("start_failed", "start", `pod failed to start: ${msg(err)}`, msg(err));
|
|
244
|
+
}
|
|
245
|
+
// Attach a PTY to the running container: stdout → onOutput, stdin ← writes.
|
|
246
|
+
try {
|
|
247
|
+
const ws = await kube.attach.attach(ns, podName, "session", stdout, null, stdin, true);
|
|
248
|
+
socket = ws;
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
// Attach failure isn't fatal — status still flows; surface the degraded PTY.
|
|
252
|
+
spec.onOutput(Buffer.from(`\r\n[runner] failed to attach PTY: ${msg(err)}\r\n`));
|
|
253
|
+
}
|
|
254
|
+
if (config.sessionIngressBaseDomain && spec.ports.length > 0) {
|
|
255
|
+
await createIngress(deps, spec.sessionId, podName, podUid, spec.ports).catch((err) => {
|
|
256
|
+
spec.onOutput(Buffer.from(`\r\n[runner] failed to create ingress: ${msg(err)}\r\n`));
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
// When inspect is on, relay the workload's in-pod kernel debug stream out
|
|
260
|
+
// over `onDebug`. Reached by pod IP over the cluster network — the inspect
|
|
261
|
+
// port is never published via Service/Ingress. The Running watch event
|
|
262
|
+
// usually carries `podIP`; if it lagged, read the pod once to recover it.
|
|
263
|
+
if (spec.inspect) {
|
|
264
|
+
if (!podIP) {
|
|
265
|
+
podIP = await kube.core
|
|
266
|
+
.readNamespacedPod({ name: podName, namespace: ns })
|
|
267
|
+
.then((p) => podStatus(p)?.podIP)
|
|
268
|
+
.catch(() => undefined);
|
|
269
|
+
}
|
|
270
|
+
if (podIP) {
|
|
271
|
+
void relayDebugStream({
|
|
272
|
+
url: `http://${podIP}:${INSPECT_PORT}/events`,
|
|
273
|
+
onFrame: spec.onDebug,
|
|
274
|
+
signal: debugAbort.signal,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
spec.onOutput(Buffer.from("\r\n[runner] debug stream unavailable: pod IP unknown\r\n"));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// `flipRunning` already announced `running` from the watch's Running
|
|
282
|
+
// transition (which `await running` above waited on).
|
|
283
|
+
return {
|
|
284
|
+
writeStdin(bytes) {
|
|
285
|
+
try {
|
|
286
|
+
stdin.write(Buffer.from(bytes));
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
/* stream ended */
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
resize(cols, rows) {
|
|
293
|
+
if (!socket)
|
|
294
|
+
return;
|
|
295
|
+
try {
|
|
296
|
+
const payload = Buffer.from(JSON.stringify({ Width: cols, Height: rows }));
|
|
297
|
+
socket.send(Buffer.concat([Buffer.from([RESIZE_CHANNEL]), payload]));
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
/* socket gone */
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
done,
|
|
304
|
+
async stop() {
|
|
305
|
+
// The route sets userStopped before calling stop(), so the watch (or
|
|
306
|
+
// this finish) classifies the kill as `stopped`, not `failed`.
|
|
307
|
+
await deletePod(kube, ns, podName);
|
|
308
|
+
finish({ kind: "stopped" });
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
async function reapOrphans() {
|
|
313
|
+
// The session registry is in-memory; on boot a prior process's pods are
|
|
314
|
+
// orphaned. Delete everything we own by label. Errors propagate to the
|
|
315
|
+
// caller (the server logs them) rather than being swallowed.
|
|
316
|
+
const list = await kube.core.listNamespacedPod({
|
|
317
|
+
namespace: ns,
|
|
318
|
+
labelSelector: `app.kubernetes.io/managed-by=${config.managedByLabel}`,
|
|
319
|
+
});
|
|
320
|
+
const failures = [];
|
|
321
|
+
for (const item of list.items ?? []) {
|
|
322
|
+
const name = item.metadata?.name;
|
|
323
|
+
if (!name)
|
|
324
|
+
continue;
|
|
325
|
+
try {
|
|
326
|
+
await deletePod(kube, ns, name);
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
failures.push(`${name}: ${msg(err)}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (failures.length > 0) {
|
|
333
|
+
throw new Error(`failed to reap ${failures.length} orphan pod(s): ${failures.join("; ")}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return { probe, start, reapOrphans };
|
|
337
|
+
}
|
|
338
|
+
async function createIngress(deps, sessionId, podName, podUid, ports) {
|
|
339
|
+
const { kube, config } = deps;
|
|
340
|
+
const ns = config.sessionNamespace;
|
|
341
|
+
const service = buildSessionService(config, sessionId, podName, podUid, ports);
|
|
342
|
+
await kube.core.createNamespacedService({ namespace: ns, body: service });
|
|
343
|
+
const { ingress } = buildSessionIngress(config, sessionId, service.metadata.name, podName, podUid, ports);
|
|
344
|
+
// No tcp ports → no HTTP-routable rules; the Service still exists for any udp.
|
|
345
|
+
if (!ingress.spec?.rules?.length)
|
|
346
|
+
return;
|
|
347
|
+
await kube.networking.createNamespacedIngress({ namespace: ns, body: ingress });
|
|
348
|
+
}
|
|
349
|
+
async function deletePod(kube, ns, name) {
|
|
350
|
+
try {
|
|
351
|
+
await kube.core.deleteNamespacedPod({ name, namespace: ns, gracePeriodSeconds: 0 });
|
|
352
|
+
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
// 404 = already gone (natural exit + GC). Anything else is a real failure.
|
|
355
|
+
if (!is404(err))
|
|
356
|
+
throw err;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
async function clusterReachable(kube) {
|
|
360
|
+
try {
|
|
361
|
+
await kube.core.listNamespace();
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function podStatus(obj) {
|
|
369
|
+
return obj?.status;
|
|
370
|
+
}
|
|
371
|
+
function podPhase(obj) {
|
|
372
|
+
return podStatus(obj)?.phase;
|
|
373
|
+
}
|
|
374
|
+
/** A coming-up message for the editor feed while the Pod is still scheduling /
|
|
375
|
+
* pulling / delivering the body / creating the container; undefined once running. */
|
|
376
|
+
function provisionMessage(obj) {
|
|
377
|
+
const status = podStatus(obj);
|
|
378
|
+
if (status?.phase !== "Pending")
|
|
379
|
+
return undefined;
|
|
380
|
+
const containers = [
|
|
381
|
+
...(status.initContainerStatuses ?? []),
|
|
382
|
+
...(status.containerStatuses ?? []),
|
|
383
|
+
];
|
|
384
|
+
for (const cs of containers) {
|
|
385
|
+
const reason = cs.state?.waiting?.reason;
|
|
386
|
+
if (reason)
|
|
387
|
+
return humanizeWaitReason(reason);
|
|
388
|
+
}
|
|
389
|
+
return "Scheduling";
|
|
390
|
+
}
|
|
391
|
+
function humanizeWaitReason(reason) {
|
|
392
|
+
switch (reason) {
|
|
393
|
+
case "ContainerCreating":
|
|
394
|
+
return "Creating container";
|
|
395
|
+
case "PodInitializing":
|
|
396
|
+
return "Delivering application";
|
|
397
|
+
case "ErrImagePull":
|
|
398
|
+
case "ImagePullBackOff":
|
|
399
|
+
return "Pulling image";
|
|
400
|
+
default:
|
|
401
|
+
return reason;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function terminalStatus(obj, userStopped) {
|
|
405
|
+
if (userStopped)
|
|
406
|
+
return { kind: "stopped" };
|
|
407
|
+
const phase = podPhase(obj);
|
|
408
|
+
if (phase === "Succeeded")
|
|
409
|
+
return { kind: "exited", code: containerExitCode(obj) ?? 0 };
|
|
410
|
+
return { kind: "failed", message: podFailureMessage(obj) };
|
|
411
|
+
}
|
|
412
|
+
// Exit code of the main session container — used to report a clean exit.
|
|
413
|
+
function containerExitCode(obj) {
|
|
414
|
+
const term = podStatus(obj)?.containerStatuses?.[0]?.state?.terminated;
|
|
415
|
+
return typeof term?.exitCode === "number" ? term.exitCode : null;
|
|
416
|
+
}
|
|
417
|
+
const MAX_FAILURE_DETAIL = 500;
|
|
418
|
+
/**
|
|
419
|
+
* Builds an actionable failure message from a terminal Pod status. Init
|
|
420
|
+
* containers are inspected first: a failed init container leaves the main
|
|
421
|
+
* container unstarted, so reading only `containerStatuses` would fall through
|
|
422
|
+
* to the bare "pod failed". For prebuilt session pods the common failure is the
|
|
423
|
+
* main container itself (image pull, OOM, a non-zero exit).
|
|
424
|
+
*/
|
|
425
|
+
export function podFailureMessage(obj) {
|
|
426
|
+
const status = podStatus(obj);
|
|
427
|
+
const fromContainer = firstContainerProblem(status);
|
|
428
|
+
if (fromContainer)
|
|
429
|
+
return fromContainer;
|
|
430
|
+
if (status?.message)
|
|
431
|
+
return truncateDetail(status.message);
|
|
432
|
+
if (status?.reason)
|
|
433
|
+
return status.reason;
|
|
434
|
+
return "pod failed";
|
|
435
|
+
}
|
|
436
|
+
function firstContainerProblem(status) {
|
|
437
|
+
const groups = [
|
|
438
|
+
["init container", status?.initContainerStatuses],
|
|
439
|
+
["container", status?.containerStatuses],
|
|
440
|
+
];
|
|
441
|
+
for (const [label, statuses] of groups) {
|
|
442
|
+
for (const cs of statuses ?? []) {
|
|
443
|
+
const problem = containerStateProblem(cs.state) ?? containerStateProblem(cs.lastState);
|
|
444
|
+
if (problem)
|
|
445
|
+
return `${label} "${cs.name}" ${problem}`;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return undefined;
|
|
449
|
+
}
|
|
450
|
+
function containerStateProblem(state) {
|
|
451
|
+
const term = state?.terminated;
|
|
452
|
+
if (term && term.exitCode !== 0) {
|
|
453
|
+
const reason = term.reason ? `${term.reason} ` : "";
|
|
454
|
+
const detail = term.message ? `: ${truncateDetail(term.message)}` : "";
|
|
455
|
+
return `failed: ${reason}(exit code ${term.exitCode ?? "unknown"})${detail}`;
|
|
456
|
+
}
|
|
457
|
+
const waiting = state?.waiting;
|
|
458
|
+
if (waiting?.reason && isBlockingWaitReason(waiting.reason)) {
|
|
459
|
+
const detail = waiting.message ? `: ${truncateDetail(waiting.message)}` : "";
|
|
460
|
+
return `waiting: ${waiting.reason}${detail}`;
|
|
461
|
+
}
|
|
462
|
+
return undefined;
|
|
463
|
+
}
|
|
464
|
+
// Benign transient reasons the kubelet reports while a Pod is still coming up.
|
|
465
|
+
function isBlockingWaitReason(reason) {
|
|
466
|
+
return reason !== "PodInitializing" && reason !== "ContainerCreating";
|
|
467
|
+
}
|
|
468
|
+
function truncateDetail(text) {
|
|
469
|
+
const trimmed = text.trim();
|
|
470
|
+
return trimmed.length > MAX_FAILURE_DETAIL ? `${trimmed.slice(0, MAX_FAILURE_DETAIL)}…` : trimmed;
|
|
471
|
+
}
|
|
472
|
+
function is404(err) {
|
|
473
|
+
const e = err;
|
|
474
|
+
return e?.statusCode === 404 || e?.code === 404 || e?.response?.statusCode === 404;
|
|
475
|
+
}
|
|
476
|
+
function msg(err) {
|
|
477
|
+
if (err instanceof Error)
|
|
478
|
+
return err.message;
|
|
479
|
+
if (typeof err === "string")
|
|
480
|
+
return err;
|
|
481
|
+
try {
|
|
482
|
+
return JSON.stringify(err);
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
return String(err);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/k8s/backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Attach, BatchV1Api, CoreV1Api, KubeConfig, NetworkingV1Api, Watch } from "@kubernetes/client-node";
|
|
2
|
+
export interface KubeClient {
|
|
3
|
+
kc: KubeConfig;
|
|
4
|
+
core: CoreV1Api;
|
|
5
|
+
batch: BatchV1Api;
|
|
6
|
+
networking: NetworkingV1Api;
|
|
7
|
+
attach: Attach;
|
|
8
|
+
watch: Watch;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Loads kube config — in-cluster (projected ServiceAccount token + CA) when
|
|
12
|
+
* running as a Pod, falling back to the local kubeconfig for out-of-cluster
|
|
13
|
+
* development.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createKubeClient(): KubeClient;
|
|
16
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/k8s/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,EACf,KAAK,EACN,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,UAAU,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,UAAU,EAAE,eAAe,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAe7C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Attach, BatchV1Api, CoreV1Api, KubeConfig, NetworkingV1Api, Watch, } from "@kubernetes/client-node";
|
|
2
|
+
/**
|
|
3
|
+
* Loads kube config — in-cluster (projected ServiceAccount token + CA) when
|
|
4
|
+
* running as a Pod, falling back to the local kubeconfig for out-of-cluster
|
|
5
|
+
* development.
|
|
6
|
+
*/
|
|
7
|
+
export function createKubeClient() {
|
|
8
|
+
const kc = new KubeConfig();
|
|
9
|
+
if (process.env.KUBERNETES_SERVICE_HOST) {
|
|
10
|
+
kc.loadFromCluster();
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
kc.loadFromDefault();
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
kc,
|
|
17
|
+
core: kc.makeApiClient(CoreV1Api),
|
|
18
|
+
batch: kc.makeApiClient(BatchV1Api),
|
|
19
|
+
networking: kc.makeApiClient(NetworkingV1Api),
|
|
20
|
+
attach: new Attach(kc),
|
|
21
|
+
watch: new Watch(kc),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/k8s/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,EACf,KAAK,GACN,MAAM,yBAAyB,CAAC;AAWjC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,EAAE,GAAG,IAAI,UAAU,EAAE,CAAC;IAC5B,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,CAAC;QACxC,EAAE,CAAC,eAAe,EAAE,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,eAAe,EAAE,CAAC;IACvB,CAAC;IACD,OAAO;QACL,EAAE;QACF,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC;QACjC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC;QACnC,UAAU,EAAE,EAAE,CAAC,aAAa,CAAC,eAAe,CAAC;QAC7C,MAAM,EAAE,IAAI,MAAM,CAAC,EAAE,CAAC;QACtB,KAAK,EAAE,IAAI,KAAK,CAAC,EAAE,CAAC;KACrB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { V1Job } from "@kubernetes/client-node";
|
|
2
|
+
import type { PullPolicy, RunBundle } from "@telorun/runner-core";
|
|
3
|
+
import type { ImageBuildConfig } from "../config.js";
|
|
4
|
+
import type { BundleStore } from "../bundle-store.js";
|
|
5
|
+
import type { KubeClient } from "./client.js";
|
|
6
|
+
export interface ImageTagInputs {
|
|
7
|
+
bundle: RunBundle;
|
|
8
|
+
/** Base image the per-app image is built FROM. */
|
|
9
|
+
baseImage: string;
|
|
10
|
+
/** Resolved digest of `baseImage`, folded into the tag so a moved moving-tag
|
|
11
|
+
* (e.g. `latest-slim`) yields a fresh tag and rebuilds. Set only under the
|
|
12
|
+
* `always` pull policy; omitted → keyed on the base TAG STRING alone. */
|
|
13
|
+
baseDigest?: string;
|
|
14
|
+
/** Telo module registry baked into the build (affects resolved manifests). */
|
|
15
|
+
teloRegistryUrl: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Content-address the per-app image on its DEPENDENCY closure — the only thing
|
|
19
|
+
* `telo install` bakes into `.telo/{manifests,npm}`: the `imports:` set plus any
|
|
20
|
+
* controllers declared by inline `Telo.Definition` docs (`extractDependencyKey`).
|
|
21
|
+
* A body-only edit (resource config, CEL) keeps the tag stable so the image is
|
|
22
|
+
* reused and the new body is delivered to the pod at runtime; an import or
|
|
23
|
+
* controller change produces a fresh tag and rebuilds. The entry path is
|
|
24
|
+
* deliberately excluded — it's supplied to `telo run` at runtime, and the same
|
|
25
|
+
* imports resolve the same closure regardless of the entry filename.
|
|
26
|
+
*
|
|
27
|
+
* `local_path` controllers (or an unparseable file) set `fullContentFallback`,
|
|
28
|
+
* which folds the whole bundle into the digest — keying degrades to today's
|
|
29
|
+
* content-address, which is correct (those depend on body bytes) if less optimal.
|
|
30
|
+
*/
|
|
31
|
+
export declare function computeImageTag(inputs: ImageTagInputs): string;
|
|
32
|
+
export declare function imageRef(config: ImageBuildConfig, tag: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Dockerfile for a self-contained per-app image: `telo install` populates
|
|
35
|
+
* `/telo-cache/{manifests,npm}` so the runtime `telo run` resolves every
|
|
36
|
+
* controller and module from disk with no network and no install. The telo
|
|
37
|
+
* registry arrives as a build-arg so the build (not the running session)
|
|
38
|
+
* carries the registry dependency.
|
|
39
|
+
*
|
|
40
|
+
* `TELO_CACHE_DIR=/telo-cache` relocates the baked `.telo` OFF `/app`, so at
|
|
41
|
+
* runtime the pod mounts the freshly-delivered body at `/app` without shadowing
|
|
42
|
+
* the deps. The deps depend only on the imports/controllers closure (see
|
|
43
|
+
* `computeImageTag`); the body COPYed here is whichever session first built this
|
|
44
|
+
* tag — disposable, replaced at boot.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildDockerfile(opts: {
|
|
47
|
+
baseImage: string;
|
|
48
|
+
entryRelativePath: string;
|
|
49
|
+
}): string;
|
|
50
|
+
export interface KanikoJobArgs {
|
|
51
|
+
config: ImageBuildConfig;
|
|
52
|
+
jobName: string;
|
|
53
|
+
/** Tokenized URL the initContainer fetches the build context (bundle + Dockerfile) from. */
|
|
54
|
+
contextUrl: string;
|
|
55
|
+
/** Fully-qualified image ref Kaniko pushes. */
|
|
56
|
+
destination: string;
|
|
57
|
+
/** Small image (wget + tar) for the context-fetch initContainer. */
|
|
58
|
+
initImage: string;
|
|
59
|
+
managedByLabel: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Builds the Kaniko Job. An initContainer fetches the tokenized context tarball
|
|
63
|
+
* into a shared emptyDir; Kaniko builds it and pushes to the registry. The Job
|
|
64
|
+
* is single-attempt (`backoffLimit: 0`), deadline-bounded, and self-cleaning
|
|
65
|
+
* (`ttlSecondsAfterFinished`). Kaniko needs a writable rootfs, so this runs in
|
|
66
|
+
* the trusted `telo-builds` namespace at baseline PodSecurity — never alongside
|
|
67
|
+
* untrusted session pods.
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildKanikoJob(args: KanikoJobArgs): V1Job;
|
|
70
|
+
export interface EnsureImageDeps {
|
|
71
|
+
kube: KubeClient;
|
|
72
|
+
build: ImageBuildConfig;
|
|
73
|
+
bundleStore: BundleStore;
|
|
74
|
+
initImage: string;
|
|
75
|
+
managedByLabel: string;
|
|
76
|
+
}
|
|
77
|
+
export interface EnsureImageArgs {
|
|
78
|
+
bundle: RunBundle;
|
|
79
|
+
entryRelativePath: string;
|
|
80
|
+
baseImage: string;
|
|
81
|
+
/** Base-image freshness. `always` pins the per-app image to the base's current
|
|
82
|
+
* Docker Hub digest so a moved moving-tag rebuilds; `missing` / `never` (and
|
|
83
|
+
* `always` on a base whose digest can't be resolved) reuse the cached build. */
|
|
84
|
+
pullPolicy: PullPolicy;
|
|
85
|
+
/** Build-phase progress for the editor feed. */
|
|
86
|
+
onProgress?: (message: string, done?: boolean) => void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the per-app image, building it on-cluster if absent. Returns the image
|
|
90
|
+
* ref to run; throws `SessionStartError` (stage `create`) with the build pod's
|
|
91
|
+
* log tail on failure so the cause reaches the editor instead of an opaque hang.
|
|
92
|
+
*/
|
|
93
|
+
export declare function ensureSessionImage(deps: EnsureImageDeps, args: EnsureImageArgs): Promise<string>;
|
|
94
|
+
/** Registry pull credentials decoded from a `.dockerconfigjson` Secret. */
|
|
95
|
+
export interface RegistryAuth {
|
|
96
|
+
username: string;
|
|
97
|
+
password: string;
|
|
98
|
+
}
|
|
99
|
+
/** Extract credentials for `repository`'s host from a `.dockerconfigjson` payload. */
|
|
100
|
+
export declare function parseDockerConfigAuth(dockerconfigjson: string, repository: string): RegistryAuth | undefined;
|
|
101
|
+
/** Host portion of a registry ref / dockerconfig key (scheme + path stripped). */
|
|
102
|
+
export declare function registryHost(ref: string): string;
|
|
103
|
+
/** Parse `key="value"` pairs out of a `WWW-Authenticate: Bearer ...` challenge. */
|
|
104
|
+
export declare function parseAuthParams(challenge: string): Record<string, string>;
|
|
105
|
+
//# sourceMappingURL=image-build.d.ts.map
|