@namzu/sandbox 13.0.0 → 15.0.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/CHANGELOG.md +1147 -0
- package/README.md +447 -0
- package/dist/backends/aci-standby-pool/index.d.ts.map +1 -1
- package/dist/backends/aci-standby-pool/index.js +13 -1
- package/dist/backends/aci-standby-pool/index.js.map +1 -1
- package/dist/backends/docker/index.d.ts.map +1 -1
- package/dist/backends/docker/index.js +19 -1
- package/dist/backends/docker/index.js.map +1 -1
- package/dist/backends/firecracker/index.d.ts.map +1 -1
- package/dist/backends/firecracker/index.js +12 -2
- package/dist/backends/firecracker/index.js.map +1 -1
- package/dist/backends/firecracker/protocol.d.ts +481 -8
- package/dist/backends/firecracker/protocol.d.ts.map +1 -1
- package/dist/backends/firecracker/protocol.js +136 -0
- package/dist/backends/firecracker/protocol.js.map +1 -1
- package/dist/backends/firecracker/transport.d.ts +642 -14
- package/dist/backends/firecracker/transport.d.ts.map +1 -1
- package/dist/backends/firecracker/transport.js +1307 -34
- package/dist/backends/firecracker/transport.js.map +1 -1
- package/dist/backends/kubernetes/egress-policy.d.ts +1296 -0
- package/dist/backends/kubernetes/egress-policy.d.ts.map +1 -0
- package/dist/backends/kubernetes/egress-policy.js +2458 -0
- package/dist/backends/kubernetes/egress-policy.js.map +1 -0
- package/dist/backends/kubernetes/identity.d.ts +193 -0
- package/dist/backends/kubernetes/identity.d.ts.map +1 -0
- package/dist/backends/kubernetes/identity.js +147 -0
- package/dist/backends/kubernetes/identity.js.map +1 -0
- package/dist/backends/kubernetes/index.d.ts +1019 -0
- package/dist/backends/kubernetes/index.d.ts.map +1 -0
- package/dist/backends/kubernetes/index.js +1756 -0
- package/dist/backends/kubernetes/index.js.map +1 -0
- package/dist/backends/kubernetes/ingress-policy.d.ts +375 -0
- package/dist/backends/kubernetes/ingress-policy.d.ts.map +1 -0
- package/dist/backends/kubernetes/ingress-policy.js +1050 -0
- package/dist/backends/kubernetes/ingress-policy.js.map +1 -0
- package/dist/backends/kubernetes/k8s-client.d.ts +334 -0
- package/dist/backends/kubernetes/k8s-client.d.ts.map +1 -0
- package/dist/backends/kubernetes/k8s-client.js +553 -0
- package/dist/backends/kubernetes/k8s-client.js.map +1 -0
- package/dist/backends/kubernetes/lease.d.ts +145 -0
- package/dist/backends/kubernetes/lease.d.ts.map +1 -0
- package/dist/backends/kubernetes/lease.js +201 -0
- package/dist/backends/kubernetes/lease.js.map +1 -0
- package/dist/backends/kubernetes/objects.d.ts +702 -0
- package/dist/backends/kubernetes/objects.d.ts.map +1 -0
- package/dist/backends/kubernetes/objects.js +518 -0
- package/dist/backends/kubernetes/objects.js.map +1 -0
- package/dist/backends/kubernetes/per-sandbox-policy.d.ts +219 -0
- package/dist/backends/kubernetes/per-sandbox-policy.d.ts.map +1 -0
- package/dist/backends/kubernetes/per-sandbox-policy.js +407 -0
- package/dist/backends/kubernetes/per-sandbox-policy.js.map +1 -0
- package/dist/backends/kubernetes/privilege-probe.d.ts +136 -0
- package/dist/backends/kubernetes/privilege-probe.d.ts.map +1 -0
- package/dist/backends/kubernetes/privilege-probe.js +185 -0
- package/dist/backends/kubernetes/privilege-probe.js.map +1 -0
- package/dist/backends/kubernetes/rbac.d.ts +153 -0
- package/dist/backends/kubernetes/rbac.d.ts.map +1 -0
- package/dist/backends/kubernetes/rbac.js +177 -0
- package/dist/backends/kubernetes/rbac.js.map +1 -0
- package/dist/backends/kubernetes/sandbox.d.ts +190 -0
- package/dist/backends/kubernetes/sandbox.d.ts.map +1 -0
- package/dist/backends/kubernetes/sandbox.js +433 -0
- package/dist/backends/kubernetes/sandbox.js.map +1 -0
- package/dist/backends/kubernetes/transport.d.ts +1048 -0
- package/dist/backends/kubernetes/transport.d.ts.map +1 -0
- package/dist/backends/kubernetes/transport.js +2093 -0
- package/dist/backends/kubernetes/transport.js.map +1 -0
- package/dist/backends/kubernetes/workspace.d.ts +1512 -0
- package/dist/backends/kubernetes/workspace.d.ts.map +1 -0
- package/dist/backends/kubernetes/workspace.js +3703 -0
- package/dist/backends/kubernetes/workspace.js.map +1 -0
- package/dist/backends/remote-execution-controller.d.ts +14 -0
- package/dist/backends/remote-execution-controller.d.ts.map +1 -1
- package/dist/backends/remote-execution-controller.js.map +1 -1
- package/dist/index.d.ts +350 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +344 -34
- package/dist/index.js.map +1 -1
- package/dist/testing/sandbox-conformance.d.ts +227 -0
- package/dist/testing/sandbox-conformance.d.ts.map +1 -0
- package/dist/testing/sandbox-conformance.js +896 -0
- package/dist/testing/sandbox-conformance.js.map +1 -0
- package/package.json +5 -4
- package/src/backends/aci-standby-pool/index.ts +16 -1
- package/src/backends/docker/index.ts +22 -1
- package/src/backends/firecracker/index.ts +14 -2
- package/src/backends/firecracker/protocol.ts +541 -6
- package/src/backends/firecracker/transport.ts +1687 -64
- package/src/backends/kubernetes/egress-policy.ts +3448 -0
- package/src/backends/kubernetes/identity.ts +261 -0
- package/src/backends/kubernetes/index.ts +2670 -0
- package/src/backends/kubernetes/ingress-policy.ts +1344 -0
- package/src/backends/kubernetes/k8s-client.ts +742 -0
- package/src/backends/kubernetes/lease.ts +254 -0
- package/src/backends/kubernetes/objects.ts +983 -0
- package/src/backends/kubernetes/per-sandbox-policy.ts +542 -0
- package/src/backends/kubernetes/privilege-probe.ts +261 -0
- package/src/backends/kubernetes/rbac.ts +192 -0
- package/src/backends/kubernetes/sandbox.ts +593 -0
- package/src/backends/kubernetes/transport.ts +2895 -0
- package/src/backends/kubernetes/workspace.ts +5640 -0
- package/src/backends/remote-execution-controller.ts +14 -0
- package/src/index.ts +838 -35
- package/src/testing/sandbox-conformance.ts +1202 -0
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Kubernetes API client: bare fetch/https transport plus in-cluster
|
|
3
|
+
* ServiceAccount bootstrap.
|
|
4
|
+
*
|
|
5
|
+
* Sibling of the ACI and Firecracker control-plane clients: same
|
|
6
|
+
* dependency-free shape, different API. No `@kubernetes/client-node`, no
|
|
7
|
+
* kubeconfig parsing, no exec-credential-plugin invocation, no YAML anywhere
|
|
8
|
+
* — `@namzu/sandbox` still declares zero runtime `dependencies` after this
|
|
9
|
+
* module.
|
|
10
|
+
*
|
|
11
|
+
* ## Auth / SDK-dependency boundary
|
|
12
|
+
* Two access sources, neither needing a YAML parser:
|
|
13
|
+
* - `{ inCluster: true }` reads the projected ServiceAccount volume
|
|
14
|
+
* (`token`, `ca.crt`, `namespace`) plus `KUBERNETES_SERVICE_HOST` /
|
|
15
|
+
* `KUBERNETES_SERVICE_PORT` — pure file + env reads, the same boundary
|
|
16
|
+
* `kubectl` itself uses inside a pod. The token file is re-read on EVERY
|
|
17
|
+
* request because kubelet rotates projected tokens under the pod; caching
|
|
18
|
+
* it would produce an intermittent 401 hours into a long-lived host
|
|
19
|
+
* process.
|
|
20
|
+
* - `{ server, ca?, getToken }` supplied by the caller mirrors ACI's
|
|
21
|
+
* `ArmTokenProvider` and Firecracker's `OrchestratorTokenProvider`
|
|
22
|
+
* (`../aci-standby-pool/index.ts`, `../firecracker/index.ts`). Kubeconfig
|
|
23
|
+
* parsing, context merging and exec-credential-plugin invocation
|
|
24
|
+
* (kubelogin, gcloud, aws-iam-authenticator, ...) all stay OUTSIDE this
|
|
25
|
+
* package; the caller resolves a bearer token however it likes.
|
|
26
|
+
*
|
|
27
|
+
* ## Transport
|
|
28
|
+
* Default path is bare global `fetch`. When a custom CA is present — always
|
|
29
|
+
* true in-cluster, since the cluster CA is never in the process's system
|
|
30
|
+
* trust store — the call goes through `node:https.request` with that `ca`
|
|
31
|
+
* and `rejectUnauthorized: true` instead, exactly as
|
|
32
|
+
* `../firecracker/index.ts` splits `fetchOrchestratorRequest` from
|
|
33
|
+
* `httpsOrchestratorRequest` ("the package declares no undici dependency").
|
|
34
|
+
*
|
|
35
|
+
* ## Status mapping
|
|
36
|
+
* 404/410 become an `AlreadyGoneError` sentinel a teardown path can treat as
|
|
37
|
+
* success (ACI's `armCall(..., [404, 410])` accepts the same pair for
|
|
38
|
+
* exactly that reason — see `../aci-standby-pool/index.ts`). 409 becomes a
|
|
39
|
+
* `ConflictError` an adoption/retry path can catch. 401/403 become a
|
|
40
|
+
* `CredentialError` naming the attempted verb and resource — never the
|
|
41
|
+
* token, which this module never logs or embeds in any thrown message.
|
|
42
|
+
*
|
|
43
|
+
* ## What a failure carries, and what it deliberately does not
|
|
44
|
+
* Everything else — a connect failure, and every non-2xx status the mapping
|
|
45
|
+
* above does not name — rejects with {@link KubernetesApiError}, which
|
|
46
|
+
* carries the verb, the path, the status where there was one, and the
|
|
47
|
+
* `Retry-After` the server sent with a 429 or a 503. That is METADATA and
|
|
48
|
+
* nothing more: there is no retry loop and no retry policy in this module.
|
|
49
|
+
* A retry is a decision about the OPERATION — a GET during a readiness poll
|
|
50
|
+
* may be repeated, a POST that may already have committed may not — and this
|
|
51
|
+
* client cannot tell those apart. `backends/kubernetes/index.ts` owns the
|
|
52
|
+
* policy, beside the acquire it serves and inside that acquire's existing
|
|
53
|
+
* deadline.
|
|
54
|
+
*
|
|
55
|
+
* ## Deadlines
|
|
56
|
+
* Every request carries its own bound
|
|
57
|
+
* ({@link KubernetesClientOptions.requestTimeoutMs}, default
|
|
58
|
+
* {@link DEFAULT_API_REQUEST_TIMEOUT_MS}) on top of whatever signal the
|
|
59
|
+
* caller passed, because the caller's signal is optional everywhere and
|
|
60
|
+
* several call sites are SHARED flights that run under whichever caller
|
|
61
|
+
* arrived first — one signal-less `destroy()` against an API server that
|
|
62
|
+
* accepted a request and never answered would otherwise pin every later
|
|
63
|
+
* caller joined to it. The bound covers `getToken()`, the connection and
|
|
64
|
+
* reading the body, on both transports, and expiry rejects with
|
|
65
|
+
* {@link KubernetesApiTimeoutError} rather than the generic `failed:` error
|
|
66
|
+
* so a caller can tell a timeout from a refusal. A caller's own abort still
|
|
67
|
+
* behaves exactly as it did.
|
|
68
|
+
*
|
|
69
|
+
* ## Not in v1
|
|
70
|
+
* No watch, no informers, no resourceVersion/bookmark tracking. Readiness is
|
|
71
|
+
* polled by the caller with `../readiness.js`'s `OperationDeadline`, exactly
|
|
72
|
+
* as ACI polls `provisioningState`.
|
|
73
|
+
*/
|
|
74
|
+
import { readFileSync } from 'node:fs';
|
|
75
|
+
import https from 'node:https';
|
|
76
|
+
/**
|
|
77
|
+
* Default {@link KubernetesClientOptions.requestTimeoutMs} — 30 s.
|
|
78
|
+
*
|
|
79
|
+
* The same cap `lease.ts` already puts on a renewal PATCH, and below the
|
|
80
|
+
* 60 s `readyTimeoutMs` default, so a request that times out still leaves
|
|
81
|
+
* the readiness budget something to report with.
|
|
82
|
+
*/
|
|
83
|
+
export const DEFAULT_API_REQUEST_TIMEOUT_MS = 30_000;
|
|
84
|
+
/** Floor for {@link KubernetesClientOptions.requestTimeoutMs} — 1 s. */
|
|
85
|
+
export const MIN_API_REQUEST_TIMEOUT_MS = 1_000;
|
|
86
|
+
const DEFAULT_SERVICE_ACCOUNT_DIR = '/var/run/secrets/kubernetes.io/serviceaccount';
|
|
87
|
+
/** 404/410 → this. A teardown path treats it as "already achieved". */
|
|
88
|
+
export class KubernetesAlreadyGoneError extends Error {
|
|
89
|
+
method;
|
|
90
|
+
resource;
|
|
91
|
+
status;
|
|
92
|
+
constructor(method, resource, status) {
|
|
93
|
+
super(`kubernetes ${method} ${resource} -> ${status}: already gone`);
|
|
94
|
+
this.method = method;
|
|
95
|
+
this.resource = resource;
|
|
96
|
+
this.status = status;
|
|
97
|
+
this.name = 'KubernetesAlreadyGoneError';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** 409 → this. An adoption/retry path can catch it and re-read + retry. */
|
|
101
|
+
export class KubernetesConflictError extends Error {
|
|
102
|
+
method;
|
|
103
|
+
resource;
|
|
104
|
+
constructor(method, resource) {
|
|
105
|
+
super(`kubernetes ${method} ${resource} -> 409: conflict`);
|
|
106
|
+
this.method = method;
|
|
107
|
+
this.resource = resource;
|
|
108
|
+
this.name = 'KubernetesConflictError';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A JSON Patch the API server would not apply: 422 Unprocessable Entity.
|
|
113
|
+
*
|
|
114
|
+
* ## What the API server actually says, measured
|
|
115
|
+
*
|
|
116
|
+
* Against a real API server (v1.37.0) and the agent-sandbox `Sandbox` CRD, a
|
|
117
|
+
* JSON Patch whose `test` clause does not hold answers **422** with this
|
|
118
|
+
* body, byte for byte:
|
|
119
|
+
*
|
|
120
|
+
* ```json
|
|
121
|
+
* {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure",
|
|
122
|
+
* "message":"the server rejected our request due to an error in our request",
|
|
123
|
+
* "reason":"Invalid","details":{},"code":422}
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* A patch that is simply WRONG — a pointer into a member that does not
|
|
127
|
+
* exist, say — answers with exactly the same status, the same reason and the
|
|
128
|
+
* same message. The server does not name the operation that failed, so
|
|
129
|
+
* nothing this class could read off the response would tell a lost race from
|
|
130
|
+
* a malformed body, and a class that claimed to would be lying.
|
|
131
|
+
*
|
|
132
|
+
* So it says what it can honestly say: the patch did not apply and NOTHING
|
|
133
|
+
* was changed. Deciding which of the two it was belongs to the caller that
|
|
134
|
+
* knows what it tested — `workspace.ts` re-reads the object and compares the
|
|
135
|
+
* value it tested against what is stored now: changed ⇒ somebody raced it,
|
|
136
|
+
* unchanged ⇒ the body is wrong and the error is rethrown rather than
|
|
137
|
+
* retried forever.
|
|
138
|
+
*
|
|
139
|
+
* Not a {@link KubernetesConflictError}: that one is 409, which the same
|
|
140
|
+
* server returns for a DELETE whose `preconditions.resourceVersion` does not
|
|
141
|
+
* match (measured on the same cluster, with a message that DOES name the two
|
|
142
|
+
* versions). The two statuses mean different things and are kept apart.
|
|
143
|
+
*/
|
|
144
|
+
export class KubernetesPatchNotAppliedError extends Error {
|
|
145
|
+
method;
|
|
146
|
+
resource;
|
|
147
|
+
status;
|
|
148
|
+
constructor(method, resource, status) {
|
|
149
|
+
super(`kubernetes ${method} ${resource} -> ${status}: the API server did not apply the JSON patch and changed nothing. It does not say which operation failed, so this is either a \`test\` clause that no longer holds — another holder wrote the object first — or a patch body that is wrong.`);
|
|
150
|
+
this.method = method;
|
|
151
|
+
this.resource = resource;
|
|
152
|
+
this.status = status;
|
|
153
|
+
this.name = 'KubernetesPatchNotAppliedError';
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 401/403 → this. Names the attempted verb and resource only — never the
|
|
158
|
+
* token, and never the response body (the API server does not echo the
|
|
159
|
+
* token back, but the body is untrusted content this module has no reason
|
|
160
|
+
* to repeat into a thrown message).
|
|
161
|
+
*/
|
|
162
|
+
export class KubernetesCredentialError extends Error {
|
|
163
|
+
verb;
|
|
164
|
+
resource;
|
|
165
|
+
status;
|
|
166
|
+
constructor(verb, resource, status) {
|
|
167
|
+
super(`kubernetes API refused ${verb} ${resource}: ${status}`);
|
|
168
|
+
this.verb = verb;
|
|
169
|
+
this.resource = resource;
|
|
170
|
+
this.status = status;
|
|
171
|
+
this.name = 'KubernetesCredentialError';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* The request outlived {@link KubernetesClientOptions.requestTimeoutMs}.
|
|
176
|
+
*
|
|
177
|
+
* Deliberately NOT the generic `failed:` error: a caller that can tell a
|
|
178
|
+
* timeout from a refusal can retry an idempotent write, and one that cannot
|
|
179
|
+
* has to treat every failure alike. Carries the verb, the resource path and
|
|
180
|
+
* the bound that expired — and, like every other error in this module,
|
|
181
|
+
* never the bearer token.
|
|
182
|
+
*
|
|
183
|
+
* A timeout says nothing about whether the request was APPLIED, which is
|
|
184
|
+
* why nothing here tries to: a suspend restores the state it saw and sends
|
|
185
|
+
* its idempotent patch again, a create POST that landed is adopted through
|
|
186
|
+
* the 409 path, and a DELETE that already applied counts as done.
|
|
187
|
+
*/
|
|
188
|
+
export class KubernetesApiTimeoutError extends Error {
|
|
189
|
+
verb;
|
|
190
|
+
resource;
|
|
191
|
+
timeoutMs;
|
|
192
|
+
constructor(verb, resource, timeoutMs) {
|
|
193
|
+
super(`kubernetes ${verb} ${resource} was still unanswered ${timeoutMs}ms after it was sent, and was given up on (apiRequestTimeoutMs). Whether the API server applied it is unknown. Raise apiRequestTimeoutMs if this cluster is genuinely this slow.`);
|
|
194
|
+
this.verb = verb;
|
|
195
|
+
this.resource = resource;
|
|
196
|
+
this.timeoutMs = timeoutMs;
|
|
197
|
+
this.name = 'KubernetesApiTimeoutError';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Every API failure this client does not already name: a connect failure, and
|
|
202
|
+
* every non-2xx status outside 401/403/404/409/410 (and the 422 a JSON patch
|
|
203
|
+
* gets).
|
|
204
|
+
*
|
|
205
|
+
* It exists because "a burst past node capacity" and "the API server is down"
|
|
206
|
+
* used to be the same plain `Error`, separable only by matching the message
|
|
207
|
+
* text a release is free to reword. The fields are the smallest set that
|
|
208
|
+
* makes the difference decidable by a caller:
|
|
209
|
+
*
|
|
210
|
+
* - `status` — absent for a connect failure, present for every answered one.
|
|
211
|
+
* - `retryAfterMs` — the `Retry-After` header the API server sends with a
|
|
212
|
+
* 429 (its own priority-and-fairness queue shedding load) and sometimes
|
|
213
|
+
* with a 503, normalised to milliseconds from either spelling the header
|
|
214
|
+
* allows. Absent when the server sent none, which is not the same as zero.
|
|
215
|
+
* - `transport` — see {@link KubernetesApiFailureTransport}.
|
|
216
|
+
* - `cause` — the underlying error for a connect failure.
|
|
217
|
+
*
|
|
218
|
+
* **There is no retry here.** This class is metadata; `index.ts` decides what
|
|
219
|
+
* is worth repeating, because only the caller knows whether the operation is
|
|
220
|
+
* idempotent and only the caller owns a clock to repeat it inside.
|
|
221
|
+
*
|
|
222
|
+
* The message keeps the exact wording both throw sites used before, so a host
|
|
223
|
+
* that logs it sees no change and only a host that inspects the type gains
|
|
224
|
+
* anything.
|
|
225
|
+
*/
|
|
226
|
+
export class KubernetesApiError extends Error {
|
|
227
|
+
method;
|
|
228
|
+
path;
|
|
229
|
+
status;
|
|
230
|
+
retryAfterMs;
|
|
231
|
+
transport;
|
|
232
|
+
constructor(message, details) {
|
|
233
|
+
super(message, details.cause !== undefined ? { cause: details.cause } : undefined);
|
|
234
|
+
this.name = 'KubernetesApiError';
|
|
235
|
+
this.method = details.method;
|
|
236
|
+
this.path = details.path;
|
|
237
|
+
if (details.status !== undefined)
|
|
238
|
+
this.status = details.status;
|
|
239
|
+
if (details.retryAfterMs !== undefined)
|
|
240
|
+
this.retryAfterMs = details.retryAfterMs;
|
|
241
|
+
this.transport = details.transport;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* `Retry-After`, in milliseconds, or `undefined` when the server sent none or
|
|
246
|
+
* sent one that cannot be read.
|
|
247
|
+
*
|
|
248
|
+
* RFC 9110 allows two spellings and the API server uses the first: a
|
|
249
|
+
* delta-seconds integer (`Retry-After: 1`), which is what
|
|
250
|
+
* priority-and-fairness sends with a 429. The HTTP-date spelling is accepted
|
|
251
|
+
* too, because a proxy in front of the API server may rewrite it. A date in
|
|
252
|
+
* the past, or a negative delta, reads as `0` — "now" — rather than being
|
|
253
|
+
* discarded, because the server still said to wait and the answer to "how
|
|
254
|
+
* long" is "no longer".
|
|
255
|
+
*/
|
|
256
|
+
export function parseRetryAfterMs(header) {
|
|
257
|
+
if (header === undefined)
|
|
258
|
+
return undefined;
|
|
259
|
+
const raw = header.trim();
|
|
260
|
+
if (raw === '')
|
|
261
|
+
return undefined;
|
|
262
|
+
if (/^-?\d+$/.test(raw)) {
|
|
263
|
+
const seconds = Number(raw);
|
|
264
|
+
if (!Number.isSafeInteger(seconds))
|
|
265
|
+
return undefined;
|
|
266
|
+
return Math.max(0, seconds * 1_000);
|
|
267
|
+
}
|
|
268
|
+
const at = Date.parse(raw);
|
|
269
|
+
if (Number.isNaN(at))
|
|
270
|
+
return undefined;
|
|
271
|
+
return Math.max(0, at - Date.now());
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Validate the configured bound, or supply the default.
|
|
275
|
+
*
|
|
276
|
+
* There is no disabling value, and `0` is refused rather than read as
|
|
277
|
+
* "unbounded": an unanswered request is not a supported configuration.
|
|
278
|
+
* Several call sites are single-flight promises that run under the FIRST
|
|
279
|
+
* caller's signal and never consult a later one's, so one hung request
|
|
280
|
+
* does not block one caller — it blocks every caller that joined it, on a
|
|
281
|
+
* handle whose state already refuses data-plane calls. A cluster with a
|
|
282
|
+
* genuinely slow API server raises the number.
|
|
283
|
+
*/
|
|
284
|
+
export function resolveRequestTimeoutMs(value) {
|
|
285
|
+
if (value === undefined)
|
|
286
|
+
return DEFAULT_API_REQUEST_TIMEOUT_MS;
|
|
287
|
+
if (!Number.isSafeInteger(value) || value < MIN_API_REQUEST_TIMEOUT_MS) {
|
|
288
|
+
throw new Error(`kubernetes: apiRequestTimeoutMs must be an integer of at least ${MIN_API_REQUEST_TIMEOUT_MS}ms, got ${JSON.stringify(value)}. No value disables the bound: a request the API server accepted and never answered would hang its caller — and every later caller joined to the same single-flight suspend, delete or verification — until the process was killed. Raise the number instead; the default is ${DEFAULT_API_REQUEST_TIMEOUT_MS}ms.`);
|
|
289
|
+
}
|
|
290
|
+
return value;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Await `promise`, but give up the moment `signal` aborts, rejecting with
|
|
294
|
+
* that signal's reason.
|
|
295
|
+
*
|
|
296
|
+
* `getToken()` is a caller-supplied callback and `res.text()` is a body the
|
|
297
|
+
* peer may simply stop sending, and neither takes a signal of its own, so
|
|
298
|
+
* without this the bound would cover only the part of a request this module
|
|
299
|
+
* happens to hold a socket for. The late settlement is swallowed rather
|
|
300
|
+
* than left dangling: an unobserved rejection is still a rejection.
|
|
301
|
+
*/
|
|
302
|
+
function settleOnAbort(promise, signal) {
|
|
303
|
+
if (signal.aborted) {
|
|
304
|
+
void promise.catch(() => { });
|
|
305
|
+
return Promise.reject(signal.reason);
|
|
306
|
+
}
|
|
307
|
+
return new Promise((resolve, reject) => {
|
|
308
|
+
let done = false;
|
|
309
|
+
const onAbort = () => {
|
|
310
|
+
done = true;
|
|
311
|
+
reject(signal.reason);
|
|
312
|
+
};
|
|
313
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
314
|
+
promise.then((value) => {
|
|
315
|
+
signal.removeEventListener('abort', onAbort);
|
|
316
|
+
if (!done)
|
|
317
|
+
resolve(value);
|
|
318
|
+
}, (error) => {
|
|
319
|
+
signal.removeEventListener('abort', onAbort);
|
|
320
|
+
if (!done)
|
|
321
|
+
reject(error);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
function stripTrailingSlash(url) {
|
|
326
|
+
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Reads `ca.crt` and `namespace` once (they do not rotate for the pod's
|
|
330
|
+
* lifetime); returns a `getToken` closure that re-reads `token` on every
|
|
331
|
+
* call, because kubelet DOES rotate the projected token under a live pod.
|
|
332
|
+
*/
|
|
333
|
+
function resolveInCluster(access) {
|
|
334
|
+
const dir = access.serviceAccountDir ?? DEFAULT_SERVICE_ACCOUNT_DIR;
|
|
335
|
+
const host = process.env.KUBERNETES_SERVICE_HOST;
|
|
336
|
+
const port = process.env.KUBERNETES_SERVICE_PORT;
|
|
337
|
+
if (host === undefined || host === '' || port === undefined || port === '') {
|
|
338
|
+
throw new Error('kubernetes: in-cluster access requires KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT');
|
|
339
|
+
}
|
|
340
|
+
const ca = readFileSync(`${dir}/ca.crt`);
|
|
341
|
+
const namespace = readFileSync(`${dir}/namespace`, 'utf8').trim();
|
|
342
|
+
return {
|
|
343
|
+
baseUrl: `https://${host}:${port}`,
|
|
344
|
+
ca,
|
|
345
|
+
namespace,
|
|
346
|
+
getToken: async () => readFileSync(`${dir}/token`, 'utf8').trim(),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function resolveExplicit(access) {
|
|
350
|
+
return {
|
|
351
|
+
baseUrl: stripTrailingSlash(access.server),
|
|
352
|
+
ca: access.ca,
|
|
353
|
+
namespace: access.namespace,
|
|
354
|
+
getToken: access.getToken,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/** The plain-`fetch` path — the DEFAULT, used whenever no custom CA is set. */
|
|
358
|
+
async function fetchRequest(url, method, headers, payload, signal) {
|
|
359
|
+
const init = { method, headers };
|
|
360
|
+
if (payload !== undefined)
|
|
361
|
+
init.body = payload;
|
|
362
|
+
if (signal !== undefined)
|
|
363
|
+
init.signal = signal;
|
|
364
|
+
const res = await fetch(url, init);
|
|
365
|
+
return {
|
|
366
|
+
status: res.status,
|
|
367
|
+
contentType: res.headers.get('content-type') ?? '',
|
|
368
|
+
header: (name) => res.headers.get(name) ?? undefined,
|
|
369
|
+
text: () => res.text(),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* The custom-CA path: a `node:https` request presenting the injected `ca`
|
|
374
|
+
* and verifying the API server's cert against it (`rejectUnauthorized:
|
|
375
|
+
* true`). `node:https` is used rather than fetch+a custom dispatcher because
|
|
376
|
+
* the package declares no undici dependency — `node:https` is always
|
|
377
|
+
* importable and needs nothing added (mirrors
|
|
378
|
+
* `../firecracker/index.ts`'s `httpsOrchestratorRequest`).
|
|
379
|
+
*/
|
|
380
|
+
function httpsRequest(url, method, headers, payload, ca, signal) {
|
|
381
|
+
const target = new URL(url);
|
|
382
|
+
return new Promise((resolve, reject) => {
|
|
383
|
+
const req = https.request({
|
|
384
|
+
protocol: target.protocol,
|
|
385
|
+
hostname: target.hostname,
|
|
386
|
+
port: target.port !== '' ? Number(target.port) : 443,
|
|
387
|
+
path: `${target.pathname}${target.search}`,
|
|
388
|
+
method,
|
|
389
|
+
headers,
|
|
390
|
+
ca,
|
|
391
|
+
rejectUnauthorized: true,
|
|
392
|
+
...(signal !== undefined ? { signal } : {}),
|
|
393
|
+
}, (res) => {
|
|
394
|
+
const chunks = [];
|
|
395
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
396
|
+
res.on('end', () => {
|
|
397
|
+
const ct = res.headers['content-type'];
|
|
398
|
+
resolve({
|
|
399
|
+
status: res.statusCode ?? 0,
|
|
400
|
+
contentType: Array.isArray(ct) ? (ct[0] ?? '') : (ct ?? ''),
|
|
401
|
+
header: (name) => {
|
|
402
|
+
const value = res.headers[name];
|
|
403
|
+
return Array.isArray(value) ? value[0] : value;
|
|
404
|
+
},
|
|
405
|
+
text: async () => Buffer.concat(chunks).toString('utf8'),
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
res.on('error', reject);
|
|
409
|
+
});
|
|
410
|
+
req.on('error', reject);
|
|
411
|
+
if (payload !== undefined)
|
|
412
|
+
req.write(payload);
|
|
413
|
+
req.end();
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* `PATCH` carries a JSON MERGE patch body (RFC 7386) unless the caller asked
|
|
418
|
+
* for `json`, which sends an RFC 6902 operation list instead — never
|
|
419
|
+
* server-side apply, never YAML. `POST` carries a plain JSON create body.
|
|
420
|
+
* `GET`/`DELETE` normally carry no body; when a caller does pass one to
|
|
421
|
+
* `DELETE` — a `DeleteOptions` with `preconditions` — it is sent as plain
|
|
422
|
+
* JSON, which is what the API server expects there.
|
|
423
|
+
*/
|
|
424
|
+
function contentTypeFor(method, patchType) {
|
|
425
|
+
if (method !== 'PATCH')
|
|
426
|
+
return 'application/json';
|
|
427
|
+
return patchType === 'json' ? 'application/json-patch+json' : 'application/merge-patch+json';
|
|
428
|
+
}
|
|
429
|
+
export function createKubernetesClient(access, options = {}) {
|
|
430
|
+
const resolved = access.inCluster === true ? resolveInCluster(access) : resolveExplicit(access);
|
|
431
|
+
// Validated at construction, not at the first request: a configuration
|
|
432
|
+
// this module will never honour should be refused where the operator can
|
|
433
|
+
// still see which backend it came from.
|
|
434
|
+
const requestTimeoutMs = resolveRequestTimeoutMs(options.requestTimeoutMs);
|
|
435
|
+
async function request(method, path, body, signal, patchType = 'merge') {
|
|
436
|
+
signal?.throwIfAborted();
|
|
437
|
+
// The caller's signal is WRAPPED, never replaced: `deadline` aborts
|
|
438
|
+
// with the caller's own reason when the caller aborts, so every path
|
|
439
|
+
// below behaves exactly as it did, and with a
|
|
440
|
+
// KubernetesApiTimeoutError when the bound expires first. Combined by
|
|
441
|
+
// hand rather than with `AbortSignal.any`, which this package's
|
|
442
|
+
// declared Node floor (20.0) predates.
|
|
443
|
+
const deadlineController = new AbortController();
|
|
444
|
+
const deadline = deadlineController.signal;
|
|
445
|
+
let timedOut = false;
|
|
446
|
+
const timer = setTimeout(() => {
|
|
447
|
+
timedOut = true;
|
|
448
|
+
deadlineController.abort(new KubernetesApiTimeoutError(method, path, requestTimeoutMs));
|
|
449
|
+
}, requestTimeoutMs);
|
|
450
|
+
timer.unref?.();
|
|
451
|
+
const forwardCallerAbort = () => deadlineController.abort(signal?.reason);
|
|
452
|
+
signal?.addEventListener('abort', forwardCallerAbort, { once: true });
|
|
453
|
+
/** True while the CALLER is the one who gave up, which wins. */
|
|
454
|
+
const callerGaveUp = () => signal?.aborted === true;
|
|
455
|
+
const readBody = async (res) => await settleOnAbort(res.text(), deadline);
|
|
456
|
+
try {
|
|
457
|
+
// Covered by the bound at last: `getToken()` is caller code that
|
|
458
|
+
// takes no signal of its own, and an in-cluster token read that
|
|
459
|
+
// blocks on a wedged volume used to be unbounded on every request.
|
|
460
|
+
const token = await settleOnAbort(resolved.getToken(), deadline);
|
|
461
|
+
deadline.throwIfAborted();
|
|
462
|
+
const url = `${resolved.baseUrl}${path}`;
|
|
463
|
+
const payload = body !== undefined ? JSON.stringify(body) : undefined;
|
|
464
|
+
const headers = {
|
|
465
|
+
Authorization: `Bearer ${token}`,
|
|
466
|
+
Accept: 'application/json',
|
|
467
|
+
};
|
|
468
|
+
if (payload !== undefined)
|
|
469
|
+
headers['content-type'] = contentTypeFor(method, patchType);
|
|
470
|
+
let res;
|
|
471
|
+
try {
|
|
472
|
+
res =
|
|
473
|
+
resolved.ca !== undefined
|
|
474
|
+
? await httpsRequest(url, method, headers, payload, resolved.ca, deadline)
|
|
475
|
+
: await fetchRequest(url, method, headers, payload, deadline);
|
|
476
|
+
}
|
|
477
|
+
catch (err) {
|
|
478
|
+
if (err instanceof KubernetesApiTimeoutError)
|
|
479
|
+
throw err;
|
|
480
|
+
// A timeout is named rather than folded into `failed:`; a
|
|
481
|
+
// caller's own abort still produces the wrapped error it
|
|
482
|
+
// always produced.
|
|
483
|
+
if (timedOut && !callerGaveUp()) {
|
|
484
|
+
throw new KubernetesApiTimeoutError(method, path, requestTimeoutMs);
|
|
485
|
+
}
|
|
486
|
+
// Same sentence it has always thrown, on a class that says which
|
|
487
|
+
// half failed. Nothing here decides whether to try again: see
|
|
488
|
+
// {@link KubernetesApiError}.
|
|
489
|
+
throw new KubernetesApiError(`kubernetes ${method} ${path} failed: ${err instanceof Error ? err.message : String(err)}`, { method, path, transport: 'connect', cause: err });
|
|
490
|
+
}
|
|
491
|
+
if (res.status === 401 || res.status === 403) {
|
|
492
|
+
await readBody(res);
|
|
493
|
+
deadline.throwIfAborted();
|
|
494
|
+
throw new KubernetesCredentialError(method, path, res.status);
|
|
495
|
+
}
|
|
496
|
+
if (res.status === 404 || res.status === 410) {
|
|
497
|
+
await readBody(res);
|
|
498
|
+
deadline.throwIfAborted();
|
|
499
|
+
throw new KubernetesAlreadyGoneError(method, path, res.status);
|
|
500
|
+
}
|
|
501
|
+
if (res.status === 409) {
|
|
502
|
+
await readBody(res);
|
|
503
|
+
deadline.throwIfAborted();
|
|
504
|
+
throw new KubernetesConflictError(method, path);
|
|
505
|
+
}
|
|
506
|
+
// Before the generic mapping, and only for the dialect that has a
|
|
507
|
+
// `test` clause to fail: a merge patch has no condition, so a 422
|
|
508
|
+
// on one is a malformed body and belongs in the generic error
|
|
509
|
+
// where it always was.
|
|
510
|
+
if (res.status === 422 && method === 'PATCH' && patchType === 'json') {
|
|
511
|
+
await readBody(res);
|
|
512
|
+
deadline.throwIfAborted();
|
|
513
|
+
throw new KubernetesPatchNotAppliedError(method, path, res.status);
|
|
514
|
+
}
|
|
515
|
+
if (res.status < 200 || res.status >= 300) {
|
|
516
|
+
// Read BEFORE the body, because `readBody` can abort out of this
|
|
517
|
+
// block and the header is the whole reason a caller can wait the
|
|
518
|
+
// length the server asked for rather than a length it invented.
|
|
519
|
+
// Only for the two statuses that mean "later, not never": a 400
|
|
520
|
+
// carrying one would be the server contradicting itself.
|
|
521
|
+
const retryAfterMs = res.status === 429 || res.status === 503
|
|
522
|
+
? parseRetryAfterMs(res.header('retry-after'))
|
|
523
|
+
: undefined;
|
|
524
|
+
const text = await readBody(res);
|
|
525
|
+
deadline.throwIfAborted();
|
|
526
|
+
throw new KubernetesApiError(`kubernetes ${method} ${path} -> ${res.status}: ${text}`, {
|
|
527
|
+
method,
|
|
528
|
+
path,
|
|
529
|
+
status: res.status,
|
|
530
|
+
...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
|
|
531
|
+
transport: 'status',
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
if (res.status === 204)
|
|
535
|
+
return undefined;
|
|
536
|
+
if (res.contentType.includes('application/json')) {
|
|
537
|
+
const text = await readBody(res);
|
|
538
|
+
deadline.throwIfAborted();
|
|
539
|
+
return text.length > 0 ? JSON.parse(text) : undefined;
|
|
540
|
+
}
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
finally {
|
|
544
|
+
clearTimeout(timer);
|
|
545
|
+
signal?.removeEventListener('abort', forwardCallerAbort);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
request,
|
|
550
|
+
namespace: () => resolved.namespace,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
//# sourceMappingURL=k8s-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k8s-client.js","sourceRoot":"","sources":["../../../src/backends/kubernetes/k8s-client.ts"],"names":[],"mappings":"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"}
|