@telorun/k8s-runner 0.14.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.
Files changed (90) hide show
  1. package/README.md +93 -14
  2. package/dist/config.d.ts +44 -9
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +51 -4
  5. package/dist/config.js.map +1 -1
  6. package/dist/k8s/backend.d.ts +4 -0
  7. package/dist/k8s/backend.d.ts.map +1 -1
  8. package/dist/k8s/backend.js +55 -17
  9. package/dist/k8s/backend.js.map +1 -1
  10. package/dist/k8s/client.d.ts +7 -1
  11. package/dist/k8s/client.d.ts.map +1 -1
  12. package/dist/k8s/client.js +3 -1
  13. package/dist/k8s/client.js.map +1 -1
  14. package/dist/k8s/pod-status.d.ts +5 -0
  15. package/dist/k8s/pod-status.d.ts.map +1 -1
  16. package/dist/k8s/pod-status.js +7 -0
  17. package/dist/k8s/pod-status.js.map +1 -1
  18. package/dist/k8s/routing/create-or-replace.d.ts +5 -0
  19. package/dist/k8s/routing/create-or-replace.d.ts.map +1 -0
  20. package/dist/k8s/routing/create-or-replace.js +15 -0
  21. package/dist/k8s/routing/create-or-replace.js.map +1 -0
  22. package/dist/k8s/routing/gateway-router.d.ts +5 -0
  23. package/dist/k8s/routing/gateway-router.d.ts.map +1 -0
  24. package/dist/k8s/routing/gateway-router.js +178 -0
  25. package/dist/k8s/routing/gateway-router.js.map +1 -0
  26. package/dist/k8s/routing/index.d.ts +24 -0
  27. package/dist/k8s/routing/index.d.ts.map +1 -0
  28. package/dist/k8s/routing/index.js +27 -0
  29. package/dist/k8s/routing/index.js.map +1 -0
  30. package/dist/k8s/routing/ingress-router.d.ts +13 -0
  31. package/dist/k8s/routing/ingress-router.d.ts.map +1 -0
  32. package/dist/k8s/routing/ingress-router.js +125 -0
  33. package/dist/k8s/routing/ingress-router.js.map +1 -0
  34. package/dist/k8s/routing/route-health.d.ts +34 -0
  35. package/dist/k8s/routing/route-health.d.ts.map +1 -0
  36. package/dist/k8s/routing/route-health.js +77 -0
  37. package/dist/k8s/routing/route-health.js.map +1 -0
  38. package/dist/k8s/routing/routing-mode.d.ts +45 -0
  39. package/dist/k8s/routing/routing-mode.d.ts.map +1 -0
  40. package/dist/k8s/routing/routing-mode.js +141 -0
  41. package/dist/k8s/routing/routing-mode.js.map +1 -0
  42. package/dist/k8s/routing/session-endpoints.d.ts +25 -0
  43. package/dist/k8s/routing/session-endpoints.d.ts.map +1 -0
  44. package/dist/k8s/routing/session-endpoints.js +36 -0
  45. package/dist/k8s/routing/session-endpoints.js.map +1 -0
  46. package/dist/k8s/routing/session-router.d.ts +57 -0
  47. package/dist/k8s/routing/session-router.d.ts.map +1 -0
  48. package/dist/k8s/routing/session-router.js +2 -0
  49. package/dist/k8s/routing/session-router.js.map +1 -0
  50. package/dist/k8s/routing/session-service.d.ts +14 -0
  51. package/dist/k8s/routing/session-service.d.ts.map +1 -0
  52. package/dist/k8s/routing/session-service.js +42 -0
  53. package/dist/k8s/routing/session-service.js.map +1 -0
  54. package/dist/k8s/watch-session.d.ts +3 -0
  55. package/dist/k8s/watch-session.d.ts.map +1 -1
  56. package/dist/k8s/watch-session.js +82 -75
  57. package/dist/k8s/watch-session.js.map +1 -1
  58. package/dist/k8s/workspace-configmap.d.ts.map +1 -1
  59. package/dist/k8s/workspace-configmap.js +3 -12
  60. package/dist/k8s/workspace-configmap.js.map +1 -1
  61. package/dist/server.d.ts.map +1 -1
  62. package/dist/server.js +34 -1
  63. package/dist/server.js.map +1 -1
  64. package/package.json +2 -2
  65. package/src/config.ts +116 -13
  66. package/src/k8s/backend.ts +73 -23
  67. package/src/k8s/client.ts +17 -1
  68. package/src/k8s/pod-status.ts +8 -0
  69. package/src/k8s/routing/create-or-replace.ts +16 -0
  70. package/src/k8s/routing/gateway-router.test.ts +236 -0
  71. package/src/k8s/routing/gateway-router.ts +219 -0
  72. package/src/k8s/routing/index.ts +44 -0
  73. package/src/k8s/routing/ingress-router.test.ts +236 -0
  74. package/src/k8s/routing/ingress-router.ts +157 -0
  75. package/src/k8s/routing/route-health.test.ts +215 -0
  76. package/src/k8s/routing/route-health.ts +95 -0
  77. package/src/k8s/routing/routing-mode.test.ts +218 -0
  78. package/src/k8s/routing/routing-mode.ts +191 -0
  79. package/src/k8s/routing/session-endpoints.ts +45 -0
  80. package/src/k8s/routing/session-router.ts +59 -0
  81. package/src/k8s/routing/session-service.ts +55 -0
  82. package/src/k8s/watch-session.ts +82 -64
  83. package/src/k8s/workspace-configmap.ts +3 -15
  84. package/src/server.ts +38 -1
  85. package/dist/k8s/ingress.d.ts +0 -15
  86. package/dist/k8s/ingress.d.ts.map +0 -1
  87. package/dist/k8s/ingress.js +0 -102
  88. package/dist/k8s/ingress.js.map +0 -1
  89. package/src/k8s/ingress.test.ts +0 -146
  90. package/src/k8s/ingress.ts +0 -127
