@telorun/k8s-runner 0.13.0 → 0.15.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 +166 -107
- package/dist/bundle-store.d.ts +6 -13
- package/dist/bundle-store.d.ts.map +1 -1
- package/dist/bundle-store.js +6 -18
- package/dist/bundle-store.js.map +1 -1
- package/dist/capabilities.d.ts +3 -3
- package/dist/capabilities.js +4 -4
- package/dist/capabilities.js.map +1 -1
- package/dist/config.d.ts +47 -37
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +64 -25
- package/dist/config.js.map +1 -1
- package/dist/k8s/api-error.d.ts +34 -0
- package/dist/k8s/api-error.d.ts.map +1 -0
- package/dist/k8s/api-error.js +118 -0
- package/dist/k8s/api-error.js.map +1 -0
- package/dist/k8s/backend.d.ts +4 -0
- package/dist/k8s/backend.d.ts.map +1 -1
- package/dist/k8s/backend.js +77 -51
- package/dist/k8s/backend.js.map +1 -1
- package/dist/k8s/client.d.ts +7 -2
- package/dist/k8s/client.d.ts.map +1 -1
- package/dist/k8s/client.js +3 -2
- package/dist/k8s/client.js.map +1 -1
- package/dist/k8s/pod-spec.d.ts +8 -9
- package/dist/k8s/pod-spec.d.ts.map +1 -1
- package/dist/k8s/pod-spec.js +19 -29
- package/dist/k8s/pod-spec.js.map +1 -1
- package/dist/k8s/pod-status.d.ts +5 -0
- package/dist/k8s/pod-status.d.ts.map +1 -1
- package/dist/k8s/pod-status.js +9 -2
- package/dist/k8s/pod-status.js.map +1 -1
- package/dist/k8s/routing/create-or-replace.d.ts +5 -0
- package/dist/k8s/routing/create-or-replace.d.ts.map +1 -0
- package/dist/k8s/routing/create-or-replace.js +15 -0
- package/dist/k8s/routing/create-or-replace.js.map +1 -0
- package/dist/k8s/routing/gateway-router.d.ts +5 -0
- package/dist/k8s/routing/gateway-router.d.ts.map +1 -0
- package/dist/k8s/routing/gateway-router.js +178 -0
- package/dist/k8s/routing/gateway-router.js.map +1 -0
- package/dist/k8s/routing/index.d.ts +24 -0
- package/dist/k8s/routing/index.d.ts.map +1 -0
- package/dist/k8s/routing/index.js +27 -0
- package/dist/k8s/routing/index.js.map +1 -0
- package/dist/k8s/routing/ingress-router.d.ts +13 -0
- package/dist/k8s/routing/ingress-router.d.ts.map +1 -0
- package/dist/k8s/routing/ingress-router.js +125 -0
- package/dist/k8s/routing/ingress-router.js.map +1 -0
- package/dist/k8s/routing/route-health.d.ts +34 -0
- package/dist/k8s/routing/route-health.d.ts.map +1 -0
- package/dist/k8s/routing/route-health.js +77 -0
- package/dist/k8s/routing/route-health.js.map +1 -0
- package/dist/k8s/routing/routing-mode.d.ts +45 -0
- package/dist/k8s/routing/routing-mode.d.ts.map +1 -0
- package/dist/k8s/routing/routing-mode.js +141 -0
- package/dist/k8s/routing/routing-mode.js.map +1 -0
- package/dist/k8s/routing/session-endpoints.d.ts +25 -0
- package/dist/k8s/routing/session-endpoints.d.ts.map +1 -0
- package/dist/k8s/routing/session-endpoints.js +36 -0
- package/dist/k8s/routing/session-endpoints.js.map +1 -0
- package/dist/k8s/routing/session-router.d.ts +57 -0
- package/dist/k8s/routing/session-router.d.ts.map +1 -0
- package/dist/k8s/routing/session-router.js +2 -0
- package/dist/k8s/routing/session-router.js.map +1 -0
- package/dist/k8s/routing/session-service.d.ts +14 -0
- package/dist/k8s/routing/session-service.d.ts.map +1 -0
- package/dist/k8s/routing/session-service.js +42 -0
- package/dist/k8s/routing/session-service.js.map +1 -0
- package/dist/k8s/watch-session.d.ts +7 -5
- package/dist/k8s/watch-session.d.ts.map +1 -1
- package/dist/k8s/watch-session.js +90 -83
- package/dist/k8s/watch-session.js.map +1 -1
- package/dist/k8s/workspace-configmap.d.ts.map +1 -1
- package/dist/k8s/workspace-configmap.js +3 -12
- package/dist/k8s/workspace-configmap.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +37 -4
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/src/bundle-store.ts +6 -19
- package/src/capabilities.ts +4 -4
- package/src/config.ts +130 -68
- package/src/k8s/api-error.test.ts +89 -0
- package/src/k8s/api-error.ts +128 -0
- package/src/k8s/backend.ts +103 -58
- package/src/k8s/client.ts +10 -3
- package/src/k8s/pod-spec.test.ts +57 -2
- package/src/k8s/pod-spec.ts +25 -36
- package/src/k8s/pod-status.ts +10 -2
- package/src/k8s/routing/create-or-replace.ts +16 -0
- package/src/k8s/routing/gateway-router.test.ts +236 -0
- package/src/k8s/routing/gateway-router.ts +219 -0
- package/src/k8s/routing/index.ts +44 -0
- package/src/k8s/routing/ingress-router.test.ts +236 -0
- package/src/k8s/routing/ingress-router.ts +157 -0
- package/src/k8s/routing/route-health.test.ts +215 -0
- package/src/k8s/routing/route-health.ts +95 -0
- package/src/k8s/routing/routing-mode.test.ts +218 -0
- package/src/k8s/routing/routing-mode.ts +191 -0
- package/src/k8s/routing/session-endpoints.ts +45 -0
- package/src/k8s/routing/session-router.ts +59 -0
- package/src/k8s/routing/session-service.ts +55 -0
- package/src/k8s/watch-pod-spec.test.ts +0 -1
- package/src/k8s/watch-session.ts +90 -77
- package/src/k8s/workspace-configmap.ts +3 -15
- package/src/server.ts +41 -4
- package/dist/k8s/image-build.d.ts +0 -103
- package/dist/k8s/image-build.d.ts.map +0 -1
- package/dist/k8s/image-build.js +0 -427
- package/dist/k8s/image-build.js.map +0 -1
- package/dist/k8s/ingress.d.ts +0 -15
- package/dist/k8s/ingress.d.ts.map +0 -1
- package/dist/k8s/ingress.js +0 -102
- package/dist/k8s/ingress.js.map +0 -1
- package/src/k8s/image-build.test.ts +0 -238
- package/src/k8s/image-build.ts +0 -540
- package/src/k8s/ingress.test.ts +0 -146
- package/src/k8s/ingress.ts +0 -127
package/dist/config.d.ts
CHANGED
|
@@ -35,59 +35,69 @@ export interface LimitCeilings {
|
|
|
35
35
|
ephemeralStorage: string;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* Which routing layer the runner publishes per-session routes on.
|
|
39
|
+
*
|
|
40
|
+
* - `auto` — resolve from what is CONFIGURED, then from what the cluster
|
|
41
|
+
* serves; refuse at boot when the answer is genuinely ambiguous.
|
|
42
|
+
* - `ingress` — `networking.k8s.io/v1` Ingress.
|
|
43
|
+
* - `gateway` — `gateway.networking.k8s.io/v1` HTTPRoute.
|
|
44
|
+
* - `none` — publish nothing; the session is logs-only even with a base
|
|
45
|
+
* domain set.
|
|
42
46
|
*/
|
|
43
|
-
export
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
export type SessionRoutingMode = "auto" | "ingress" | "gateway" | "none";
|
|
48
|
+
/** The Gateway a session's HTTPRoute attaches to. Never detected: picking one of
|
|
49
|
+
* several Gateways is the same guess as picking one of two routing APIs, and a
|
|
50
|
+
* route attached to the wrong Gateway fails exactly as silently as no route. */
|
|
51
|
+
export interface GatewayParentConfig {
|
|
52
|
+
name: string;
|
|
53
|
+
/** Defaults to the session namespace — mirroring Gateway API's own
|
|
54
|
+
* `parentRefs[].namespace` default, which is the route's own namespace. */
|
|
48
55
|
namespace: string;
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
*
|
|
62
|
-
|
|
56
|
+
/** Optional listener name, when the Gateway has several and only one should
|
|
57
|
+
* carry session traffic. */
|
|
58
|
+
sectionName?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface SessionRoutingConfig {
|
|
61
|
+
mode: SessionRoutingMode;
|
|
62
|
+
/** Wildcard base domain for per-session hosts; unset → logs-only whatever the
|
|
63
|
+
* mode, since without it no host can be constructed at all. */
|
|
64
|
+
baseDomain?: string;
|
|
65
|
+
/** IngressClass for created session Ingresses (ingress mode). */
|
|
66
|
+
ingressClassName?: string;
|
|
67
|
+
/** `kubernetes.io/tls` Secret (in the session namespace) the per-session
|
|
68
|
+
* Ingress presents so an upstream (e.g. Cloudflare Full (Strict)) can validate
|
|
69
|
+
* the origin. Must cover `*.<baseDomain>`. INGRESS MODE ONLY — under Gateway
|
|
70
|
+
* API the certificate belongs to the Gateway's listener, so setting this in
|
|
71
|
+
* gateway mode is refused at boot rather than silently ignored. */
|
|
72
|
+
tlsSecretName?: string;
|
|
73
|
+
gateway?: GatewayParentConfig;
|
|
74
|
+
/** How long a published route may go unclaimed by any controller before it is
|
|
75
|
+
* reported `unprogrammed`. */
|
|
76
|
+
routeReadyTimeoutMs: number;
|
|
63
77
|
}
|
|
64
78
|
export interface K8sRunnerConfig extends RunnerCoreConfig {
|
|
65
79
|
/** Identity advertised on `/v1/capabilities` — studio labels the runner
|
|
66
80
|
* with these. */
|
|
67
81
|
displayName: string;
|
|
68
82
|
description: string;
|
|
69
|
-
/** Namespace where session Pods/Services
|
|
83
|
+
/** Namespace where session Pods/Services and routing objects are created. */
|
|
70
84
|
sessionNamespace: string;
|
|
71
85
|
/** Default image for spawned session Pods (telorun/node). Baked into the runner
|
|
72
86
|
* image as `RUNNER_IMAGE` at build time (the kernel version the runner was
|
|
73
87
|
* built against); the literal fallback below is for a source checkout only. */
|
|
74
88
|
defaultImage: string;
|
|
75
|
-
/** Small image for the bundle
|
|
89
|
+
/** Small image for the bundle-fetch initContainer (needs wget + tar). */
|
|
76
90
|
initImage: string;
|
|
91
|
+
/** Optional dockerconfig Secret (in the session namespace) the kubelet pulls
|
|
92
|
+
* session images with — the kernel image, and any operator catalog image
|
|
93
|
+
* (`RUNNER_APPS`) held in a private registry. */
|
|
94
|
+
imagePullSecret?: string;
|
|
77
95
|
/** Optional sandbox RuntimeClass (gvisor/kata). Unset → cluster default (runc). */
|
|
78
96
|
runtimeClass?: string;
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
/** Optional `kubernetes.io/tls` Secret (in the session namespace) the per-session
|
|
84
|
-
* Ingress presents so an upstream (e.g. Cloudflare Full (Strict)) can validate
|
|
85
|
-
* the origin. Must cover the wildcard `*.<sessionIngressBaseDomain>`. Unset → no TLS block. */
|
|
86
|
-
sessionIngressTlsSecretName?: string;
|
|
87
|
-
/** On-cluster image build — the only delivery path (always present). */
|
|
88
|
-
build: ImageBuildConfig;
|
|
89
|
-
/** Runner's own in-cluster base URL, used to build the bundle/build-context
|
|
90
|
-
* fetch URL the initContainer curls (e.g. http://k8s-runner.telo-runner:8062). */
|
|
97
|
+
/** How per-session routes are published. */
|
|
98
|
+
sessionRouting: SessionRoutingConfig;
|
|
99
|
+
/** Runner's own in-cluster base URL, used to build the bundle fetch URL the
|
|
100
|
+
* initContainer curls (e.g. http://k8s-runner.telo-runner:8062). */
|
|
91
101
|
selfUrl: string;
|
|
92
102
|
/** Label applied to every session object, used for orphan reaping. */
|
|
93
103
|
managedByLabel: string;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,CAAC;IAClB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,CAAC;IAClB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAIzE;;iFAEiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb;gFAC4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB;iCAC6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB;oEACgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;wEAIoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B;mCAC+B;IAC/B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD;sBACkB;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB;;oFAEgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB;;sDAEkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,cAAc,EAAE,oBAAoB,CAAC;IACrC;yEACqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,aAAa,CAAC;IACtB;;;6BAGyB;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,+EAA+E;IAC/E,gBAAgB,EAAE,sBAAsB,CAAC;CAC1C;AAyGD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,eAAe,CAmD3E"}
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadCoreConfig, parseBool, parsePositiveInt, RunnerConfigError, } from "@telorun/runner-core";
|
|
2
2
|
export { RunnerConfigError };
|
|
3
|
+
const ROUTING_MODES = ["auto", "ingress", "gateway", "none"];
|
|
3
4
|
const DEFAULT_PORT = 8062;
|
|
4
5
|
/** Compile a single optional regex env var into a one-element `RegExp[]`. */
|
|
5
6
|
function parseTagRegex(raw, field) {
|
|
@@ -13,6 +14,53 @@ function parseTagRegex(raw, field) {
|
|
|
13
14
|
throw new RunnerConfigError(`${field} is not a valid regular expression: ${err.message}`);
|
|
14
15
|
}
|
|
15
16
|
}
|
|
17
|
+
/** Parse the routing mode, THROWING on an unrecognized one rather than degrading
|
|
18
|
+
* to a default — a typo'd mode that silently fell back to `auto` is the same
|
|
19
|
+
* class of failure this whole mechanism exists to remove. */
|
|
20
|
+
function parseRoutingMode(raw) {
|
|
21
|
+
const v = raw?.trim();
|
|
22
|
+
if (!v)
|
|
23
|
+
return "auto";
|
|
24
|
+
if (ROUTING_MODES.includes(v))
|
|
25
|
+
return v;
|
|
26
|
+
throw new RunnerConfigError(`SESSION_ROUTING_MODE must be one of ${ROUTING_MODES.join(" | ")} (got '${v}').`);
|
|
27
|
+
}
|
|
28
|
+
function loadSessionRoutingConfig(env, sessionNamespace) {
|
|
29
|
+
const mode = parseRoutingMode(env.SESSION_ROUTING_MODE);
|
|
30
|
+
const gatewayName = env.SESSION_GATEWAY_NAME?.trim() || undefined;
|
|
31
|
+
const tlsSecretName = env.SESSION_INGRESS_TLS_SECRET?.trim() || undefined;
|
|
32
|
+
// A certificate configured for a layer that will never present it is a silent
|
|
33
|
+
// downgrade to plaintext origin traffic, so it is refused rather than ignored.
|
|
34
|
+
// This is only the FAST PATH, for the mode that states the layer outright: the
|
|
35
|
+
// authoritative refusal is at routing resolution, because under `auto` a named
|
|
36
|
+
// Gateway decides the layer and nothing here can see that yet.
|
|
37
|
+
if (mode === "gateway" && tlsSecretName) {
|
|
38
|
+
throw new RunnerConfigError("SESSION_INGRESS_TLS_SECRET is set but SESSION_ROUTING_MODE=gateway. Under Gateway API the " +
|
|
39
|
+
"origin certificate belongs to the Gateway listener's own `tls.certificateRefs`, not to the " +
|
|
40
|
+
"per-session route. Move the Secret reference to the Gateway and unset this.");
|
|
41
|
+
}
|
|
42
|
+
if (mode === "gateway" && !gatewayName) {
|
|
43
|
+
throw new RunnerConfigError("SESSION_ROUTING_MODE=gateway requires SESSION_GATEWAY_NAME — the Gateway each session's " +
|
|
44
|
+
"HTTPRoute attaches to. Set it (and SESSION_GATEWAY_NAMESPACE when the Gateway is not in " +
|
|
45
|
+
`'${sessionNamespace}').`);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
mode,
|
|
49
|
+
baseDomain: env.SESSION_ROUTING_BASE_DOMAIN?.trim() || undefined,
|
|
50
|
+
ingressClassName: env.SESSION_INGRESS_CLASS?.trim() || undefined,
|
|
51
|
+
tlsSecretName,
|
|
52
|
+
...(gatewayName
|
|
53
|
+
? {
|
|
54
|
+
gateway: {
|
|
55
|
+
name: gatewayName,
|
|
56
|
+
namespace: env.SESSION_GATEWAY_NAMESPACE?.trim() || sessionNamespace,
|
|
57
|
+
sectionName: env.SESSION_GATEWAY_SECTION_NAME?.trim() || undefined,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
: {}),
|
|
61
|
+
routeReadyTimeoutMs: parsePositiveInt(env.SESSION_ROUTE_READY_TIMEOUT_SECONDS, 60, "SESSION_ROUTE_READY_TIMEOUT_SECONDS") * 1000,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
16
64
|
function loadBaseImageCatalogConfig(env) {
|
|
17
65
|
return {
|
|
18
66
|
enabled: parseBool(env.RUNNER_BASE_IMAGE_CATALOG_ENABLED, true, "RUNNER_BASE_IMAGE_CATALOG_ENABLED"),
|
|
@@ -34,42 +82,33 @@ export function loadK8sRunnerConfig(env) {
|
|
|
34
82
|
throw new RunnerConfigError("RUNNER_SELF_URL env var is required. Set it to the runner's in-cluster base URL " +
|
|
35
83
|
"(e.g. http://k8s-runner.telo-runner.svc:8062) so session initContainers can fetch the bundle.");
|
|
36
84
|
}
|
|
37
|
-
const
|
|
38
|
-
if (!repository) {
|
|
39
|
-
throw new RunnerConfigError("RUNNER_IMAGE_REPOSITORY env var is required. The runner prebuilds a per-app session image for " +
|
|
40
|
-
"every run (there is no in-pod install fallback); set it to the registry repository the built " +
|
|
41
|
-
"images are pushed to / pulled from (e.g. registry.example.com/telo-sessions). The tag is the " +
|
|
42
|
-
"bundle content hash.");
|
|
43
|
-
}
|
|
44
|
-
const build = {
|
|
45
|
-
repository: repository.replace(/\/+$/, ""),
|
|
46
|
-
namespace: env.RUNNER_BUILD_NAMESPACE?.trim() || "telo-builds",
|
|
47
|
-
builderImage: env.RUNNER_BUILDER_IMAGE?.trim() || "gcr.io/kaniko-project/executor:latest",
|
|
48
|
-
timeoutSeconds: parsePositiveInt(env.RUNNER_BUILD_TIMEOUT_SECONDS, 600, "RUNNER_BUILD_TIMEOUT_SECONDS"),
|
|
49
|
-
insecureRegistry: env.RUNNER_REGISTRY_INSECURE?.trim() === "true",
|
|
50
|
-
registryApiUrl: env.RUNNER_REGISTRY_API_URL?.trim() || undefined,
|
|
51
|
-
pushSecretName: env.RUNNER_REGISTRY_PUSH_SECRET?.trim() || undefined,
|
|
52
|
-
imagePullSecret: env.RUNNER_IMAGE_PULL_SECRET?.trim() || undefined,
|
|
53
|
-
};
|
|
85
|
+
const sessionNamespace = env.RUNNER_SESSION_NAMESPACE?.trim() || "telo-sessions";
|
|
54
86
|
return {
|
|
55
87
|
...loadCoreConfig(env, { port: DEFAULT_PORT }),
|
|
56
88
|
displayName: env.RUNNER_DISPLAY_NAME?.trim() || "Telo Runner",
|
|
57
89
|
description: env.RUNNER_DESCRIPTION?.trim() || "Runs the Telo application in a cloud environment",
|
|
58
|
-
sessionNamespace
|
|
90
|
+
sessionNamespace,
|
|
59
91
|
defaultImage: env.RUNNER_IMAGE?.trim() || "telorun/node:latest-slim",
|
|
60
92
|
initImage: env.RUNNER_INIT_IMAGE?.trim() || "busybox:stable",
|
|
93
|
+
imagePullSecret: env.RUNNER_IMAGE_PULL_SECRET?.trim() || undefined,
|
|
61
94
|
runtimeClass: env.RUNNER_RUNTIME_CLASS?.trim() || undefined,
|
|
62
|
-
|
|
63
|
-
sessionIngressClassName: env.SESSION_INGRESS_CLASS?.trim() || undefined,
|
|
64
|
-
sessionIngressTlsSecretName: env.SESSION_INGRESS_TLS_SECRET?.trim() || undefined,
|
|
65
|
-
build,
|
|
95
|
+
sessionRouting: loadSessionRoutingConfig(env, sessionNamespace),
|
|
66
96
|
selfUrl: selfUrl.replace(/\/+$/, ""),
|
|
67
97
|
managedByLabel: env.RUNNER_MANAGED_BY?.trim() || "telo-k8s-runner",
|
|
98
|
+
// Sized for a pod that RESOLVES ITS OWN module closure. They used to be
|
|
99
|
+
// 50m / 100Mi / 512Mi, which described a different workload: the closure
|
|
100
|
+
// arrived in image layers, so the pod only ran it. It now downloads,
|
|
101
|
+
// unpacks and resolves it in-pod, into an emptyDir that counts against
|
|
102
|
+
// ephemeral-storage — so the old numbers meant an OOMKill on the memory
|
|
103
|
+
// ceiling and an eviction on the storage one, for the ordinary case. The
|
|
104
|
+
// watch path already ran this workload and already carried the roomier
|
|
105
|
+
// ceiling; these match it. `appLimits` stays separate: it still differs in
|
|
106
|
+
// TTL, and both remain the operator's policy to tighten.
|
|
68
107
|
limits: {
|
|
69
|
-
cpu: env.RUNNER_MAX_CPU?.trim() || "
|
|
70
|
-
memory: env.RUNNER_MAX_MEMORY?.trim() || "
|
|
108
|
+
cpu: env.RUNNER_MAX_CPU?.trim() || "500m",
|
|
109
|
+
memory: env.RUNNER_MAX_MEMORY?.trim() || "512Mi",
|
|
71
110
|
ttlSeconds: parsePositiveInt(env.RUNNER_MAX_TTL_SECONDS, 3600, "RUNNER_MAX_TTL_SECONDS"),
|
|
72
|
-
ephemeralStorage: env.RUNNER_MAX_EPHEMERAL_STORAGE?.trim() || "
|
|
111
|
+
ephemeralStorage: env.RUNNER_MAX_EPHEMERAL_STORAGE?.trim() || "1Gi",
|
|
73
112
|
},
|
|
74
113
|
appLimits: {
|
|
75
114
|
cpu: env.RUNNER_APP_MAX_CPU?.trim() || "500m",
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,iBAAiB,GAGlB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,iBAAiB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,iBAAiB,GAGlB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAkD7B,MAAM,aAAa,GAAkC,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAsE5F,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,6EAA6E;AAC7E,SAAS,aAAa,CAAC,GAAuB,EAAE,KAAa;IAC3D,MAAM,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC;IACtB,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,iBAAiB,CACzB,GAAG,KAAK,uCAAwC,GAAa,CAAC,OAAO,EAAE,CACxE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;8DAE8D;AAC9D,SAAS,gBAAgB,CAAC,GAAuB;IAC/C,MAAM,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC;IACtB,IAAI,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACtB,IAAK,aAAmC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,CAAuB,CAAC;IACrF,MAAM,IAAI,iBAAiB,CACzB,uCAAuC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CACjF,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAC/B,GAAsB,EACtB,gBAAwB;IAExB,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAClE,MAAM,aAAa,GAAG,GAAG,CAAC,0BAA0B,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAE1E,8EAA8E;IAC9E,+EAA+E;IAC/E,+EAA+E;IAC/E,+EAA+E;IAC/E,+DAA+D;IAC/D,IAAI,IAAI,KAAK,SAAS,IAAI,aAAa,EAAE,CAAC;QACxC,MAAM,IAAI,iBAAiB,CACzB,4FAA4F;YAC1F,6FAA6F;YAC7F,6EAA6E,CAChF,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,IAAI,iBAAiB,CACzB,0FAA0F;YACxF,0FAA0F;YAC1F,IAAI,gBAAgB,KAAK,CAC5B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI;QACJ,UAAU,EAAE,GAAG,CAAC,2BAA2B,EAAE,IAAI,EAAE,IAAI,SAAS;QAChE,gBAAgB,EAAE,GAAG,CAAC,qBAAqB,EAAE,IAAI,EAAE,IAAI,SAAS;QAChE,aAAa;QACb,GAAG,CAAC,WAAW;YACb,CAAC,CAAC;gBACE,OAAO,EAAE;oBACP,IAAI,EAAE,WAAW;oBACjB,SAAS,EAAE,GAAG,CAAC,yBAAyB,EAAE,IAAI,EAAE,IAAI,gBAAgB;oBACpE,WAAW,EAAE,GAAG,CAAC,4BAA4B,EAAE,IAAI,EAAE,IAAI,SAAS;iBACnE;aACF;YACH,CAAC,CAAC,EAAE,CAAC;QACP,mBAAmB,EACjB,gBAAgB,CACd,GAAG,CAAC,mCAAmC,EACvC,EAAE,EACF,qCAAqC,CACtC,GAAG,IAAI;KACX,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CAAC,GAAsB;IACxD,OAAO;QACL,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,iCAAiC,EAAE,IAAI,EAAE,mCAAmC,CAAC;QACpG,UAAU,EAAE,GAAG,CAAC,sBAAsB,EAAE,IAAI,EAAE,IAAI,cAAc;QAChE,MAAM,EAAE;YACN,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,6BAA6B,EAAE,IAAI,EAAE,+BAA+B,CAAC;YAC/F,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,6BAA6B,EAAE,IAAI,EAAE,+BAA+B,CAAC;YAC/F,iBAAiB,EAAE,SAAS,CAC1B,GAAG,CAAC,oCAAoC,EACxC,IAAI,EACJ,sCAAsC,CACvC;YACD,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,yBAAyB,EAAE,2BAA2B,CAAC;YAClF,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,yBAAyB,EAAE,2BAA2B,CAAC;SACnF;QACD,KAAK,EAAE,gBAAgB,CAAC,GAAG,CAAC,uBAAuB,EAAE,EAAE,EAAE,yBAAyB,CAAC;QACnF,iBAAiB,EACf,gBAAgB,CACd,GAAG,CAAC,iCAAiC,EACrC,IAAI,EACJ,mCAAmC,CACpC,GAAG,IAAI;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAsB;IACxD,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,iBAAiB,CACzB,kFAAkF;YAChF,+FAA+F,CAClG,CAAC;IACJ,CAAC;IAED,MAAM,gBAAgB,GAAG,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE,IAAI,eAAe,CAAC;IAEjF,OAAO;QACL,GAAG,cAAc,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QAC9C,WAAW,EAAE,GAAG,CAAC,mBAAmB,EAAE,IAAI,EAAE,IAAI,aAAa;QAC7D,WAAW,EACT,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,kDAAkD;QACtF,gBAAgB;QAChB,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,0BAA0B;QACpE,SAAS,EAAE,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,gBAAgB;QAC5D,eAAe,EAAE,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE,IAAI,SAAS;QAClE,YAAY,EAAE,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,IAAI,SAAS;QAC3D,cAAc,EAAE,wBAAwB,CAAC,GAAG,EAAE,gBAAgB,CAAC;QAC/D,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,cAAc,EAAE,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,iBAAiB;QAClE,wEAAwE;QACxE,yEAAyE;QACzE,qEAAqE;QACrE,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,yDAAyD;QACzD,MAAM,EAAE;YACN,GAAG,EAAE,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,MAAM;YACzC,MAAM,EAAE,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,OAAO;YAChD,UAAU,EAAE,gBAAgB,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,EAAE,wBAAwB,CAAC;YACxF,gBAAgB,EAAE,GAAG,CAAC,4BAA4B,EAAE,IAAI,EAAE,IAAI,KAAK;SACpE;QACD,SAAS,EAAE;YACT,GAAG,EAAE,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,MAAM;YAC7C,MAAM,EAAE,GAAG,CAAC,qBAAqB,EAAE,IAAI,EAAE,IAAI,OAAO;YACpD,UAAU,EAAE,gBAAgB,CAC1B,GAAG,CAAC,0BAA0B,EAC9B,KAAK,EACL,4BAA4B,CAC7B;YACD,gBAAgB,EAAE,GAAG,CAAC,gCAAgC,EAAE,IAAI,EAAE,IAAI,KAAK;SACxE;QACD,gBAAgB,EAAE,0BAA0B,CAAC,GAAG,CAAC;KAClD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { SessionStartError, type StartFailureStage } from "@telorun/runner-core";
|
|
2
|
+
/**
|
|
3
|
+
* A Kubernetes API rejection, phrased for the person who clicked Run.
|
|
4
|
+
*
|
|
5
|
+
* `ApiException.message` is a full HTTP dump — status line, the raw `Status`
|
|
6
|
+
* body and every response header — and a start failure's message travels
|
|
7
|
+
* verbatim to the client as the session's terminal `failed` status. That put
|
|
8
|
+
* the runner's ServiceAccount name, the request's audit id and its flowschema
|
|
9
|
+
* UIDs on an end user's screen, none of which they can act on.
|
|
10
|
+
*
|
|
11
|
+
* So the client sees the operation, the HTTP status and the API's own one-word
|
|
12
|
+
* `reason` — enough for an operator to know what was refused — and never the
|
|
13
|
+
* body or the headers. The raw exception rides along as the error's `cause`,
|
|
14
|
+
* which the runner's log serializer records, so nothing is swallowed: the detail
|
|
15
|
+
* moves to the log, it does not disappear.
|
|
16
|
+
*/
|
|
17
|
+
export declare function apiFailure(err: unknown, stage: StartFailureStage, action: string): SessionStartError;
|
|
18
|
+
/** Attach the original error as `cause` so a log serializer can reach it.
|
|
19
|
+
* Every wrap on a failure path goes through here — a rewrap that drops the
|
|
20
|
+
* cause silently undoes the whole point of summarizing the message. */
|
|
21
|
+
export declare function withCause<E extends Error>(error: E, cause: unknown): E;
|
|
22
|
+
/** The client-safe half of a Kubernetes API error: what was refused, and who
|
|
23
|
+
* can act on it — never the `Status` message, which names cluster identities. */
|
|
24
|
+
export declare function apiReason(err: unknown): string;
|
|
25
|
+
/**
|
|
26
|
+
* The HTTP status an error carries, or `undefined` when it carries none.
|
|
27
|
+
*
|
|
28
|
+
* The number check is the whole point: `code` is also where Node puts a system
|
|
29
|
+
* error's string code and where a `DOMException` puts its legacy numeric one, so
|
|
30
|
+
* an unreachable apiserver and an aborted request both arrive here looking like
|
|
31
|
+
* a status. Only a value in the HTTP range is one.
|
|
32
|
+
*/
|
|
33
|
+
export declare function statusCode(err: unknown): number | undefined;
|
|
34
|
+
//# sourceMappingURL=api-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-error.d.ts","sourceRoot":"","sources":["../../src/k8s/api-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEjF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,MAAM,GACb,iBAAiB,CAKnB;AAED;;wEAEwE;AACxE,wBAAgB,SAAS,CAAC,CAAC,SAAS,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAGtE;AAED;kFACkF;AAClF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAoB9C;AAuDD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAM3D"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { SessionStartError } from "@telorun/runner-core";
|
|
2
|
+
/**
|
|
3
|
+
* A Kubernetes API rejection, phrased for the person who clicked Run.
|
|
4
|
+
*
|
|
5
|
+
* `ApiException.message` is a full HTTP dump — status line, the raw `Status`
|
|
6
|
+
* body and every response header — and a start failure's message travels
|
|
7
|
+
* verbatim to the client as the session's terminal `failed` status. That put
|
|
8
|
+
* the runner's ServiceAccount name, the request's audit id and its flowschema
|
|
9
|
+
* UIDs on an end user's screen, none of which they can act on.
|
|
10
|
+
*
|
|
11
|
+
* So the client sees the operation, the HTTP status and the API's own one-word
|
|
12
|
+
* `reason` — enough for an operator to know what was refused — and never the
|
|
13
|
+
* body or the headers. The raw exception rides along as the error's `cause`,
|
|
14
|
+
* which the runner's log serializer records, so nothing is swallowed: the detail
|
|
15
|
+
* moves to the log, it does not disappear.
|
|
16
|
+
*/
|
|
17
|
+
export function apiFailure(err, stage, action) {
|
|
18
|
+
return withCause(new SessionStartError("start_failed", stage, `${action}: ${apiReason(err)}`), err);
|
|
19
|
+
}
|
|
20
|
+
/** Attach the original error as `cause` so a log serializer can reach it.
|
|
21
|
+
* Every wrap on a failure path goes through here — a rewrap that drops the
|
|
22
|
+
* cause silently undoes the whole point of summarizing the message. */
|
|
23
|
+
export function withCause(error, cause) {
|
|
24
|
+
error.cause = cause;
|
|
25
|
+
return error;
|
|
26
|
+
}
|
|
27
|
+
/** The client-safe half of a Kubernetes API error: what was refused, and who
|
|
28
|
+
* can act on it — never the `Status` message, which names cluster identities. */
|
|
29
|
+
export function apiReason(err) {
|
|
30
|
+
const code = statusCode(err);
|
|
31
|
+
// No HTTP status means the request never got an answer: a refused connection,
|
|
32
|
+
// a DNS failure, an abort. Those carry a `code` too (`ECONNREFUSED`, an
|
|
33
|
+
// `ABORT_ERR` number), which is why `statusCode` accepts only what an HTTP
|
|
34
|
+
// response could have produced — reporting `HTTP ECONNREFUSED` claimed the
|
|
35
|
+
// apiserver rejected something when it was never reached, which is exactly
|
|
36
|
+
// the case where the difference matters.
|
|
37
|
+
if (code === undefined)
|
|
38
|
+
return "the Kubernetes API could not be reached";
|
|
39
|
+
const reason = statusReason(err);
|
|
40
|
+
const detail = reason ? `${reason}, HTTP ${code}` : `HTTP ${code}`;
|
|
41
|
+
const cause = refusalCause(err);
|
|
42
|
+
if (cause === "quota") {
|
|
43
|
+
return `the session namespace's resource quota is exhausted (${detail})`;
|
|
44
|
+
}
|
|
45
|
+
if (cause === "rbac") {
|
|
46
|
+
return `the runner is not permitted to do this (${detail}). The cluster operator must check the runner's RBAC.`;
|
|
47
|
+
}
|
|
48
|
+
return `the Kubernetes API rejected the request (${detail})`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Which of the two things a `403 Forbidden` means here.
|
|
52
|
+
*
|
|
53
|
+
* The status code alone cannot say: an RBAC denial and a `ResourceQuota`
|
|
54
|
+
* rejection are both `403` with `reason: Forbidden`, and this chart ships a
|
|
55
|
+
* quota (32 pods) enabled by default — so blaming RBAC on every 403 tells the
|
|
56
|
+
* operator to re-check a Role at exactly the moment the cluster is simply full.
|
|
57
|
+
* The two also differ in when they happen: an RBAC 403 is an install-time
|
|
58
|
+
* mistake, a quota 403 arrives under load.
|
|
59
|
+
*
|
|
60
|
+
* The only thing that separates them is the admission message, which is why it
|
|
61
|
+
* is READ here and never echoed: the classification crosses the boundary, the
|
|
62
|
+
* text does not. An unrecognized 403 degrades to the neutral wording rather than
|
|
63
|
+
* to a guess.
|
|
64
|
+
*/
|
|
65
|
+
function refusalCause(err) {
|
|
66
|
+
const code = statusCode(err);
|
|
67
|
+
if (code !== 401 && code !== 403)
|
|
68
|
+
return undefined;
|
|
69
|
+
const message = statusMessage(err);
|
|
70
|
+
if (message && /exceeded quota|forbidden: failed quota/i.test(message))
|
|
71
|
+
return "quota";
|
|
72
|
+
if (!message || /\bis forbidden: User\b|\bcannot \w+ resource\b/i.test(message))
|
|
73
|
+
return "rbac";
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
/** The API `Status` object, however the client-node deserializer left it: a
|
|
77
|
+
* parsed object for a status code it recognizes, the raw JSON text for one it
|
|
78
|
+
* does not (which is the 403 case, reported as "Unknown API Status Code!"). */
|
|
79
|
+
function status(err) {
|
|
80
|
+
const body = err?.body;
|
|
81
|
+
const parsed = typeof body === "string" ? parseJson(body) : body;
|
|
82
|
+
return parsed && typeof parsed === "object" ? parsed : undefined;
|
|
83
|
+
}
|
|
84
|
+
/** `Forbidden`, `NotFound`, `Invalid`, … — the one word safe to pass on. */
|
|
85
|
+
function statusReason(err) {
|
|
86
|
+
const reason = status(err)?.reason;
|
|
87
|
+
return typeof reason === "string" && reason.trim() !== "" ? reason : undefined;
|
|
88
|
+
}
|
|
89
|
+
/** Read for classification only — it names cluster identities and never leaves. */
|
|
90
|
+
function statusMessage(err) {
|
|
91
|
+
const message = status(err)?.message;
|
|
92
|
+
return typeof message === "string" ? message : undefined;
|
|
93
|
+
}
|
|
94
|
+
function parseJson(text) {
|
|
95
|
+
try {
|
|
96
|
+
return JSON.parse(text);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The HTTP status an error carries, or `undefined` when it carries none.
|
|
104
|
+
*
|
|
105
|
+
* The number check is the whole point: `code` is also where Node puts a system
|
|
106
|
+
* error's string code and where a `DOMException` puts its legacy numeric one, so
|
|
107
|
+
* an unreachable apiserver and an aborted request both arrive here looking like
|
|
108
|
+
* a status. Only a value in the HTTP range is one.
|
|
109
|
+
*/
|
|
110
|
+
export function statusCode(err) {
|
|
111
|
+
const e = err;
|
|
112
|
+
for (const candidate of [e?.statusCode, e?.code, e?.response?.statusCode]) {
|
|
113
|
+
if (typeof candidate === "number" && candidate >= 100 && candidate <= 599)
|
|
114
|
+
return candidate;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=api-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-error.js","sourceRoot":"","sources":["../../src/k8s/api-error.ts"],"names":[],"mappings":"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"}
|
package/dist/k8s/backend.d.ts
CHANGED
|
@@ -2,11 +2,15 @@ import type { RunnerBackend } from "@telorun/runner-core";
|
|
|
2
2
|
import type { BundleStore } from "../bundle-store.js";
|
|
3
3
|
import type { K8sRunnerConfig } from "../config.js";
|
|
4
4
|
import type { KubeClient } from "./client.js";
|
|
5
|
+
import { type SessionRouter } from "./routing/index.js";
|
|
5
6
|
export { podFailureMessage } from "./pod-status.js";
|
|
6
7
|
export interface K8sBackendDeps {
|
|
7
8
|
kube: KubeClient;
|
|
8
9
|
config: K8sRunnerConfig;
|
|
9
10
|
bundleStore: BundleStore;
|
|
11
|
+
/** How this cluster publishes session routes, resolved once at boot. Absent →
|
|
12
|
+
* logs-only: no base domain, or `SESSION_ROUTING_MODE=none`. */
|
|
13
|
+
router?: SessionRouter;
|
|
10
14
|
}
|
|
11
15
|
export declare function createKubernetesBackend(deps: K8sBackendDeps): RunnerBackend;
|
|
12
16
|
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/k8s/backend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAOV,aAAa,EACd,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/k8s/backend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAOV,aAAa,EACd,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAc5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAepD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB;qEACiE;IACjE,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,cAAc,GAAG,aAAa,CA8a3E"}
|