@intentius/chant-k8s-client 0.42.1 → 0.44.1
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/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client.test.ts +74 -0
- package/src/client.ts +85 -30
- package/src/errors.ts +12 -0
- package/src/types.ts +9 -0
package/dist/client.d.ts
CHANGED
|
@@ -153,6 +153,15 @@ export interface K8sClient {
|
|
|
153
153
|
* such groupVersion — an answer, not a failure. Cached like `resolve`.
|
|
154
154
|
*/
|
|
155
155
|
resources(apiVersion: string, signal?: AbortSignal): Promise<ApiResourceInfo[]>;
|
|
156
|
+
/**
|
|
157
|
+
* One groupVersion per API group the cluster serves — each group's own
|
|
158
|
+
* `preferredVersion` (falling back to its first listed version), with the
|
|
159
|
+
* core group's `v1` first (chant #1517). This is the discovery-driven
|
|
160
|
+
* sweep's enumeration unit: one version per group names every kind exactly
|
|
161
|
+
* once, without re-listing the same resources at a second served version.
|
|
162
|
+
* Cached for the client's lifetime, like the rest of discovery.
|
|
163
|
+
*/
|
|
164
|
+
preferredGroupVersions(signal?: AbortSignal): Promise<string[]>;
|
|
156
165
|
/** The API resource lists discovery has been asked for so far, for tests and diagnostics. */
|
|
157
166
|
discoveryCacheKeys(): string[];
|
|
158
167
|
}
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -30,6 +30,14 @@ export declare class K8sApiError extends Error {
|
|
|
30
30
|
/** What was being addressed, e.g. `apps/v1 Deployment prod/web`. */
|
|
31
31
|
readonly target?: string | undefined;
|
|
32
32
|
readonly status?: K8sStatus | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Which cluster the failing read actually talked to, e.g.
|
|
35
|
+
* `context "k3d-fountain-local" (bound by k8s.profiles.local.context)`.
|
|
36
|
+
* Stamped by the client that issued the request (chant #1488) — a
|
|
37
|
+
* `read-failed` that does not name the cluster it read cost an afternoon on
|
|
38
|
+
* a laptop with two k3d clusters, so the failure carries it from birth.
|
|
39
|
+
*/
|
|
40
|
+
contextNote?: string;
|
|
33
41
|
constructor(statusCode: number, reason: string | undefined, apiMessage: string,
|
|
34
42
|
/** What was being addressed, e.g. `apps/v1 Deployment prod/web`. */
|
|
35
43
|
target?: string | undefined, status?: K8sStatus | undefined);
|
|
@@ -58,6 +66,8 @@ export declare class K8sApiError extends Error {
|
|
|
58
66
|
*/
|
|
59
67
|
export declare class K8sTransportError extends Error {
|
|
60
68
|
readonly target?: string | undefined;
|
|
69
|
+
/** Which cluster context the failed request was aimed at — see {@link K8sApiError.contextNote}. */
|
|
70
|
+
contextNote?: string;
|
|
61
71
|
constructor(message: string, target?: string | undefined, options?: {
|
|
62
72
|
cause?: unknown;
|
|
63
73
|
});
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAWlB,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM,GAAG,SAAS;aAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;aACpD,MAAM,CAAC,EAAE,MAAM;aACf,MAAM,CAAC,EAAE,SAAS;IAfpC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;gBAGH,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;IACpD,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,MAAM,CAAC,EAAE,SAAS,YAAA;IASpC,0EAA0E;IAC1E,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,4EAA4E;IAC5E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,8CAA8C;IAC9C,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW;CAapF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAMxB,MAAM,CAAC,EAAE,MAAM;IALjC,mGAAmG;IACnG,WAAW,CAAC,EAAE,MAAM,CAAC;gBAGnB,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EAC/B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAMhC;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,KAAK,CAAC,EAAE,OAAO;CAS5B;AAED;;;;;;;GAOG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;aAEpC,OAAO,EAAE,MAAM;aACf,OAAO,EAAE,SAAS,MAAM,EAAE;gBAD1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE;CAU7C;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,4EAA4E;AAC5E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,YAAY,EAAE,MAAM;gBAApB,YAAY,EAAE,MAAM,EACpC,OAAO,CAAC,EAAE,MAAM;CAKnB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -115,6 +115,15 @@ export interface K8sClientOptions {
|
|
|
115
115
|
concurrency?: number;
|
|
116
116
|
/** Where the context came from, for provenance. Default `ambient`. */
|
|
117
117
|
contextSource?: "bound" | "ambient";
|
|
118
|
+
/**
|
|
119
|
+
* How the context was selected, in words — e.g.
|
|
120
|
+
* `bound by k8s.profiles.prod.context` or
|
|
121
|
+
* `ambient; no k8s.profiles.local binding`. Stamped (with the resolved
|
|
122
|
+
* context name) onto every API/transport failure the client throws, so a
|
|
123
|
+
* `read-failed` reason names the cluster it read (chant #1488). Defaults to
|
|
124
|
+
* the bare `contextSource`.
|
|
125
|
+
*/
|
|
126
|
+
contextLabel?: string;
|
|
118
127
|
}
|
|
119
128
|
/**
|
|
120
129
|
* `@kubernetes/client-node`'s promise-shaped HTTP library: it receives the
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-k8s-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.1",
|
|
4
4
|
"description": "Typed Kubernetes API client for chant — the read/write path of the k8s lexicon, kept out of the build path",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant/lexicons/k8s/api-client/",
|
package/src/client.test.ts
CHANGED
|
@@ -117,6 +117,47 @@ describe("createK8sClient — kubeconfig and credential policy", () => {
|
|
|
117
117
|
expect(layer.requests).toHaveLength(0);
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
test("an API failure names the context it read (#1488)", async () => {
|
|
121
|
+
const layer = cluster({}, (req) =>
|
|
122
|
+
req.path.endsWith("/deployments/web")
|
|
123
|
+
? { status: 500, body: statusBody(500, "InternalError", "etcdserver: leader changed") }
|
|
124
|
+
: undefined,
|
|
125
|
+
);
|
|
126
|
+
const c = await client(layer);
|
|
127
|
+
const err = await c.read({ apiVersion: "apps/v1", kind: "Deployment", name: "web", namespace: "prod" }).catch((e: unknown) => e);
|
|
128
|
+
expect(err).toBeInstanceOf(K8sApiError);
|
|
129
|
+
expect((err as K8sApiError).contextNote).toBe('context "test-context" (ambient)');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("a bound client's failures carry the binding story via contextLabel (#1488)", async () => {
|
|
133
|
+
const layer = cluster({}, (req) =>
|
|
134
|
+
req.path.endsWith("/deployments/web") ? { status: 503, body: statusBody(503, "ServiceUnavailable", "apiserver down") } : undefined,
|
|
135
|
+
);
|
|
136
|
+
const c = await createK8sClient({
|
|
137
|
+
kubeconfig: fakeKubeconfig({
|
|
138
|
+
contexts: [{ name: "staging-eks" }, { name: "prod-eks", cluster: "prod", user: "prod-user" }],
|
|
139
|
+
currentContext: "staging-eks",
|
|
140
|
+
}),
|
|
141
|
+
context: "prod-eks",
|
|
142
|
+
contextSource: "bound",
|
|
143
|
+
contextLabel: "bound by k8s.profiles.prod.context",
|
|
144
|
+
requestLayer: layer,
|
|
145
|
+
});
|
|
146
|
+
const err = await c.read({ apiVersion: "apps/v1", kind: "Deployment", name: "web", namespace: "prod" }).catch((e: unknown) => e);
|
|
147
|
+
expect(err).toBeInstanceOf(K8sApiError);
|
|
148
|
+
expect((err as K8sApiError).contextNote).toBe('context "prod-eks" (bound by k8s.profiles.prod.context)');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("a transport failure names the context it was aimed at (#1488)", async () => {
|
|
152
|
+
const layer = fakeRequestLayer(() => {
|
|
153
|
+
throw new Error("connect ECONNREFUSED 127.0.0.1:6443");
|
|
154
|
+
});
|
|
155
|
+
const c = await createK8sClient({ kubeconfig: fakeKubeconfig(), requestLayer: layer });
|
|
156
|
+
const err = await c.read({ apiVersion: "apps/v1", kind: "Deployment", name: "web" }).catch((e: unknown) => e);
|
|
157
|
+
expect(err).toBeInstanceOf(K8sTransportError);
|
|
158
|
+
expect((err as K8sTransportError).contextNote).toBe('context "test-context" (ambient)');
|
|
159
|
+
});
|
|
160
|
+
|
|
120
161
|
test("an exec credential plugin off the allowlist refuses before it can run", async () => {
|
|
121
162
|
const layer = cluster();
|
|
122
163
|
await expect(
|
|
@@ -201,6 +242,39 @@ describe("resource resolution through the cluster's own discovery", () => {
|
|
|
201
242
|
expect(c.discoveryCacheKeys()).toEqual(["apps/v1"]);
|
|
202
243
|
});
|
|
203
244
|
|
|
245
|
+
// chant #1517 — the runtime-children sweep's enumeration unit: one version
|
|
246
|
+
// per group, so every kind is named exactly once.
|
|
247
|
+
test("preferredGroupVersions returns core v1 plus each group's own preferredVersion, once", async () => {
|
|
248
|
+
const layer = fakeRequestLayer((req) => {
|
|
249
|
+
if (req.path === "/api") return { body: { kind: "APIVersions", versions: ["v1"] } };
|
|
250
|
+
if (req.path === "/apis") {
|
|
251
|
+
return {
|
|
252
|
+
body: {
|
|
253
|
+
kind: "APIGroupList",
|
|
254
|
+
groups: [
|
|
255
|
+
{
|
|
256
|
+
name: "apps",
|
|
257
|
+
preferredVersion: { groupVersion: "apps/v1", version: "v1" },
|
|
258
|
+
versions: [{ groupVersion: "apps/v1" }, { groupVersion: "apps/v1beta1" }],
|
|
259
|
+
},
|
|
260
|
+
// A group reporting no preferredVersion falls back to its first
|
|
261
|
+
// listed version rather than being dropped.
|
|
262
|
+
{ name: "widgets.example.com", versions: [{ groupVersion: "widgets.example.com/v1alpha1" }] },
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return { status: 404, body: statusBody(404, "NotFound", `${req.path} not found`) };
|
|
268
|
+
});
|
|
269
|
+
const c = await client(layer);
|
|
270
|
+
|
|
271
|
+
expect(await c.preferredGroupVersions()).toEqual(["v1", "apps/v1", "widgets.example.com/v1alpha1"]);
|
|
272
|
+
|
|
273
|
+
// Cached: a second call issues no further discovery requests.
|
|
274
|
+
await c.preferredGroupVersions();
|
|
275
|
+
expect(layer.paths().filter((p) => p === "/apis")).toHaveLength(1);
|
|
276
|
+
});
|
|
277
|
+
|
|
204
278
|
test("kubectl-style resource strings resolve the way kubectl resolves them", async () => {
|
|
205
279
|
const c = await client(cluster());
|
|
206
280
|
// plural.group — how waitForReady's callers have always spelled CRDs
|
package/src/client.ts
CHANGED
|
@@ -215,6 +215,15 @@ export interface K8sClient {
|
|
|
215
215
|
* such groupVersion — an answer, not a failure. Cached like `resolve`.
|
|
216
216
|
*/
|
|
217
217
|
resources(apiVersion: string, signal?: AbortSignal): Promise<ApiResourceInfo[]>;
|
|
218
|
+
/**
|
|
219
|
+
* One groupVersion per API group the cluster serves — each group's own
|
|
220
|
+
* `preferredVersion` (falling back to its first listed version), with the
|
|
221
|
+
* core group's `v1` first (chant #1517). This is the discovery-driven
|
|
222
|
+
* sweep's enumeration unit: one version per group names every kind exactly
|
|
223
|
+
* once, without re-listing the same resources at a second served version.
|
|
224
|
+
* Cached for the client's lifetime, like the rest of discovery.
|
|
225
|
+
*/
|
|
226
|
+
preferredGroupVersions(signal?: AbortSignal): Promise<string[]>;
|
|
218
227
|
/** The API resource lists discovery has been asked for so far, for tests and diagnostics. */
|
|
219
228
|
discoveryCacheKeys(): string[];
|
|
220
229
|
}
|
|
@@ -231,6 +240,14 @@ interface ApiResourceListResponse {
|
|
|
231
240
|
}>;
|
|
232
241
|
}
|
|
233
242
|
|
|
243
|
+
/** The `/api` + `/apis` root discovery documents, kept structured so both the
|
|
244
|
+
* flat enumeration (`servedGroupVersions`) and the one-per-group enumeration
|
|
245
|
+
* (`preferredGroupVersions`, chant #1517) derive from a single cached fetch. */
|
|
246
|
+
interface RootDiscovery {
|
|
247
|
+
coreVersions: string[];
|
|
248
|
+
groups: Array<{ preferred?: string; versions: string[] }>;
|
|
249
|
+
}
|
|
250
|
+
|
|
234
251
|
/**
|
|
235
252
|
* Build a client. Nothing is read from the network here — the kubeconfig is
|
|
236
253
|
* parsed, the context resolved, and the credential policy enforced, all before
|
|
@@ -301,12 +318,23 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
|
|
|
301
318
|
const defaultNamespace = kc.getContextObject(kc.getCurrentContext())?.namespace || "default";
|
|
302
319
|
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
303
320
|
|
|
321
|
+
// Which cluster this client reads, in words, stamped onto every failure it
|
|
322
|
+
// throws (chant #1488). A `read-failed` that does not say which context it
|
|
323
|
+
// read turned "the ambient context moved" into an afternoon of debugging;
|
|
324
|
+
// naming it here — at construction, once — means every read path's
|
|
325
|
+
// classification carries it without each caller remembering to.
|
|
326
|
+
const contextNote = `context "${provenance.context ?? "(unset)"}" (${options.contextLabel ?? provenance.contextSource})`;
|
|
327
|
+
function noted<T extends { contextNote?: string }>(err: T): T {
|
|
328
|
+
err.contextNote = contextNote;
|
|
329
|
+
return err;
|
|
330
|
+
}
|
|
331
|
+
|
|
304
332
|
// apiVersion → its APIResourceList, or null when the cluster does not serve
|
|
305
333
|
// that group/version at all. Promises are cached, not values, so N entities
|
|
306
334
|
// resolved concurrently issue one discovery request between them rather
|
|
307
335
|
// than N identical ones.
|
|
308
336
|
const discoveryCache = new Map<string, Promise<ApiResourceListResponse | null>>();
|
|
309
|
-
let
|
|
337
|
+
let rootDiscoveryCache: Promise<RootDiscovery> | undefined;
|
|
310
338
|
|
|
311
339
|
async function send(
|
|
312
340
|
path: string,
|
|
@@ -338,10 +366,10 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
|
|
|
338
366
|
.send(ctx)
|
|
339
367
|
.toPromise()) as unknown as ResponseContextLike;
|
|
340
368
|
} catch (err) {
|
|
341
|
-
throw
|
|
342
|
-
err instanceof Error ? err.message : String(err),
|
|
343
|
-
|
|
344
|
-
|
|
369
|
+
throw noted(
|
|
370
|
+
new K8sTransportError(err instanceof Error ? err.message : String(err), opts.target ?? `${method} ${path}`, {
|
|
371
|
+
cause: err,
|
|
372
|
+
}),
|
|
345
373
|
);
|
|
346
374
|
}
|
|
347
375
|
|
|
@@ -349,10 +377,12 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
|
|
|
349
377
|
try {
|
|
350
378
|
text = await response.body.text();
|
|
351
379
|
} catch (err) {
|
|
352
|
-
throw
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
380
|
+
throw noted(
|
|
381
|
+
new K8sTransportError(
|
|
382
|
+
`response body could not be read: ${err instanceof Error ? err.message : String(err)}`,
|
|
383
|
+
opts.target ?? `${method} ${path}`,
|
|
384
|
+
{ cause: err },
|
|
385
|
+
),
|
|
356
386
|
);
|
|
357
387
|
}
|
|
358
388
|
return { status: response.httpStatusCode, body: text };
|
|
@@ -365,15 +395,17 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
|
|
|
365
395
|
): Promise<T> {
|
|
366
396
|
const { status, body } = await send(path, method, opts);
|
|
367
397
|
if (status < 200 || status > 299) {
|
|
368
|
-
throw K8sApiError.fromResponse(status, body, opts.target);
|
|
398
|
+
throw noted(K8sApiError.fromResponse(status, body, opts.target));
|
|
369
399
|
}
|
|
370
400
|
try {
|
|
371
401
|
return JSON.parse(body) as T;
|
|
372
402
|
} catch (err) {
|
|
373
|
-
throw
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
403
|
+
throw noted(
|
|
404
|
+
new K8sTransportError(
|
|
405
|
+
`the API server returned HTTP ${status} with a body that is not JSON`,
|
|
406
|
+
opts.target ?? `${method} ${path}`,
|
|
407
|
+
{ cause: err },
|
|
408
|
+
),
|
|
377
409
|
);
|
|
378
410
|
}
|
|
379
411
|
}
|
|
@@ -399,15 +431,13 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
|
|
|
399
431
|
return pending;
|
|
400
432
|
}
|
|
401
433
|
|
|
402
|
-
async function
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
const out: string[] = [];
|
|
434
|
+
async function rootDiscovery(signal?: AbortSignal): Promise<RootDiscovery> {
|
|
435
|
+
if (rootDiscoveryCache) return rootDiscoveryCache;
|
|
436
|
+
rootDiscoveryCache = (async (): Promise<RootDiscovery> => {
|
|
406
437
|
const core = await sendJson<{ versions?: string[] }>("/api", "GET", {
|
|
407
438
|
signal,
|
|
408
439
|
target: "discovery /api",
|
|
409
440
|
});
|
|
410
|
-
out.push(...(core.versions ?? ["v1"]));
|
|
411
441
|
const groups = await sendJson<{
|
|
412
442
|
groups?: Array<{
|
|
413
443
|
name?: string;
|
|
@@ -415,19 +445,43 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
|
|
|
415
445
|
versions?: Array<{ groupVersion?: string }>;
|
|
416
446
|
}>;
|
|
417
447
|
}>("/apis", "GET", { signal, target: "discovery /apis" });
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
448
|
+
return {
|
|
449
|
+
coreVersions: core.versions ?? ["v1"],
|
|
450
|
+
groups: (groups.groups ?? []).map((group) => ({
|
|
451
|
+
preferred: group.preferredVersion?.groupVersion,
|
|
452
|
+
versions: (group.versions ?? [])
|
|
453
|
+
.map((v) => v.groupVersion)
|
|
454
|
+
.filter((gv): gv is string => typeof gv === "string" && gv.length > 0),
|
|
455
|
+
})),
|
|
456
|
+
};
|
|
426
457
|
})().catch((err: unknown) => {
|
|
427
|
-
|
|
458
|
+
rootDiscoveryCache = undefined;
|
|
428
459
|
throw err;
|
|
429
460
|
});
|
|
430
|
-
return
|
|
461
|
+
return rootDiscoveryCache;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function servedGroupVersions(signal?: AbortSignal): Promise<string[]> {
|
|
465
|
+
const root = await rootDiscovery(signal);
|
|
466
|
+
const out: string[] = [...root.coreVersions];
|
|
467
|
+
for (const group of root.groups) {
|
|
468
|
+
if (group.preferred) out.push(group.preferred);
|
|
469
|
+
for (const gv of group.versions) {
|
|
470
|
+
if (gv !== group.preferred) out.push(gv);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return [...new Set(out)];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
async function preferredGroupVersions(signal?: AbortSignal): Promise<string[]> {
|
|
477
|
+
const root = await rootDiscovery(signal);
|
|
478
|
+
const core = root.coreVersions.includes("v1") ? "v1" : root.coreVersions[0];
|
|
479
|
+
const out: string[] = core ? [core] : [];
|
|
480
|
+
for (const group of root.groups) {
|
|
481
|
+
const gv = group.preferred ?? group.versions[0];
|
|
482
|
+
if (gv) out.push(gv);
|
|
483
|
+
}
|
|
484
|
+
return [...new Set(out)];
|
|
431
485
|
}
|
|
432
486
|
|
|
433
487
|
function toInfo(apiVersion: string, entry: NonNullable<ApiResourceListResponse["resources"]>[number]): ApiResourceInfo {
|
|
@@ -618,7 +672,7 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
|
|
|
618
672
|
target,
|
|
619
673
|
});
|
|
620
674
|
if (status < 200 || status > 299) {
|
|
621
|
-
throw K8sApiError.fromResponse(status, body, target);
|
|
675
|
+
throw noted(K8sApiError.fromResponse(status, body, target));
|
|
622
676
|
}
|
|
623
677
|
return body;
|
|
624
678
|
}
|
|
@@ -655,6 +709,7 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
|
|
|
655
709
|
delete: remove,
|
|
656
710
|
concurrently: (items, fn) => mapConcurrent(items, fn, concurrency),
|
|
657
711
|
resources: resourcesOf,
|
|
712
|
+
preferredGroupVersions,
|
|
658
713
|
discoveryCacheKeys: () => [...discoveryCache.keys()].sort(),
|
|
659
714
|
};
|
|
660
715
|
}
|
package/src/errors.ts
CHANGED
|
@@ -26,6 +26,15 @@ export interface K8sStatus {
|
|
|
26
26
|
* `reason` come from the response, not from parsing text.
|
|
27
27
|
*/
|
|
28
28
|
export class K8sApiError extends Error {
|
|
29
|
+
/**
|
|
30
|
+
* Which cluster the failing read actually talked to, e.g.
|
|
31
|
+
* `context "k3d-fountain-local" (bound by k8s.profiles.local.context)`.
|
|
32
|
+
* Stamped by the client that issued the request (chant #1488) — a
|
|
33
|
+
* `read-failed` that does not name the cluster it read cost an afternoon on
|
|
34
|
+
* a laptop with two k3d clusters, so the failure carries it from birth.
|
|
35
|
+
*/
|
|
36
|
+
contextNote?: string;
|
|
37
|
+
|
|
29
38
|
constructor(
|
|
30
39
|
public readonly statusCode: number,
|
|
31
40
|
public readonly reason: string | undefined,
|
|
@@ -90,6 +99,9 @@ export class K8sApiError extends Error {
|
|
|
90
99
|
* connect" and "the server said no" are different observations.
|
|
91
100
|
*/
|
|
92
101
|
export class K8sTransportError extends Error {
|
|
102
|
+
/** Which cluster context the failed request was aimed at — see {@link K8sApiError.contextNote}. */
|
|
103
|
+
contextNote?: string;
|
|
104
|
+
|
|
93
105
|
constructor(
|
|
94
106
|
message: string,
|
|
95
107
|
public readonly target?: string,
|
package/src/types.ts
CHANGED
|
@@ -125,6 +125,15 @@ export interface K8sClientOptions {
|
|
|
125
125
|
concurrency?: number;
|
|
126
126
|
/** Where the context came from, for provenance. Default `ambient`. */
|
|
127
127
|
contextSource?: "bound" | "ambient";
|
|
128
|
+
/**
|
|
129
|
+
* How the context was selected, in words — e.g.
|
|
130
|
+
* `bound by k8s.profiles.prod.context` or
|
|
131
|
+
* `ambient; no k8s.profiles.local binding`. Stamped (with the resolved
|
|
132
|
+
* context name) onto every API/transport failure the client throws, so a
|
|
133
|
+
* `read-failed` reason names the cluster it read (chant #1488). Defaults to
|
|
134
|
+
* the bare `contextSource`.
|
|
135
|
+
*/
|
|
136
|
+
contextLabel?: string;
|
|
128
137
|
}
|
|
129
138
|
|
|
130
139
|
/**
|