@compforge/harness-toolbox 0.1.2 → 0.3.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/README.md +62 -5
- package/dist/{chunk-94anqtxh.js → chunk-0cq1gx86.js} +5 -5
- package/dist/chunk-1kpbvat9.js +89 -0
- package/dist/chunk-4pdwsfcx.js +111 -0
- package/dist/chunk-a9htb6cr.js +4 -0
- package/dist/{chunk-vjsr2jb3.js → chunk-mnad4zkp.js} +3 -3
- package/dist/chunk-ntfm5e85.js +27 -0
- package/dist/chunk-y3dx8jh6.js +94 -0
- package/dist/chunk-z5pm2emz.js +4 -0
- package/dist/client-manager.d.ts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +6 -0
- package/dist/datasource.d.ts +2 -1
- package/dist/datasource.js +11 -3
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +10 -0
- package/dist/kubernetes/client.d.ts +6 -1
- package/dist/kubernetes/client.js +9 -85
- package/dist/kubernetes/environment.d.ts +19 -0
- package/dist/kubernetes/environment.js +43 -0
- package/dist/kubernetes/pod-log-client.js +2 -2
- package/dist/kubernetes/pod-log-datasource.d.ts +3 -3
- package/dist/kubernetes/pod-log-datasource.js +8 -6
- package/dist/kubernetes/resources.d.ts +23 -0
- package/dist/kubernetes/resources.js +8 -0
- package/dist/kubernetes/service-port-forward.js +2 -2
- package/dist/kubernetes/workload.d.ts +7 -0
- package/dist/kubernetes/workload.js +7 -0
- package/dist/mysql/client.js +2 -2
- package/dist/mysql/index.js +2 -2
- package/dist/redis/index.js +3 -3
- package/dist/s3/datasource.d.ts +3 -3
- package/dist/s3/datasource.js +3 -2
- package/dist/s3/index.js +3 -2
- package/package.json +6 -2
- package/dist/chunk-fnc291vk.js +0 -4
- package/dist/{chunk-9x1apyp4.js → chunk-jkpwa8tb.js} +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Harness Toolbox
|
|
2
2
|
|
|
3
|
-
Client, DataSource and ClientManager are owned by the sibling TypeScript package
|
|
3
|
+
Client, ClientProvider, DataSource, EnvironmentContext and ClientManager are owned by the sibling TypeScript package
|
|
4
4
|
`@compforge/harness-common`. Toolbox provides protocol implementations and transports;
|
|
5
5
|
its lifecycle entrypoints re-export the same common symbols.
|
|
6
6
|
|
|
@@ -12,20 +12,77 @@ The package supports Node.js 22+ and Bun. Install with `npm install @compforge/h
|
|
|
12
12
|
Protocol adapters have separate entry points; importing lifecycle or transport contracts does
|
|
13
13
|
not load database or Kubernetes drivers.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## Workload discovery
|
|
16
|
+
|
|
17
|
+
Borrow a KubernetesClient through KubernetesEnvironment and the shared ClientManager lifecycle, then call
|
|
18
|
+
`client.resolveWorkload(workload, environmentId)`. The ID is a stable target identity from your
|
|
19
|
+
environment registry, not a display name or kubeconfig path. The Workload's namespace overrides the client's default.
|
|
20
|
+
Resource, Service selector and labels locations return all matching Pod incarnations without
|
|
21
|
+
readiness filtering; successful empty discovery returns an empty array. Missing resources,
|
|
22
|
+
selectorless Services and failed access raise KubernetesError.
|
|
23
|
+
|
|
24
|
+
The resource path uses client-node kubeconfig authentication/TLS and bounded native JSON reads,
|
|
25
|
+
not kubectl error text. Configure request timeout, concurrency and response bytes with the
|
|
26
|
+
client's ResourceLimits constructor argument. Existing exec/port-forward operations remain unchanged.
|
|
27
|
+
See [the shared Workload model](../../../docs/workload.md).
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { ClientManager, EnvironmentContext } from "@compforge/harness-toolbox";
|
|
31
|
+
import { KubernetesEnvironment } from "@compforge/harness-toolbox/kubernetes/environment";
|
|
32
|
+
|
|
33
|
+
const clients = new ClientManager();
|
|
34
|
+
try {
|
|
35
|
+
const ctx = new EnvironmentContext(
|
|
36
|
+
new KubernetesEnvironment("dev",
|
|
37
|
+
{ kubeconfig: "/config/cluster", context: "dev", namespace: "app" },
|
|
38
|
+
{ timeoutMs: 10_000, concurrency: 4, maxBytes: 8 * 1024 * 1024 }),
|
|
39
|
+
clients, performance.now() + 30_000,
|
|
40
|
+
);
|
|
41
|
+
const kube = await ctx.clients.get(ctx.environment);
|
|
42
|
+
const pods = await kube.resolveWorkload({
|
|
43
|
+
name: "api", platform: "kubernetes",
|
|
44
|
+
location: { kind: "resource", resource_kind: "Deployment", name: "api" },
|
|
45
|
+
}, "cluster-a/runtime");
|
|
46
|
+
} finally {
|
|
47
|
+
await clients.dispose();
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The context does not cancel work automatically. Its budget is available as remainingMs;
|
|
52
|
+
the root owns cancellation and must join child work before disposing clients.
|
|
53
|
+
|
|
54
|
+
Catch ToolboxError from the root entry or `@compforge/harness-toolbox/errors`; use `kind` and
|
|
55
|
+
`code` for decisions. Native `cause` may contain sensitive details and is debug-only.
|
|
56
|
+
|
|
57
|
+
For coordinated common/toolbox development, install and build common before installing toolbox:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# From sdks/typescript/toolbox
|
|
61
|
+
(cd ../common && bun install --frozen-lockfile && make build)
|
|
62
|
+
bun install --frozen-lockfile
|
|
63
|
+
make lint
|
|
64
|
+
make test
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The toolbox root override resolves common from `../common` only for local development;
|
|
68
|
+
the published dependency remains a registry version. Publish common before toolbox.
|
|
69
|
+
The Makefile builds common before typechecking or building toolbox.
|
|
70
|
+
|
|
71
|
+
A `ClientProvider` identifies a reusable client and constructs it; `DataSource` adds data-access semantics.
|
|
72
|
+
Accessible environments implement ClientProvider directly without becoming data sources. The `Client` owns initialization
|
|
16
73
|
and idempotent disposal; a `ClientManager` joins concurrent initialization for the same key and
|
|
17
74
|
closes owned clients when the caller finishes. A `ConnectionSource` resolves connection settings
|
|
18
75
|
and available `Transport` paths. Clients execute protocol operations through those paths.
|
|
19
76
|
|
|
20
77
|
```ts
|
|
21
|
-
import { ClientManager,
|
|
78
|
+
import { ClientManager, clientKey, type DataSource } from "@compforge/harness-toolbox";
|
|
22
79
|
import { MysqlClient } from "@compforge/harness-toolbox/mysql";
|
|
23
80
|
import { DirectTransport } from "@compforge/harness-toolbox/transport";
|
|
24
81
|
|
|
25
82
|
const target = { host: "localhost", port: 3306, database: "app", user: "reader", password: "..." };
|
|
26
83
|
const source: DataSource<MysqlClient> = {
|
|
27
|
-
|
|
28
|
-
createClient: signal => new MysqlClient({
|
|
84
|
+
clientKey: clientKey("mysql", target),
|
|
85
|
+
createClient: (_clients, signal) => new MysqlClient({
|
|
29
86
|
resolve: async () => target,
|
|
30
87
|
transports: [new DirectTransport()],
|
|
31
88
|
}, { signal, connectTimeoutMs: 5_000, queryTimeoutMs: 10_000 }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
clientKey
|
|
3
|
+
} from "./chunk-z5pm2emz.js";
|
|
4
4
|
import {
|
|
5
5
|
S3Client
|
|
6
6
|
} from "./chunk-zp1s833x.js";
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
|
|
11
11
|
// src/s3/datasource.ts
|
|
12
12
|
class S3DataSource {
|
|
13
|
-
|
|
13
|
+
clientKey;
|
|
14
14
|
#target;
|
|
15
15
|
#limits;
|
|
16
16
|
#transport;
|
|
@@ -18,13 +18,13 @@ class S3DataSource {
|
|
|
18
18
|
this.#target = structuredClone(target);
|
|
19
19
|
this.#limits = { ...limits };
|
|
20
20
|
this.#transport = route?.transport ?? new DirectTransport;
|
|
21
|
-
this.
|
|
21
|
+
this.clientKey = clientKey("s3", {
|
|
22
22
|
target: this.#target,
|
|
23
23
|
limits: this.#limits,
|
|
24
24
|
route: route ? { key: route.key, transport: route.transport.name } : "direct"
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
createClient(signal) {
|
|
27
|
+
createClient(_clients, signal) {
|
|
28
28
|
return new S3Client({
|
|
29
29
|
resolve: async () => this.#target,
|
|
30
30
|
transports: [this.#transport]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConcurrencyPool
|
|
3
|
+
} from "./chunk-v3hxvnrc.js";
|
|
4
|
+
import {
|
|
5
|
+
KubernetesError,
|
|
6
|
+
ToolboxError,
|
|
7
|
+
kubernetesHttpError
|
|
8
|
+
} from "./chunk-ntfm5e85.js";
|
|
9
|
+
|
|
10
|
+
// src/kubernetes/resources.ts
|
|
11
|
+
import { KubeConfig } from "@kubernetes/client-node";
|
|
12
|
+
import fetch from "node-fetch/lib/index.js";
|
|
13
|
+
function resourceAccess(options, signal, limits) {
|
|
14
|
+
if (![limits.timeoutMs, limits.concurrency, limits.maxBytes].every((value) => Number.isSafeInteger(value) && value > 0)) {
|
|
15
|
+
throw new KubernetesError("Resource limits must be positive integers", { kind: "invalid_argument" });
|
|
16
|
+
}
|
|
17
|
+
const config = new KubeConfig;
|
|
18
|
+
try {
|
|
19
|
+
if (options.kubeconfig)
|
|
20
|
+
config.loadFromFile(options.kubeconfig);
|
|
21
|
+
else
|
|
22
|
+
config.loadFromDefault();
|
|
23
|
+
if (options.context)
|
|
24
|
+
config.setCurrentContext(options.context);
|
|
25
|
+
} catch (cause) {
|
|
26
|
+
throw new KubernetesError("Cannot load Kubernetes access configuration", { kind: "connection_failed", cause });
|
|
27
|
+
}
|
|
28
|
+
const cluster = config.getCurrentCluster();
|
|
29
|
+
if (!cluster)
|
|
30
|
+
throw new KubernetesError("Kubernetes context has no cluster", { kind: "invalid_argument" });
|
|
31
|
+
if (new URL(cluster.server).protocol === "http:") {
|
|
32
|
+
config.clusters = config.clusters.map((item) => item === cluster ? { ...item, skipTLSVerify: true } : item);
|
|
33
|
+
}
|
|
34
|
+
const pool = new ConcurrencyPool(limits.concurrency);
|
|
35
|
+
return {
|
|
36
|
+
get: (namespace, resource, name, selector) => pool.run(async () => {
|
|
37
|
+
const group = ["pods", "services"].includes(resource) ? "/api/v1" : "/apis/apps/v1";
|
|
38
|
+
const path = group + "/namespaces/" + encodeURIComponent(namespace) + "/" + resource + (name ? "/" + encodeURIComponent(name) : "");
|
|
39
|
+
const url = new URL(cluster.server.replace(/\/$/, "") + path);
|
|
40
|
+
if (selector)
|
|
41
|
+
url.searchParams.set("labelSelector", selector);
|
|
42
|
+
const controller = new AbortController;
|
|
43
|
+
const abort = () => controller.abort(signal.reason);
|
|
44
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
45
|
+
let timedOut = false;
|
|
46
|
+
const timer = setTimeout(() => {
|
|
47
|
+
timedOut = true;
|
|
48
|
+
controller.abort();
|
|
49
|
+
}, limits.timeoutMs);
|
|
50
|
+
try {
|
|
51
|
+
signal.throwIfAborted();
|
|
52
|
+
const init = await config.applyToFetchOptions({});
|
|
53
|
+
controller.signal.throwIfAborted();
|
|
54
|
+
const response = await fetch(url.href, { ...init, signal: controller.signal, size: limits.maxBytes });
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
controller.abort();
|
|
57
|
+
const error = kubernetesHttpError(response.status);
|
|
58
|
+
throw new KubernetesError("Read " + resource + " in namespace " + namespace + " failed", { kind: error.kind, code: error.code });
|
|
59
|
+
}
|
|
60
|
+
return await response.json();
|
|
61
|
+
} catch (cause) {
|
|
62
|
+
if (signal.aborted)
|
|
63
|
+
throw signal.reason;
|
|
64
|
+
if (cause instanceof ToolboxError)
|
|
65
|
+
throw cause;
|
|
66
|
+
const type = cause && typeof cause === "object" && "type" in cause ? cause.type : undefined;
|
|
67
|
+
const code = cause && typeof cause === "object" && "code" in cause && typeof cause.code === "string" ? cause.code : undefined;
|
|
68
|
+
const tls = code !== undefined && [
|
|
69
|
+
"CERT_HAS_EXPIRED",
|
|
70
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
71
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
72
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
73
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
74
|
+
"ERR_TLS_CERT_ALTNAME_INVALID"
|
|
75
|
+
].includes(code);
|
|
76
|
+
throw new KubernetesError("Read " + resource + " in namespace " + namespace + " failed", {
|
|
77
|
+
kind: timedOut ? "timeout" : type === "max-size" ? "limit_exceeded" : type === "invalid-json" ? "invalid_response" : tls ? "tls_verification_failed" : code === "ECONNRESET" ? "connection_lost" : "connection_failed",
|
|
78
|
+
cause,
|
|
79
|
+
code
|
|
80
|
+
});
|
|
81
|
+
} finally {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
signal.removeEventListener("abort", abort);
|
|
84
|
+
}
|
|
85
|
+
}, signal)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { resourceAccess };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ServicePortForwarder
|
|
3
|
+
} from "./chunk-205a4vma.js";
|
|
4
|
+
import {
|
|
5
|
+
KubectlExecutor
|
|
6
|
+
} from "./chunk-0rs1aacd.js";
|
|
7
|
+
import {
|
|
8
|
+
resourceAccess
|
|
9
|
+
} from "./chunk-1kpbvat9.js";
|
|
10
|
+
import {
|
|
11
|
+
resolveWorkload
|
|
12
|
+
} from "./chunk-y3dx8jh6.js";
|
|
13
|
+
import {
|
|
14
|
+
KubernetesError
|
|
15
|
+
} from "./chunk-ntfm5e85.js";
|
|
16
|
+
|
|
17
|
+
// src/kubernetes/client.ts
|
|
18
|
+
class KubernetesClient {
|
|
19
|
+
kube;
|
|
20
|
+
resourceLimits;
|
|
21
|
+
#controller = new AbortController;
|
|
22
|
+
signal;
|
|
23
|
+
#executors = new Map;
|
|
24
|
+
#forwarders = new Map;
|
|
25
|
+
#operations = new Set;
|
|
26
|
+
#disposal;
|
|
27
|
+
#resources;
|
|
28
|
+
constructor(kube, signal, executor, resourceLimits = { timeoutMs: 30000, concurrency: 4, maxBytes: 8 * 1024 * 1024 }, resources) {
|
|
29
|
+
this.kube = kube;
|
|
30
|
+
this.resourceLimits = resourceLimits;
|
|
31
|
+
this.signal = signal ? AbortSignal.any([signal, this.#controller.signal]) : this.#controller.signal;
|
|
32
|
+
if (executor)
|
|
33
|
+
this.#executors.set(kube.namespace, executor);
|
|
34
|
+
this.#resources = resources;
|
|
35
|
+
}
|
|
36
|
+
async initialize() {
|
|
37
|
+
this.signal.throwIfAborted();
|
|
38
|
+
}
|
|
39
|
+
resolveWorkload(workload, environment) {
|
|
40
|
+
return this.#track(() => {
|
|
41
|
+
const namespace = workload.namespace ?? this.kube.namespace;
|
|
42
|
+
if (!namespace.trim())
|
|
43
|
+
throw new KubernetesError("Workload namespace is required", { kind: "invalid_argument" });
|
|
44
|
+
if (!environment.trim())
|
|
45
|
+
throw new KubernetesError("Environment identity is required", { kind: "invalid_argument" });
|
|
46
|
+
this.#resources ??= resourceAccess(this.kube, this.signal, this.resourceLimits);
|
|
47
|
+
return resolveWorkload(this.#resources, workload, namespace, environment);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
#executor(namespace) {
|
|
51
|
+
let executor = this.#executors.get(namespace);
|
|
52
|
+
if (!executor) {
|
|
53
|
+
executor = new KubectlExecutor({ ...this.kube, namespace });
|
|
54
|
+
this.#executors.set(namespace, executor);
|
|
55
|
+
}
|
|
56
|
+
return executor;
|
|
57
|
+
}
|
|
58
|
+
#track(work) {
|
|
59
|
+
this.signal.throwIfAborted();
|
|
60
|
+
const pending = Promise.resolve().then(() => {
|
|
61
|
+
this.signal.throwIfAborted();
|
|
62
|
+
return work();
|
|
63
|
+
});
|
|
64
|
+
this.#operations.add(pending);
|
|
65
|
+
pending.then(() => this.#operations.delete(pending), () => this.#operations.delete(pending));
|
|
66
|
+
return pending;
|
|
67
|
+
}
|
|
68
|
+
#options(options) {
|
|
69
|
+
return { ...options, signal: options?.signal ? AbortSignal.any([this.signal, options.signal]) : this.signal };
|
|
70
|
+
}
|
|
71
|
+
run(namespace, command, options) {
|
|
72
|
+
return this.#track(() => this.#executor(namespace).run(command, this.#options(options)));
|
|
73
|
+
}
|
|
74
|
+
exec(namespace, target, command, options) {
|
|
75
|
+
return this.#track(() => this.#executor(namespace).exec(target, command, this.#options(options)));
|
|
76
|
+
}
|
|
77
|
+
forward(namespace, target) {
|
|
78
|
+
return this.#track(async () => {
|
|
79
|
+
let pending = this.#forwarders.get(namespace);
|
|
80
|
+
if (!pending) {
|
|
81
|
+
pending = ServicePortForwarder.create(this.#executor(namespace), { ...this.kube, namespace });
|
|
82
|
+
this.#forwarders.set(namespace, pending);
|
|
83
|
+
pending.catch(() => {
|
|
84
|
+
if (this.#forwarders.get(namespace) === pending)
|
|
85
|
+
this.#forwarders.delete(namespace);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const forwarder = await pending;
|
|
89
|
+
this.signal.throwIfAborted();
|
|
90
|
+
return forwarder.forward(target);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
dispose() {
|
|
94
|
+
return this.#disposal ??= (async () => {
|
|
95
|
+
this.#controller.abort(new Error("Kubernetes client disposed"));
|
|
96
|
+
await Promise.allSettled(this.#operations);
|
|
97
|
+
const results = await Promise.allSettled([...this.#forwarders.values()].map(async (pending) => {
|
|
98
|
+
const forwarder = await pending.catch(() => {
|
|
99
|
+
return;
|
|
100
|
+
});
|
|
101
|
+
forwarder?.stop();
|
|
102
|
+
}));
|
|
103
|
+
this.#forwarders.clear();
|
|
104
|
+
const errors = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
105
|
+
if (errors.length)
|
|
106
|
+
throw new AggregateError(errors, "Kubernetes client cleanup failed");
|
|
107
|
+
})();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export { KubernetesClient };
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
LogCaptureFile
|
|
10
10
|
} from "./chunk-1vfwvgkj.js";
|
|
11
11
|
import {
|
|
12
|
-
|
|
13
|
-
} from "./chunk-
|
|
12
|
+
clientKey
|
|
13
|
+
} from "./chunk-a9htb6cr.js";
|
|
14
14
|
|
|
15
15
|
// src/kubernetes/pod-log-client.ts
|
|
16
16
|
import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
@@ -40,7 +40,7 @@ class PodLogClient {
|
|
|
40
40
|
this.signal.throwIfAborted();
|
|
41
41
|
if (!this.#directory || this.#disposal)
|
|
42
42
|
throw new Error("Log capture session is not active");
|
|
43
|
-
const key = scope.instance && request.sinceTime ?
|
|
43
|
+
const key = scope.instance && request.sinceTime ? clientKey("pod-log", {
|
|
44
44
|
scope,
|
|
45
45
|
pod: request.pod,
|
|
46
46
|
container: request.container,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
class ToolboxError extends Error {
|
|
3
|
+
kind;
|
|
4
|
+
code;
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message, { cause: options.cause });
|
|
7
|
+
this.name = new.target.name;
|
|
8
|
+
this.kind = options.kind;
|
|
9
|
+
this.code = options.code;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class KubernetesError extends ToolboxError {
|
|
14
|
+
}
|
|
15
|
+
function kubernetesHttpError(status) {
|
|
16
|
+
const kind = {
|
|
17
|
+
401: "authentication_failed",
|
|
18
|
+
403: "permission_denied",
|
|
19
|
+
404: "resource_not_found",
|
|
20
|
+
408: "timeout",
|
|
21
|
+
429: "limit_exceeded",
|
|
22
|
+
504: "timeout"
|
|
23
|
+
}[status] ?? "operation_failed";
|
|
24
|
+
return new KubernetesError("Kubernetes resource request failed", { kind, code: status });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { ToolboxError, KubernetesError, kubernetesHttpError };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KubernetesError
|
|
3
|
+
} from "./chunk-ntfm5e85.js";
|
|
4
|
+
|
|
5
|
+
// src/kubernetes/workload.ts
|
|
6
|
+
function failure(message, kind) {
|
|
7
|
+
throw new KubernetesError(message, { kind });
|
|
8
|
+
}
|
|
9
|
+
function selector(value, service = false) {
|
|
10
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
11
|
+
return failure("Resource has no Pod selector", "unsupported_operation");
|
|
12
|
+
}
|
|
13
|
+
const input = value;
|
|
14
|
+
const labels = service ? value : input.matchLabels ?? {};
|
|
15
|
+
const terms = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => {
|
|
16
|
+
if (!key || /[,=()!\s]/.test(key) || typeof item !== "string" || /[,=()!\s]/.test(item)) {
|
|
17
|
+
return failure("Invalid label selector", "invalid_argument");
|
|
18
|
+
}
|
|
19
|
+
return key + "=" + item;
|
|
20
|
+
});
|
|
21
|
+
for (const entry of service ? [] : input.matchExpressions ?? []) {
|
|
22
|
+
const values = entry.values ?? [];
|
|
23
|
+
if (!entry.key || /[,=()!\s]/.test(entry.key) || values.some((v) => /[,=()!\s]/.test(v))) {
|
|
24
|
+
return failure("Invalid selector expression", "invalid_response");
|
|
25
|
+
}
|
|
26
|
+
if ((entry.operator === "In" || entry.operator === "NotIn") && values.length) {
|
|
27
|
+
terms.push(entry.key + (entry.operator === "In" ? " in (" : " notin (") + [...values].sort().join(",") + ")");
|
|
28
|
+
} else if (entry.operator === "Exists" && !values.length)
|
|
29
|
+
terms.push(entry.key);
|
|
30
|
+
else if (entry.operator === "DoesNotExist" && !values.length)
|
|
31
|
+
terms.push("!" + entry.key);
|
|
32
|
+
else
|
|
33
|
+
return failure("Unsupported selector expression", "invalid_response");
|
|
34
|
+
}
|
|
35
|
+
if (!terms.length)
|
|
36
|
+
return failure("Resource has no Pod selector", "unsupported_operation");
|
|
37
|
+
return terms.join(",");
|
|
38
|
+
}
|
|
39
|
+
async function resolveWorkload(access, workload, namespace, environment) {
|
|
40
|
+
if (!environment.trim())
|
|
41
|
+
return failure("Environment identity is required", "invalid_argument");
|
|
42
|
+
if (!namespace.trim())
|
|
43
|
+
return failure("Workload namespace is required", "invalid_argument");
|
|
44
|
+
const location = workload.location;
|
|
45
|
+
let selected;
|
|
46
|
+
let pods;
|
|
47
|
+
if (location.kind === "labels") {
|
|
48
|
+
if (!Object.keys(location.labels).length)
|
|
49
|
+
return failure("Pod labels must be nonempty", "invalid_argument");
|
|
50
|
+
selected = selector(location.labels, true);
|
|
51
|
+
const result = await access.get(namespace, "pods", undefined, selected);
|
|
52
|
+
if (!Array.isArray(result.items))
|
|
53
|
+
return failure("Pod list lacks items", "invalid_response");
|
|
54
|
+
pods = result.items;
|
|
55
|
+
} else {
|
|
56
|
+
const resource = location.kind === "service" ? "services" : {
|
|
57
|
+
Pod: "pods",
|
|
58
|
+
Deployment: "deployments",
|
|
59
|
+
StatefulSet: "statefulsets",
|
|
60
|
+
DaemonSet: "daemonsets"
|
|
61
|
+
}[location.resource_kind];
|
|
62
|
+
if (!resource)
|
|
63
|
+
return failure("Unsupported workload resource", "unsupported_operation");
|
|
64
|
+
if (!location.name.trim())
|
|
65
|
+
return failure("Resource name is required", "invalid_argument");
|
|
66
|
+
const item = await access.get(namespace, resource, location.name);
|
|
67
|
+
if (resource === "pods")
|
|
68
|
+
pods = [item];
|
|
69
|
+
else {
|
|
70
|
+
selected = selector(item.spec?.selector, resource === "services");
|
|
71
|
+
const result = await access.get(namespace, "pods", undefined, selected);
|
|
72
|
+
if (!Array.isArray(result.items))
|
|
73
|
+
return failure("Pod list lacks items", "invalid_response");
|
|
74
|
+
pods = result.items;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return pods.map((pod) => {
|
|
78
|
+
const metadata = pod.metadata;
|
|
79
|
+
if (!metadata?.name || !metadata.uid || metadata.namespace !== namespace) {
|
|
80
|
+
return failure("Pod response lacks identity or has a different namespace", "invalid_response");
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
platform: "kubernetes",
|
|
84
|
+
environment,
|
|
85
|
+
workload: workload.name,
|
|
86
|
+
namespace,
|
|
87
|
+
pod: metadata.name,
|
|
88
|
+
uid: metadata.uid,
|
|
89
|
+
container: workload.container
|
|
90
|
+
};
|
|
91
|
+
}).sort((a, b) => a.pod.localeCompare(b.pod) || a.uid.localeCompare(b.uid));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { resolveWorkload };
|
package/dist/client-manager.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ClientManager
|
|
1
|
+
export { ClientManager } from "@compforge/harness-common";
|
package/dist/client.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export { clientKey, type Client, type ClientManager, type ClientProvider } from "@compforge/harness-common";
|
package/dist/client.js
CHANGED
package/dist/datasource.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { clientKey, ClientManager, EnvironmentContext, type Client, type ClientProvider, type DataSource, type ServiceDataSource } from "@compforge/harness-common";
|
|
2
2
|
import type { Transport } from "./transport";
|
|
3
|
+
export { ToolboxError, KubernetesError, type ErrorKind } from "./errors";
|
|
3
4
|
/** Resolution owns configuration semantics; transports preserve the resolved target and identity. */
|
|
4
5
|
export interface ConnectionSource<Target> {
|
|
5
6
|
resolve(): Promise<Target>;
|
package/dist/datasource.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ClientManager,
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
EnvironmentContext,
|
|
4
|
+
clientKey
|
|
5
|
+
} from "./chunk-z5pm2emz.js";
|
|
6
|
+
import {
|
|
7
|
+
KubernetesError,
|
|
8
|
+
ToolboxError
|
|
9
|
+
} from "./chunk-ntfm5e85.js";
|
|
5
10
|
export {
|
|
6
|
-
|
|
11
|
+
clientKey,
|
|
12
|
+
ToolboxError,
|
|
13
|
+
KubernetesError,
|
|
14
|
+
EnvironmentContext,
|
|
7
15
|
ClientManager
|
|
8
16
|
};
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Stable toolbox failure semantics; native causes are for debugging, not user reports. */
|
|
2
|
+
export type ErrorKind = "connection_failed" | "connection_lost" | "tls_verification_failed" | "authentication_failed" | "permission_denied" | "resource_not_found" | "timeout" | "limit_exceeded" | "invalid_response" | "operation_failed" | "invalid_argument" | "unsupported_operation";
|
|
3
|
+
export declare class ToolboxError extends Error {
|
|
4
|
+
readonly kind: ErrorKind;
|
|
5
|
+
readonly code?: number | string;
|
|
6
|
+
constructor(message: string, options: {
|
|
7
|
+
kind: ErrorKind;
|
|
8
|
+
code?: number | string;
|
|
9
|
+
cause?: unknown;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export declare class KubernetesError extends ToolboxError {
|
|
13
|
+
}
|
|
14
|
+
export declare function kubernetesHttpError(status: number): KubernetesError;
|
package/dist/errors.js
ADDED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import type { Client } from "../client";
|
|
2
2
|
import { type Executor, type KubectlOptions, type ExecTarget, type RunOptions } from "./executor";
|
|
3
3
|
import type { KubernetesEndpoint } from "./endpoint";
|
|
4
|
+
import type { Workload, WorkloadInstance } from "@compforge/harness-common";
|
|
5
|
+
import { type ResourceAccess, type ResourceLimits } from "./resources";
|
|
4
6
|
/** Owns cluster transports; protocol callers borrow this client without closing it. */
|
|
5
7
|
export declare class KubernetesClient implements Client {
|
|
6
8
|
#private;
|
|
7
9
|
private readonly kube;
|
|
10
|
+
private readonly resourceLimits;
|
|
8
11
|
readonly signal: AbortSignal;
|
|
9
12
|
constructor(kube: KubectlOptions & {
|
|
10
13
|
namespace: string;
|
|
11
|
-
}, signal?: AbortSignal, executor?: Executor);
|
|
14
|
+
}, signal?: AbortSignal, executor?: Executor, resourceLimits?: ResourceLimits, resources?: ResourceAccess);
|
|
12
15
|
initialize(): Promise<void>;
|
|
16
|
+
/** environment is a stable target ID from the caller's registry, not access configuration. */
|
|
17
|
+
resolveWorkload(workload: Workload, environment: string): Promise<WorkloadInstance[]>;
|
|
13
18
|
run(namespace: string, command: string[], options?: RunOptions): Promise<import("./executor").ExecResult>;
|
|
14
19
|
exec(namespace: string, target: ExecTarget, command: string[], options?: RunOptions): Promise<import("./executor").ExecResult>;
|
|
15
20
|
forward(namespace: string, target: KubernetesEndpoint): Promise<import("./service-port-forward").ForwardedEndpoint>;
|
|
@@ -1,92 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "../chunk-
|
|
2
|
+
KubernetesClient
|
|
3
|
+
} from "../chunk-4pdwsfcx.js";
|
|
4
|
+
import"../chunk-205a4vma.js";
|
|
4
5
|
import"../chunk-hv2kx47y.js";
|
|
5
|
-
import {
|
|
6
|
-
KubectlExecutor
|
|
7
|
-
} from "../chunk-0rs1aacd.js";
|
|
8
|
-
import"../chunk-ztenxgjv.js";
|
|
9
6
|
import"../chunk-j46x7j7c.js";
|
|
10
7
|
import"../chunk-6fnhzhdb.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
#executors = new Map;
|
|
18
|
-
#forwarders = new Map;
|
|
19
|
-
#operations = new Set;
|
|
20
|
-
#disposal;
|
|
21
|
-
constructor(kube, signal, executor) {
|
|
22
|
-
this.kube = kube;
|
|
23
|
-
this.signal = signal ? AbortSignal.any([signal, this.#controller.signal]) : this.#controller.signal;
|
|
24
|
-
if (executor)
|
|
25
|
-
this.#executors.set(kube.namespace, executor);
|
|
26
|
-
}
|
|
27
|
-
async initialize() {
|
|
28
|
-
this.signal.throwIfAborted();
|
|
29
|
-
}
|
|
30
|
-
#executor(namespace) {
|
|
31
|
-
let executor = this.#executors.get(namespace);
|
|
32
|
-
if (!executor) {
|
|
33
|
-
executor = new KubectlExecutor({ ...this.kube, namespace });
|
|
34
|
-
this.#executors.set(namespace, executor);
|
|
35
|
-
}
|
|
36
|
-
return executor;
|
|
37
|
-
}
|
|
38
|
-
#track(work) {
|
|
39
|
-
this.signal.throwIfAborted();
|
|
40
|
-
const pending = Promise.resolve().then(() => {
|
|
41
|
-
this.signal.throwIfAborted();
|
|
42
|
-
return work();
|
|
43
|
-
});
|
|
44
|
-
this.#operations.add(pending);
|
|
45
|
-
pending.then(() => this.#operations.delete(pending), () => this.#operations.delete(pending));
|
|
46
|
-
return pending;
|
|
47
|
-
}
|
|
48
|
-
#options(options) {
|
|
49
|
-
return { ...options, signal: options?.signal ? AbortSignal.any([this.signal, options.signal]) : this.signal };
|
|
50
|
-
}
|
|
51
|
-
run(namespace, command, options) {
|
|
52
|
-
return this.#track(() => this.#executor(namespace).run(command, this.#options(options)));
|
|
53
|
-
}
|
|
54
|
-
exec(namespace, target, command, options) {
|
|
55
|
-
return this.#track(() => this.#executor(namespace).exec(target, command, this.#options(options)));
|
|
56
|
-
}
|
|
57
|
-
forward(namespace, target) {
|
|
58
|
-
return this.#track(async () => {
|
|
59
|
-
let pending = this.#forwarders.get(namespace);
|
|
60
|
-
if (!pending) {
|
|
61
|
-
pending = ServicePortForwarder.create(this.#executor(namespace), { ...this.kube, namespace });
|
|
62
|
-
this.#forwarders.set(namespace, pending);
|
|
63
|
-
pending.catch(() => {
|
|
64
|
-
if (this.#forwarders.get(namespace) === pending)
|
|
65
|
-
this.#forwarders.delete(namespace);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
const forwarder = await pending;
|
|
69
|
-
this.signal.throwIfAborted();
|
|
70
|
-
return forwarder.forward(target);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
dispose() {
|
|
74
|
-
return this.#disposal ??= (async () => {
|
|
75
|
-
this.#controller.abort(new Error("Kubernetes client disposed"));
|
|
76
|
-
await Promise.allSettled(this.#operations);
|
|
77
|
-
const results = await Promise.allSettled([...this.#forwarders.values()].map(async (pending) => {
|
|
78
|
-
const forwarder = await pending.catch(() => {
|
|
79
|
-
return;
|
|
80
|
-
});
|
|
81
|
-
forwarder?.stop();
|
|
82
|
-
}));
|
|
83
|
-
this.#forwarders.clear();
|
|
84
|
-
const errors = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
85
|
-
if (errors.length)
|
|
86
|
-
throw new AggregateError(errors, "Kubernetes client cleanup failed");
|
|
87
|
-
})();
|
|
88
|
-
}
|
|
89
|
-
}
|
|
8
|
+
import"../chunk-0rs1aacd.js";
|
|
9
|
+
import"../chunk-ztenxgjv.js";
|
|
10
|
+
import"../chunk-1kpbvat9.js";
|
|
11
|
+
import"../chunk-y3dx8jh6.js";
|
|
12
|
+
import"../chunk-v3hxvnrc.js";
|
|
13
|
+
import"../chunk-ntfm5e85.js";
|
|
90
14
|
export {
|
|
91
15
|
KubernetesClient
|
|
92
16
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ClientProvider, type ClientManager } from "../client";
|
|
2
|
+
import type { Environment } from "@compforge/harness-common";
|
|
3
|
+
import { KubernetesClient } from "./client";
|
|
4
|
+
import type { KubectlOptions } from "./executor";
|
|
5
|
+
import type { ResourceLimits } from "./resources";
|
|
6
|
+
/** Cluster access is a client provider; the execution owns client lifetime. */
|
|
7
|
+
export declare class KubernetesEnvironment implements Environment, ClientProvider<KubernetesClient> {
|
|
8
|
+
#private;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly kind = "kubernetes";
|
|
11
|
+
readonly clientKey: string;
|
|
12
|
+
/** Deployment metadata only; not part of client reuse or access configuration. */
|
|
13
|
+
readonly imageRegistry?: string;
|
|
14
|
+
constructor(name: string, kube: KubectlOptions & {
|
|
15
|
+
namespace: string;
|
|
16
|
+
imageRegistry?: string;
|
|
17
|
+
}, limits: ResourceLimits);
|
|
18
|
+
createClient(_clients: Pick<ClientManager, "get">, signal: AbortSignal): KubernetesClient;
|
|
19
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KubernetesClient
|
|
3
|
+
} from "../chunk-4pdwsfcx.js";
|
|
4
|
+
import"../chunk-205a4vma.js";
|
|
5
|
+
import"../chunk-hv2kx47y.js";
|
|
6
|
+
import"../chunk-j46x7j7c.js";
|
|
7
|
+
import"../chunk-6fnhzhdb.js";
|
|
8
|
+
import"../chunk-0rs1aacd.js";
|
|
9
|
+
import"../chunk-ztenxgjv.js";
|
|
10
|
+
import"../chunk-1kpbvat9.js";
|
|
11
|
+
import"../chunk-y3dx8jh6.js";
|
|
12
|
+
import {
|
|
13
|
+
clientKey
|
|
14
|
+
} from "../chunk-a9htb6cr.js";
|
|
15
|
+
import"../chunk-v3hxvnrc.js";
|
|
16
|
+
import"../chunk-ntfm5e85.js";
|
|
17
|
+
|
|
18
|
+
// src/kubernetes/environment.ts
|
|
19
|
+
class KubernetesEnvironment {
|
|
20
|
+
name;
|
|
21
|
+
kind = "kubernetes";
|
|
22
|
+
clientKey;
|
|
23
|
+
imageRegistry;
|
|
24
|
+
#kube;
|
|
25
|
+
#limits;
|
|
26
|
+
constructor(name, kube, limits) {
|
|
27
|
+
this.name = name;
|
|
28
|
+
this.imageRegistry = kube.imageRegistry;
|
|
29
|
+
this.#kube = Object.freeze({
|
|
30
|
+
namespace: kube.namespace,
|
|
31
|
+
kubeconfig: kube.kubeconfig,
|
|
32
|
+
context: kube.context
|
|
33
|
+
});
|
|
34
|
+
this.#limits = Object.freeze({ ...limits });
|
|
35
|
+
this.clientKey = clientKey("kubernetes-client", [this.#kube, this.#limits]);
|
|
36
|
+
}
|
|
37
|
+
createClient(_clients, signal) {
|
|
38
|
+
return new KubernetesClient(this.#kube, signal, undefined, this.#limits);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
KubernetesEnvironment
|
|
43
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PodLogClient
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-mnad4zkp.js";
|
|
4
4
|
import"../chunk-8nm9f0hh.js";
|
|
5
5
|
import"../chunk-mp88s7r7.js";
|
|
6
6
|
import"../chunk-1vfwvgkj.js";
|
|
7
|
-
import"../chunk-
|
|
7
|
+
import"../chunk-a9htb6cr.js";
|
|
8
8
|
import"../chunk-v3hxvnrc.js";
|
|
9
9
|
export {
|
|
10
10
|
PodLogClient
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DataSource } from "../datasource";
|
|
1
|
+
import { type DataSource, type ClientManager } from "../datasource";
|
|
2
2
|
import type { ConcurrencyPool } from "../concurrency";
|
|
3
3
|
import { PodLogClient } from "./pod-log-client";
|
|
4
4
|
import type { PodLogByteBudget, PodLogCapturePolicy } from "./log-capture-plan";
|
|
@@ -7,11 +7,11 @@ export declare class PodLogDataSource implements DataSource<PodLogClient> {
|
|
|
7
7
|
private readonly pool;
|
|
8
8
|
private readonly budget;
|
|
9
9
|
private readonly policy;
|
|
10
|
-
readonly
|
|
10
|
+
readonly clientKey: string;
|
|
11
11
|
constructor(target: {
|
|
12
12
|
kubeconfig?: string;
|
|
13
13
|
context?: string;
|
|
14
14
|
namespace: string;
|
|
15
15
|
}, pool: ConcurrencyPool, budget: PodLogByteBudget, policy: PodLogCapturePolicy);
|
|
16
|
-
createClient(signal: AbortSignal): PodLogClient;
|
|
16
|
+
createClient(_clients: Pick<ClientManager, "get">, signal: AbortSignal): PodLogClient;
|
|
17
17
|
}
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PodLogClient
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-mnad4zkp.js";
|
|
4
4
|
import"../chunk-8nm9f0hh.js";
|
|
5
5
|
import"../chunk-mp88s7r7.js";
|
|
6
6
|
import"../chunk-1vfwvgkj.js";
|
|
7
|
+
import"../chunk-a9htb6cr.js";
|
|
7
8
|
import {
|
|
8
|
-
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
clientKey
|
|
10
|
+
} from "../chunk-z5pm2emz.js";
|
|
10
11
|
import"../chunk-v3hxvnrc.js";
|
|
12
|
+
import"../chunk-ntfm5e85.js";
|
|
11
13
|
|
|
12
14
|
// src/kubernetes/pod-log-datasource.ts
|
|
13
15
|
class PodLogDataSource {
|
|
14
16
|
pool;
|
|
15
17
|
budget;
|
|
16
18
|
policy;
|
|
17
|
-
|
|
19
|
+
clientKey;
|
|
18
20
|
constructor(target, pool, budget, policy) {
|
|
19
21
|
this.pool = pool;
|
|
20
22
|
this.budget = budget;
|
|
21
23
|
this.policy = policy;
|
|
22
|
-
this.
|
|
24
|
+
this.clientKey = clientKey("pod-log", target);
|
|
23
25
|
}
|
|
24
|
-
createClient(signal) {
|
|
26
|
+
createClient(_clients, signal) {
|
|
25
27
|
return new PodLogClient(this.pool, signal, this.policy, this.budget);
|
|
26
28
|
}
|
|
27
29
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { KubectlOptions } from "./executor";
|
|
2
|
+
export interface Resource {
|
|
3
|
+
metadata?: {
|
|
4
|
+
name?: string;
|
|
5
|
+
namespace?: string;
|
|
6
|
+
uid?: string;
|
|
7
|
+
};
|
|
8
|
+
spec?: {
|
|
9
|
+
selector?: unknown;
|
|
10
|
+
};
|
|
11
|
+
items?: Resource[];
|
|
12
|
+
kind?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ResourceAccess {
|
|
15
|
+
get(namespace: string, resource: string, name?: string, selector?: string): Promise<Resource>;
|
|
16
|
+
}
|
|
17
|
+
export interface ResourceLimits {
|
|
18
|
+
timeoutMs: number;
|
|
19
|
+
concurrency: number;
|
|
20
|
+
maxBytes: number;
|
|
21
|
+
}
|
|
22
|
+
/** Native JSON reads use the same client-node auth/TLS path as native Pod logs. */
|
|
23
|
+
export declare function resourceAccess(options: KubectlOptions, signal: AbortSignal, limits: ResourceLimits): ResourceAccess;
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
ServicePortForwarder
|
|
3
3
|
} from "../chunk-205a4vma.js";
|
|
4
4
|
import"../chunk-hv2kx47y.js";
|
|
5
|
-
import"../chunk-0rs1aacd.js";
|
|
6
|
-
import"../chunk-ztenxgjv.js";
|
|
7
5
|
import"../chunk-j46x7j7c.js";
|
|
8
6
|
import"../chunk-6fnhzhdb.js";
|
|
7
|
+
import"../chunk-0rs1aacd.js";
|
|
8
|
+
import"../chunk-ztenxgjv.js";
|
|
9
9
|
export {
|
|
10
10
|
ServicePortForwarder
|
|
11
11
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Workload, WorkloadInstance } from "@compforge/harness-common";
|
|
2
|
+
import type { ResourceAccess } from "./resources";
|
|
3
|
+
/**
|
|
4
|
+
* No readiness filter or replica selection; consumers own collection policy.
|
|
5
|
+
* @rule environment is the caller's stable, non-secret target ID, independent of access routes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveWorkload(access: ResourceAccess, workload: Workload, namespace: string, environment: string): Promise<WorkloadInstance[]>;
|
package/dist/mysql/client.js
CHANGED
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
MysqlClient,
|
|
3
3
|
MysqlDatabase,
|
|
4
4
|
openMysql
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import"../chunk-s06hs7a8.js";
|
|
5
|
+
} from "../chunk-jkpwa8tb.js";
|
|
7
6
|
import"../chunk-kbewz29a.js";
|
|
7
|
+
import"../chunk-s06hs7a8.js";
|
|
8
8
|
import"../chunk-v3hxvnrc.js";
|
|
9
9
|
import"../chunk-xy4g56g4.js";
|
|
10
10
|
export {
|
package/dist/mysql/index.js
CHANGED
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
MysqlClient,
|
|
7
7
|
MysqlDatabase,
|
|
8
8
|
openMysql
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import"../chunk-s06hs7a8.js";
|
|
9
|
+
} from "../chunk-jkpwa8tb.js";
|
|
11
10
|
import"../chunk-kbewz29a.js";
|
|
11
|
+
import"../chunk-s06hs7a8.js";
|
|
12
12
|
import"../chunk-v3hxvnrc.js";
|
|
13
13
|
import"../chunk-xy4g56g4.js";
|
|
14
14
|
export {
|
package/dist/redis/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
discoverRedisTopology
|
|
3
|
+
} from "../chunk-c6z919qb.js";
|
|
1
4
|
import {
|
|
2
5
|
RedisAccess,
|
|
3
6
|
RedisClient,
|
|
@@ -9,9 +12,6 @@ import {
|
|
|
9
12
|
collectRedisKeyStats,
|
|
10
13
|
redisGroupedSampleSizes
|
|
11
14
|
} from "../chunk-jr9y0ghr.js";
|
|
12
|
-
import {
|
|
13
|
-
discoverRedisTopology
|
|
14
|
-
} from "../chunk-c6z919qb.js";
|
|
15
15
|
export {
|
|
16
16
|
redisGroupedSampleSizes,
|
|
17
17
|
parseRedisInfo,
|
package/dist/s3/datasource.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { type DataSource } from "../datasource";
|
|
1
|
+
import { type DataSource, type ClientManager } from "../datasource";
|
|
2
2
|
import { type TcpTransport } from "../transport";
|
|
3
3
|
import { S3Client } from "./client";
|
|
4
4
|
import type { S3Limits, S3Target } from "./types";
|
|
5
5
|
/** Connection details are resolved by the caller; route identity includes the cluster/context, not just a transport name. */
|
|
6
6
|
export declare class S3DataSource implements DataSource<S3Client> {
|
|
7
7
|
#private;
|
|
8
|
-
readonly
|
|
8
|
+
readonly clientKey: string;
|
|
9
9
|
constructor(target: S3Target, limits: S3Limits, route?: {
|
|
10
10
|
key: string;
|
|
11
11
|
transport: TcpTransport;
|
|
12
12
|
});
|
|
13
|
-
createClient(signal: AbortSignal): S3Client;
|
|
13
|
+
createClient(_clients: Pick<ClientManager, "get">, signal: AbortSignal): S3Client;
|
|
14
14
|
}
|
package/dist/s3/datasource.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
S3DataSource
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import"../chunk-
|
|
3
|
+
} from "../chunk-0cq1gx86.js";
|
|
4
|
+
import"../chunk-z5pm2emz.js";
|
|
5
5
|
import"../chunk-zp1s833x.js";
|
|
6
6
|
import"../chunk-v3hxvnrc.js";
|
|
7
7
|
import"../chunk-x4ntb898.js";
|
|
8
8
|
import"../chunk-xy4g56g4.js";
|
|
9
|
+
import"../chunk-ntfm5e85.js";
|
|
9
10
|
export {
|
|
10
11
|
S3DataSource
|
|
11
12
|
};
|
package/dist/s3/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
S3DataSource
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import"../chunk-
|
|
3
|
+
} from "../chunk-0cq1gx86.js";
|
|
4
|
+
import"../chunk-z5pm2emz.js";
|
|
5
5
|
import {
|
|
6
6
|
S3Client
|
|
7
7
|
} from "../chunk-zp1s833x.js";
|
|
8
8
|
import"../chunk-v3hxvnrc.js";
|
|
9
9
|
import"../chunk-x4ntb898.js";
|
|
10
10
|
import"../chunk-xy4g56g4.js";
|
|
11
|
+
import"../chunk-ntfm5e85.js";
|
|
11
12
|
export {
|
|
12
13
|
S3DataSource,
|
|
13
14
|
S3Client
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@compforge/harness-toolbox",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Shared infrastructure clients, transports and bounded data capture",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
"build": "bun scripts/build.ts && tsc -p tsconfig.build.json"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
|
+
"./errors": {
|
|
13
|
+
"types": "./dist/errors.d.ts",
|
|
14
|
+
"import": "./dist/errors.js"
|
|
15
|
+
},
|
|
12
16
|
".": {
|
|
13
17
|
"types": "./dist/datasource.d.ts",
|
|
14
18
|
"import": "./dist/datasource.js"
|
|
@@ -83,7 +87,7 @@
|
|
|
83
87
|
"README.md"
|
|
84
88
|
],
|
|
85
89
|
"dependencies": {
|
|
86
|
-
"@compforge/harness-common": "0.0
|
|
90
|
+
"@compforge/harness-common": "0.3.0",
|
|
87
91
|
"@aws-sdk/client-s3": "3.1133.0",
|
|
88
92
|
"@kubernetes/client-node": "1.4.0",
|
|
89
93
|
"@opensearch-project/opensearch": "3.6.0",
|
package/dist/chunk-fnc291vk.js
DELETED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
queryMysqlViaPod
|
|
3
|
-
} from "./chunk-s06hs7a8.js";
|
|
4
1
|
import {
|
|
5
2
|
queryReadonlySession,
|
|
6
3
|
validateQueryLimits
|
|
7
4
|
} from "./chunk-kbewz29a.js";
|
|
5
|
+
import {
|
|
6
|
+
queryMysqlViaPod
|
|
7
|
+
} from "./chunk-s06hs7a8.js";
|
|
8
8
|
import {
|
|
9
9
|
ConcurrencyPool
|
|
10
10
|
} from "./chunk-v3hxvnrc.js";
|