@@ -1 +1 @@
1
- {"version":3,"file":"pod-status.d.ts","sourceRoot":"","sources":["../../src/k8s/pod-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuC,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;GAKG;AAEH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,SAAS,CAEnE;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAEzD;AAED;sFACsF;AACtF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAYjE;AAgBD,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,SAAS,CAK5E;AAGD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAG7D;AAID;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAOtD;AAyCD,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzF;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE3C;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAQxC"}
1
+ {"version":3,"file":"pod-status.d.ts","sourceRoot":"","sources":["../../src/k8s/pod-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuC,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;GAKG;AAEH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,SAAS,CAEnE;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAEzD;AAED;sFACsF;AACtF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAYjE;AAgBD,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,SAAS,CAK5E;AAGD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAG7D;AAID;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAOtD;AAyCD,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzF;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE3C;AAED;;;4CAG4C;AAC5C,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEhD;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAQxC"}
@@ -122,6 +122,13 @@ export async function deletePod(kube, ns, name) {
122
122
  export function is404(err) {
123
123
  return statusCode(err) === 404;
124
124
  }
125
+ /** A name-already-taken rejection — what makes a create idempotent by falling
126
+ * through to a replace. Reads the status through `statusCode` like every other
127
+ * classification here, so an `ECONNREFUSED` or a `DOMException`'s legacy number
128
+ * in `code` cannot be mistaken for one. */
129
+ export function isConflict(err) {
130
+ return statusCode(err) === 409;
131
+ }
125
132
  export function msg(err) {
126
133
  if (err instanceof Error)
127
134
  return err.message;
@@ -1 +1 @@
1
- {"version":3,"file":"pod-status.js","sourceRoot":"","sources":["../../src/k8s/pod-status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pod-status.js","sourceRoot":"","sources":["../../src/k8s/pod-status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** A create that yields to an existing object of the same name. Every routing
2
+ * object is republished on reload (an app's declared port set changes), so the
3
+ * write has to be idempotent rather than create-once. */
4
+ export declare function createOrReplace(create: () => Promise<unknown>, replace: () => Promise<unknown>): Promise<void>;
5
+ //# sourceMappingURL=create-or-replace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-or-replace.d.ts","sourceRoot":"","sources":["../../../src/k8s/routing/create-or-replace.ts"],"names":[],"mappings":"AAEA;;0DAE0D;AAC1D,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC,CAOf"}
@@ -0,0 +1,15 @@
1
+ import { isConflict } from "../pod-status.js";
2
+ /** A create that yields to an existing object of the same name. Every routing
3
+ * object is republished on reload (an app's declared port set changes), so the
4
+ * write has to be idempotent rather than create-once. */
5
+ export async function createOrReplace(create, replace) {
6
+ try {
7
+ await create();
8
+ }
9
+ catch (err) {
10
+ if (!isConflict(err))
11
+ throw err;
12
+ await replace();
13
+ }
14
+ }
15
+ //# sourceMappingURL=create-or-replace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-or-replace.js","sourceRoot":"","sources":["../../../src/k8s/routing/create-or-replace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;0DAE0D;AAC1D,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAA8B,EAC9B,OAA+B;IAE/B,IAAI,CAAC;QACH,MAAM,MAAM,EAAE,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,MAAM,GAAG,CAAC;QAChC,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { K8sRunnerConfig } from "../../config.js";
2
+ import type { KubeClient } from "../client.js";
3
+ import type { SessionRouter } from "./session-router.js";
4
+ export declare function createGatewayRouter(kube: KubeClient, config: K8sRunnerConfig, apiVersion: string): SessionRouter;
5
+ //# sourceMappingURL=gateway-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-router.d.ts","sourceRoot":"","sources":["../../../src/k8s/routing/gateway-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK/C,OAAO,KAAK,EAIV,aAAa,EACd,MAAM,qBAAqB,CAAC;AA8C7B,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,MAAM,GACjB,aAAa,CA6Jf"}
@@ -0,0 +1,178 @@
1
+ import { is404 } from "../pod-status.js";
2
+ import { createOrReplace } from "./create-or-replace.js";
3
+ import { hostForPort } from "./session-endpoints.js";
4
+ import { podOwnerRef, sessionObjectName } from "./session-service.js";
5
+ const GROUP = "gateway.networking.k8s.io";
6
+ const PLURAL = "httproutes";
7
+ /**
8
+ * ONE HTTPRoute PER PORT, and that is forced by the API rather than chosen:
9
+ * `hostnames` is a property of the whole HTTPRoute, while a `rule` selects on
10
+ * path/header/method — never on host. A single route carrying every session
11
+ * hostname plus one rule per port would therefore send every host to whichever
12
+ * rule matched `/` first, silently collapsing all of a session's ports onto one.
13
+ *
14
+ * An Ingress rule owns its host, which is why that layer needs only one object.
15
+ */
16
+ function routeName(sessionId, port) {
17
+ return `${sessionObjectName(sessionId)}-${port}`;
18
+ }
19
+ function condition(parent, type) {
20
+ return parent.conditions?.find((c) => c.type === type);
21
+ }
22
+ /** Phrase a refusal in the controller's own words — every cause here is fixed in
23
+ * the cluster (a listener that does not admit this namespace, a hostname outside
24
+ * the listener's own), so the controller's `reason` is the actionable part. */
25
+ function refusal(c) {
26
+ const reason = c.reason ?? "rejected";
27
+ return c.message ? `${reason}: ${c.message}` : reason;
28
+ }
29
+ export function createGatewayRouter(kube, config, apiVersion) {
30
+ const ns = config.sessionNamespace;
31
+ const gateway = config.sessionRouting.gateway;
32
+ if (!gateway) {
33
+ // Unreachable through the factory, which refuses this at boot; kept so the
34
+ // invariant is stated where the field is read rather than assumed.
35
+ throw new Error("gateway routing selected without a configured Gateway parent");
36
+ }
37
+ const base = { group: GROUP, version: apiVersion, namespace: ns, plural: PLURAL };
38
+ // Arrow consts, not declarations: a hoisted `function` resets the narrowing of
39
+ // `gateway` above, since TypeScript cannot prove it is not called earlier.
40
+ const buildRoute = (args, port) => {
41
+ return {
42
+ apiVersion: `${GROUP}/${apiVersion}`,
43
+ kind: "HTTPRoute",
44
+ metadata: {
45
+ name: routeName(args.sessionId, port),
46
+ namespace: ns,
47
+ labels: {
48
+ "app.kubernetes.io/managed-by": config.managedByLabel,
49
+ "telo.run/session-id": args.sessionId,
50
+ },
51
+ ownerReferences: [podOwnerRef(args.podName, args.podUid)],
52
+ },
53
+ spec: {
54
+ parentRefs: [
55
+ {
56
+ group: GROUP,
57
+ kind: "Gateway",
58
+ name: gateway.name,
59
+ namespace: gateway.namespace,
60
+ ...(gateway.sectionName ? { sectionName: gateway.sectionName } : {}),
61
+ },
62
+ ],
63
+ hostnames: [hostForPort(config, args.sessionId, port)],
64
+ rules: [
65
+ {
66
+ matches: [{ path: { type: "PathPrefix", value: "/" } }],
67
+ backendRefs: [{ name: args.serviceName, port }],
68
+ },
69
+ ],
70
+ },
71
+ };
72
+ };
73
+ const listSessionRoutes = async (sessionId) => {
74
+ const list = (await kube.custom.listNamespacedCustomObject({
75
+ ...base,
76
+ labelSelector: `telo.run/session-id=${sessionId}`,
77
+ }));
78
+ return list.items ?? [];
79
+ };
80
+ return {
81
+ layer: "gateway",
82
+ async publish(args) {
83
+ const tcp = args.ports.filter((p) => p.protocol === "tcp");
84
+ const desired = new Set(tcp.map((p) => routeName(args.sessionId, p.port)));
85
+ for (const p of tcp) {
86
+ const body = buildRoute(args, p.port);
87
+ const name = routeName(args.sessionId, p.port);
88
+ await createOrReplace(() => kube.custom.createNamespacedCustomObject({ ...base, body }), async () => {
89
+ const existing = (await kube.custom.getNamespacedCustomObject({
90
+ ...base,
91
+ name,
92
+ }));
93
+ await kube.custom.replaceNamespacedCustomObject({
94
+ ...base,
95
+ name,
96
+ body: {
97
+ ...body,
98
+ metadata: {
99
+ ...body.metadata,
100
+ resourceVersion: existing.metadata?.resourceVersion,
101
+ },
102
+ },
103
+ });
104
+ });
105
+ }
106
+ // A reload that DROPS a port must delete that port's route. In ingress mode
107
+ // the single object is replaced and the stale rule vanishes with it; here
108
+ // each port owns an object, so a removed one would keep routing to a port
109
+ // nothing listens on.
110
+ for (const existing of await listSessionRoutes(args.sessionId)) {
111
+ const name = existing.metadata?.name;
112
+ if (!name || desired.has(name))
113
+ continue;
114
+ try {
115
+ await kube.custom.deleteNamespacedCustomObject({ ...base, name });
116
+ }
117
+ catch (err) {
118
+ if (!is404(err))
119
+ throw err;
120
+ }
121
+ }
122
+ return tcp.map((p) => ({
123
+ host: hostForPort(config, args.sessionId, p.port),
124
+ port: p.port,
125
+ }));
126
+ },
127
+ /**
128
+ * Gateway API is the layer that can actually answer this: a controller writes
129
+ * `Accepted` and `ResolvedRefs` per parent, so a route refused because the
130
+ * Gateway's listener does not admit this namespace says exactly that
131
+ * (`NotAllowedByListeners`) instead of timing out with no cause.
132
+ */
133
+ async verdictFor(sessionId, route) {
134
+ let obj;
135
+ try {
136
+ obj = (await kube.custom.getNamespacedCustomObject({
137
+ ...base,
138
+ name: routeName(sessionId, route.port),
139
+ }));
140
+ }
141
+ catch (err) {
142
+ if (is404(err))
143
+ return { kind: "pending" };
144
+ throw err;
145
+ }
146
+ const parents = obj.status?.parents ?? [];
147
+ if (parents.length === 0)
148
+ return { kind: "pending" };
149
+ let rejected;
150
+ for (const parent of parents) {
151
+ const accepted = condition(parent, "Accepted");
152
+ const resolved = condition(parent, "ResolvedRefs");
153
+ if (accepted?.status === "False") {
154
+ rejected ??= refusal(accepted);
155
+ continue;
156
+ }
157
+ if (resolved?.status === "False") {
158
+ rejected ??= refusal(resolved);
159
+ continue;
160
+ }
161
+ // A route may attach to several parents; one that programmed it is enough
162
+ // for traffic to arrive, so a positive verdict wins over a sibling's refusal.
163
+ if (accepted?.status === "True" && resolved?.status !== "False") {
164
+ return { kind: "programmed" };
165
+ }
166
+ }
167
+ return rejected ? { kind: "rejected", reason: rejected } : { kind: "pending" };
168
+ },
169
+ unclaimedReason() {
170
+ const section = gateway.sectionName ? ` (listener '${gateway.sectionName}')` : "";
171
+ return (`no controller claimed the session HTTPRoute for Gateway ` +
172
+ `'${gateway.namespace}/${gateway.name}'${section}. Check the Gateway exists, is programmed, ` +
173
+ `and that a listener admits routes from namespace '${ns}' ` +
174
+ `(spec.listeners[].allowedRoutes.namespaces).`);
175
+ },
176
+ };
177
+ }
178
+ //# sourceMappingURL=gateway-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-router.js","sourceRoot":"","sources":["../../../src/k8s/routing/gateway-router.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
+ import { type ResolvedRouting } from "./routing-mode.js";
4
+ import type { SessionRouter } from "./session-router.js";
5
+ export { createOrReplace } from "./create-or-replace.js";
6
+ export { buildSessionIngress } from "./ingress-router.js";
7
+ export { watchRouteHealth, type WatchRouteHealthOptions } from "./route-health.js";
8
+ export { detectRoutingSupport, resolveRouting, type ClusterRoutingSupport, type ResolvedRouting, } from "./routing-mode.js";
9
+ export { endpointsFor, hostForPort } from "./session-endpoints.js";
10
+ export type { PublishedRoute, PublishRouteArgs, RouteVerdict, SessionRouter, } from "./session-router.js";
11
+ export { buildSessionService, podOwnerRef, sessionObjectName } from "./session-service.js";
12
+ /**
13
+ * Resolve the cluster's routing layer and build the router for it. Called ONCE at
14
+ * boot: an unresolvable configuration must fail the runner rather than every
15
+ * session it would otherwise accept and leave unroutable.
16
+ *
17
+ * `undefined` means logs-only — the honest answer when there is deliberately
18
+ * nothing to publish, distinct from a failure, which throws.
19
+ */
20
+ export declare function createSessionRouter(kube: KubeClient, config: K8sRunnerConfig): Promise<{
21
+ router?: SessionRouter;
22
+ resolved: ResolvedRouting;
23
+ }>;
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/k8s/routing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACnE,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE3F;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAA;CAAE,CAAC,CAOhE"}
@@ -0,0 +1,27 @@
1
+ import { createGatewayRouter } from "./gateway-router.js";
2
+ import { createIngressRouter } from "./ingress-router.js";
3
+ import { resolveRouting } from "./routing-mode.js";
4
+ export { createOrReplace } from "./create-or-replace.js";
5
+ export { buildSessionIngress } from "./ingress-router.js";
6
+ export { watchRouteHealth } from "./route-health.js";
7
+ export { detectRoutingSupport, resolveRouting, } from "./routing-mode.js";
8
+ export { endpointsFor, hostForPort } from "./session-endpoints.js";
9
+ export { buildSessionService, podOwnerRef, sessionObjectName } from "./session-service.js";
10
+ /**
11
+ * Resolve the cluster's routing layer and build the router for it. Called ONCE at
12
+ * boot: an unresolvable configuration must fail the runner rather than every
13
+ * session it would otherwise accept and leave unroutable.
14
+ *
15
+ * `undefined` means logs-only — the honest answer when there is deliberately
16
+ * nothing to publish, distinct from a failure, which throws.
17
+ */
18
+ export async function createSessionRouter(kube, config) {
19
+ const resolved = await resolveRouting(kube, config);
20
+ if (resolved.layer === "none")
21
+ return { resolved };
22
+ if (resolved.layer === "gateway") {
23
+ return { router: createGatewayRouter(kube, config, resolved.apiVersion), resolved };
24
+ }
25
+ return { router: createIngressRouter(kube, config), resolved };
26
+ }
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/k8s/routing/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAwB,MAAM,mBAAmB,CAAC;AAGzE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAgC,MAAM,mBAAmB,CAAC;AACnF,OAAO,EACL,oBAAoB,EACpB,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAOnE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE3F;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAgB,EAChB,MAAuB;IAEvB,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpD,IAAI,QAAQ,CAAC,KAAK,KAAK,MAAM;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IACnD,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,EAAE,MAAM,EAAE,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IACtF,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;AACjE,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { V1Ingress } from "@kubernetes/client-node";
2
+ import type { PortMapping } from "@telorun/runner-core";
3
+ import type { K8sRunnerConfig } from "../../config.js";
4
+ import type { KubeClient } from "../client.js";
5
+ import type { SessionRouter } from "./session-router.js";
6
+ /** One Ingress carries every host for a session: unlike Gateway API, an Ingress
7
+ * rule is scoped to its own host, so per-host backends fit in one object. */
8
+ export declare function buildSessionIngress(config: K8sRunnerConfig, sessionId: string, serviceName: string, podName: string, podUid: string, ports: PortMapping[]): {
9
+ ingress: V1Ingress;
10
+ hosts: string[];
11
+ };
12
+ export declare function createIngressRouter(kube: KubeClient, config: K8sRunnerConfig): SessionRouter;
13
+ //# sourceMappingURL=ingress-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingress-router.d.ts","sourceRoot":"","sources":["../../../src/k8s/routing/ingress-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK/C,OAAO,KAAK,EAIV,aAAa,EACd,MAAM,qBAAqB,CAAC;AAE7B;8EAC8E;AAC9E,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAAE,GACnB;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAgDzC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,GAAG,aAAa,CAgF5F"}
@@ -0,0 +1,125 @@
1
+ import { is404 } from "../pod-status.js";
2
+ import { createOrReplace } from "./create-or-replace.js";
3
+ import { hostForPort } from "./session-endpoints.js";
4
+ import { podOwnerRef, sessionObjectName } from "./session-service.js";
5
+ /** One Ingress carries every host for a session: unlike Gateway API, an Ingress
6
+ * rule is scoped to its own host, so per-host backends fit in one object. */
7
+ export function buildSessionIngress(config, sessionId, serviceName, podName, podUid, ports) {
8
+ const rules = ports
9
+ .filter((p) => p.protocol === "tcp")
10
+ .map((p) => ({
11
+ host: hostForPort(config, sessionId, p.port),
12
+ http: {
13
+ paths: [
14
+ {
15
+ path: "/",
16
+ pathType: "Prefix",
17
+ backend: { service: { name: serviceName, port: { number: p.port } } },
18
+ },
19
+ ],
20
+ },
21
+ }));
22
+ const ingress = {
23
+ apiVersion: "networking.k8s.io/v1",
24
+ kind: "Ingress",
25
+ metadata: {
26
+ name: sessionObjectName(sessionId),
27
+ namespace: config.sessionNamespace,
28
+ labels: {
29
+ "app.kubernetes.io/managed-by": config.managedByLabel,
30
+ "telo.run/session-id": sessionId,
31
+ },
32
+ ownerReferences: [podOwnerRef(podName, podUid)],
33
+ },
34
+ spec: {
35
+ ...(config.sessionRouting.ingressClassName
36
+ ? { ingressClassName: config.sessionRouting.ingressClassName }
37
+ : {}),
38
+ // Present the predefined cert (e.g. a Cloudflare Origin cert) so an upstream
39
+ // in Full (Strict) mode can validate the origin. Only meaningful with routable
40
+ // hosts; a single wildcard `*.<domain>` Secret covers every session host.
41
+ ...(config.sessionRouting.tlsSecretName && rules.length > 0
42
+ ? {
43
+ tls: [
44
+ {
45
+ hosts: rules.map((r) => r.host),
46
+ secretName: config.sessionRouting.tlsSecretName,
47
+ },
48
+ ],
49
+ }
50
+ : {}),
51
+ rules,
52
+ },
53
+ };
54
+ return { ingress, hosts: rules.map((r) => r.host) };
55
+ }
56
+ export function createIngressRouter(kube, config) {
57
+ const ns = config.sessionNamespace;
58
+ return {
59
+ layer: "ingress",
60
+ async publish(args) {
61
+ const { ingress } = buildSessionIngress(config, args.sessionId, args.serviceName, args.podName, args.podUid, args.ports);
62
+ const name = ingress.metadata.name;
63
+ const rules = ingress.spec?.rules ?? [];
64
+ // No tcp ports → nothing HTTP-routable. DELETE rather than return: a reload
65
+ // that drops the last routed port would otherwise leave the previous
66
+ // Ingress serving a port nothing listens on. (The Service stays for any udp.)
67
+ if (rules.length === 0) {
68
+ try {
69
+ await kube.networking.deleteNamespacedIngress({ name, namespace: ns });
70
+ }
71
+ catch (err) {
72
+ if (!is404(err))
73
+ throw err;
74
+ }
75
+ return [];
76
+ }
77
+ await createOrReplace(() => kube.networking.createNamespacedIngress({ namespace: ns, body: ingress }), async () => {
78
+ const existing = await kube.networking.readNamespacedIngress({ name, namespace: ns });
79
+ await kube.networking.replaceNamespacedIngress({
80
+ name,
81
+ namespace: ns,
82
+ body: {
83
+ ...ingress,
84
+ metadata: {
85
+ ...ingress.metadata,
86
+ resourceVersion: existing.metadata?.resourceVersion,
87
+ },
88
+ },
89
+ });
90
+ });
91
+ return args.ports
92
+ .filter((p) => p.protocol === "tcp")
93
+ .map((p) => ({ host: hostForPort(config, args.sessionId, p.port), port: p.port }));
94
+ },
95
+ /**
96
+ * Ingress offers no rejection signal — a controller that refuses a rule simply
97
+ * never writes status — so this only ever answers `programmed` or `pending`,
98
+ * and an unclaimed Ingress is caught by the deadline rather than by a reason
99
+ * the cluster gave. That is the weaker half of the two layers, and it is a
100
+ * property of the API, not of this implementation.
101
+ */
102
+ async verdictFor(sessionId) {
103
+ try {
104
+ const ingress = await kube.networking.readNamespacedIngress({
105
+ name: sessionObjectName(sessionId),
106
+ namespace: ns,
107
+ });
108
+ const assigned = ingress.status?.loadBalancer?.ingress ?? [];
109
+ return assigned.length > 0 ? { kind: "programmed" } : { kind: "pending" };
110
+ }
111
+ catch (err) {
112
+ if (is404(err))
113
+ return { kind: "pending" };
114
+ throw err;
115
+ }
116
+ },
117
+ unclaimedReason() {
118
+ const cls = config.sessionRouting.ingressClassName;
119
+ return (`no ingress controller claimed the session Ingress` +
120
+ (cls ? ` for class '${cls}'` : " (no ingressClassName configured)") +
121
+ `. Check that an Ingress controller is installed and watching namespace '${ns}'.`);
122
+ },
123
+ };
124
+ }
125
+ //# sourceMappingURL=ingress-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingress-router.js","sourceRoot":"","sources":["../../../src/k8s/routing/ingress-router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type RouteState } from "@telorun/runner-core";
2
+ import type { PublishedRoute, SessionRouter } from "./session-router.js";
3
+ export interface WatchRouteHealthOptions {
4
+ router: SessionRouter;
5
+ sessionId: string;
6
+ /** The hosts `publish` reported. Empty → nothing to watch. */
7
+ routes: PublishedRoute[];
8
+ /** Receives every transition for one host. */
9
+ onState: (route: PublishedRoute, state: RouteState, reason?: string) => void;
10
+ /** Aborts the watch — wire to the session's teardown. */
11
+ signal: AbortSignal;
12
+ /** How long a route may stay unclaimed before it is reported `unprogrammed`. */
13
+ timeoutMs: number;
14
+ /** Poll interval while waiting for a verdict. */
15
+ intervalMs?: number;
16
+ /** Receives a read failure once, so a permanent one (a missing RBAC grant, an
17
+ * unserved API version) reaches the runner's log and not only the client. */
18
+ onReadError?: (err: unknown) => void;
19
+ }
20
+ /**
21
+ * Watch whether the routing layer actually programmed each published host.
22
+ *
23
+ * This is the check whose absence made an unroutable cluster indistinguishable
24
+ * from a healthy one: the runner already dials the workload's own address, which
25
+ * proves the app is listening and says nothing about whether traffic can reach
26
+ * it. A session whose routing objects are reconciled by no controller reported
27
+ * every port reachable while every public URL 404'd.
28
+ *
29
+ * REPORTS, never fails the session: a slow controller and an absent one look the
30
+ * same for the first few seconds, and killing a healthy session over routing that
31
+ * arrives late would trade a visible problem for a worse one.
32
+ */
33
+ export declare function watchRouteHealth(options: WatchRouteHealthOptions): Promise<void>;
34
+ //# sourceMappingURL=route-health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-health.d.ts","sourceRoot":"","sources":["../../../src/k8s/routing/route-health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,8CAA8C;IAC9C,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7E,yDAAyD;IACzD,MAAM,EAAE,WAAW,CAAC;IACpB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;kFAC8E;IAC9E,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACtC;AAID;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDtF"}
@@ -0,0 +1,77 @@
1
+ import { abortableDelay } from "@telorun/runner-core";
2
+ import { apiReason } from "../api-error.js";
3
+ const DEFAULT_INTERVAL_MS = 2_000;
4
+ /**
5
+ * Watch whether the routing layer actually programmed each published host.
6
+ *
7
+ * This is the check whose absence made an unroutable cluster indistinguishable
8
+ * from a healthy one: the runner already dials the workload's own address, which
9
+ * proves the app is listening and says nothing about whether traffic can reach
10
+ * it. A session whose routing objects are reconciled by no controller reported
11
+ * every port reachable while every public URL 404'd.
12
+ *
13
+ * REPORTS, never fails the session: a slow controller and an absent one look the
14
+ * same for the first few seconds, and killing a healthy session over routing that
15
+ * arrives late would trade a visible problem for a worse one.
16
+ */
17
+ export async function watchRouteHealth(options) {
18
+ const { router, sessionId, routes, onState, signal } = options;
19
+ if (routes.length === 0)
20
+ return;
21
+ const interval = options.intervalMs ?? DEFAULT_INTERVAL_MS;
22
+ const deadline = Date.now() + options.timeoutMs;
23
+ const pending = new Map(routes.map((r) => [r.host, r]));
24
+ for (const route of routes)
25
+ onState(route, "pending");
26
+ // A read failure is NOT a verdict, but it is also not nothing: a permanent one
27
+ // (no RBAC on the routing objects, an API version this runner did not resolve)
28
+ // polls out the whole deadline and would otherwise be reported as
29
+ // `unclaimedReason()` — sending an operator to inspect the Gateway when the
30
+ // runner could not read its own object. So the last one is retained and wins
31
+ // at the deadline.
32
+ let lastReadError;
33
+ let reportedReadError = false;
34
+ while (pending.size > 0 && !signal.aborted) {
35
+ for (const [host, route] of [...pending]) {
36
+ if (signal.aborted)
37
+ return;
38
+ let verdict;
39
+ try {
40
+ verdict = await router.verdictFor(sessionId, route);
41
+ }
42
+ catch (err) {
43
+ lastReadError = err;
44
+ if (!reportedReadError) {
45
+ reportedReadError = true;
46
+ options.onReadError?.(err);
47
+ }
48
+ continue;
49
+ }
50
+ // A verdict of ANY kind proves the read path works, so an earlier failure
51
+ // was transient and must not decide the message at the deadline.
52
+ lastReadError = undefined;
53
+ if (verdict.kind === "programmed") {
54
+ pending.delete(host);
55
+ onState(route, "programmed");
56
+ }
57
+ else if (verdict.kind === "rejected") {
58
+ pending.delete(host);
59
+ onState(route, "unprogrammed", verdict.reason);
60
+ }
61
+ }
62
+ if (pending.size === 0)
63
+ return;
64
+ if (Date.now() >= deadline)
65
+ break;
66
+ await abortableDelay(interval, signal);
67
+ }
68
+ if (signal.aborted)
69
+ return;
70
+ const reason = lastReadError !== undefined
71
+ ? `route status could not be read: ${apiReason(lastReadError)}`
72
+ : router.unclaimedReason();
73
+ for (const route of pending.values()) {
74
+ onState(route, "unprogrammed", reason);
75
+ }
76
+ }
77
+ //# sourceMappingURL=route-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-health.js","sourceRoot":"","sources":["../../../src/k8s/routing/route-health.ts"],"names":[],"mappings":"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"}