rightsize 0.1.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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +255 -0
- package/dist/backend-docker/backend.d.ts +53 -0
- package/dist/backend-docker/backend.d.ts.map +1 -0
- package/dist/backend-docker/backend.js +336 -0
- package/dist/backend-docker/backend.js.map +1 -0
- package/dist/backend-docker/client.d.ts +82 -0
- package/dist/backend-docker/client.d.ts.map +1 -0
- package/dist/backend-docker/client.js +190 -0
- package/dist/backend-docker/client.js.map +1 -0
- package/dist/backend-docker/frames.d.ts +44 -0
- package/dist/backend-docker/frames.d.ts.map +1 -0
- package/dist/backend-docker/frames.js +105 -0
- package/dist/backend-docker/frames.js.map +1 -0
- package/dist/backend-docker/index.d.ts +5 -0
- package/dist/backend-docker/index.d.ts.map +1 -0
- package/dist/backend-docker/index.js +20 -0
- package/dist/backend-docker/index.js.map +1 -0
- package/dist/backend-docker/json.d.ts +13 -0
- package/dist/backend-docker/json.d.ts.map +1 -0
- package/dist/backend-docker/json.js +47 -0
- package/dist/backend-docker/json.js.map +1 -0
- package/dist/backend-docker/labels.d.ts +11 -0
- package/dist/backend-docker/labels.d.ts.map +1 -0
- package/dist/backend-docker/labels.js +13 -0
- package/dist/backend-docker/labels.js.map +1 -0
- package/dist/backend-docker/port-conflict.d.ts +9 -0
- package/dist/backend-docker/port-conflict.d.ts.map +1 -0
- package/dist/backend-docker/port-conflict.js +12 -0
- package/dist/backend-docker/port-conflict.js.map +1 -0
- package/dist/backend-docker/provider.d.ts +22 -0
- package/dist/backend-docker/provider.d.ts.map +1 -0
- package/dist/backend-docker/provider.js +36 -0
- package/dist/backend-docker/provider.js.map +1 -0
- package/dist/backend-msb/backend.d.ts +169 -0
- package/dist/backend-msb/backend.d.ts.map +1 -0
- package/dist/backend-msb/backend.js +664 -0
- package/dist/backend-msb/backend.js.map +1 -0
- package/dist/backend-msb/commands.d.ts +28 -0
- package/dist/backend-msb/commands.d.ts.map +1 -0
- package/dist/backend-msb/commands.js +67 -0
- package/dist/backend-msb/commands.js.map +1 -0
- package/dist/backend-msb/exec-tunnel.d.ts +33 -0
- package/dist/backend-msb/exec-tunnel.d.ts.map +1 -0
- package/dist/backend-msb/exec-tunnel.js +163 -0
- package/dist/backend-msb/exec-tunnel.js.map +1 -0
- package/dist/backend-msb/follow-replay.d.ts +14 -0
- package/dist/backend-msb/follow-replay.d.ts.map +1 -0
- package/dist/backend-msb/follow-replay.js +20 -0
- package/dist/backend-msb/follow-replay.js.map +1 -0
- package/dist/backend-msb/image-cache.d.ts +25 -0
- package/dist/backend-msb/image-cache.d.ts.map +1 -0
- package/dist/backend-msb/image-cache.js +27 -0
- package/dist/backend-msb/image-cache.js.map +1 -0
- package/dist/backend-msb/index.d.ts +6 -0
- package/dist/backend-msb/index.d.ts.map +1 -0
- package/dist/backend-msb/index.js +21 -0
- package/dist/backend-msb/index.js.map +1 -0
- package/dist/backend-msb/invoke.d.ts +24 -0
- package/dist/backend-msb/invoke.d.ts.map +1 -0
- package/dist/backend-msb/invoke.js +73 -0
- package/dist/backend-msb/invoke.js.map +1 -0
- package/dist/backend-msb/ls-json.d.ts +23 -0
- package/dist/backend-msb/ls-json.d.ts.map +1 -0
- package/dist/backend-msb/ls-json.js +113 -0
- package/dist/backend-msb/ls-json.js.map +1 -0
- package/dist/backend-msb/network-links.d.ts +7 -0
- package/dist/backend-msb/network-links.d.ts.map +1 -0
- package/dist/backend-msb/network-links.js +31 -0
- package/dist/backend-msb/network-links.js.map +1 -0
- package/dist/backend-msb/platform.d.ts +29 -0
- package/dist/backend-msb/platform.d.ts.map +1 -0
- package/dist/backend-msb/platform.js +128 -0
- package/dist/backend-msb/platform.js.map +1 -0
- package/dist/backend-msb/port-conflict.d.ts +10 -0
- package/dist/backend-msb/port-conflict.d.ts.map +1 -0
- package/dist/backend-msb/port-conflict.js +16 -0
- package/dist/backend-msb/port-conflict.js.map +1 -0
- package/dist/backend-msb/provider.d.ts +10 -0
- package/dist/backend-msb/provider.d.ts.map +1 -0
- package/dist/backend-msb/provider.js +40 -0
- package/dist/backend-msb/provider.js.map +1 -0
- package/dist/backend-msb/provisioner.d.ts +19 -0
- package/dist/backend-msb/provisioner.d.ts.map +1 -0
- package/dist/backend-msb/provisioner.js +332 -0
- package/dist/backend-msb/provisioner.js.map +1 -0
- package/dist/backend-msb/reaper.d.ts +10 -0
- package/dist/backend-msb/reaper.d.ts.map +1 -0
- package/dist/backend-msb/reaper.js +73 -0
- package/dist/backend-msb/reaper.js.map +1 -0
- package/dist/core/backend.d.ts +98 -0
- package/dist/core/backend.d.ts.map +1 -0
- package/dist/core/backend.js +2 -0
- package/dist/core/backend.js.map +1 -0
- package/dist/core/backends.d.ts +37 -0
- package/dist/core/backends.d.ts.map +1 -0
- package/dist/core/backends.js +127 -0
- package/dist/core/backends.js.map +1 -0
- package/dist/core/cleanup.d.ts +37 -0
- package/dist/core/cleanup.d.ts.map +1 -0
- package/dist/core/cleanup.js +86 -0
- package/dist/core/cleanup.js.map +1 -0
- package/dist/core/errors.d.ts +51 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +66 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/free-ports.d.ts +23 -0
- package/dist/core/free-ports.d.ts.map +1 -0
- package/dist/core/free-ports.js +63 -0
- package/dist/core/free-ports.js.map +1 -0
- package/dist/core/generic-container.d.ts +145 -0
- package/dist/core/generic-container.d.ts.map +1 -0
- package/dist/core/generic-container.js +385 -0
- package/dist/core/generic-container.js.map +1 -0
- package/dist/core/model.d.ts +60 -0
- package/dist/core/model.d.ts.map +1 -0
- package/dist/core/model.js +2 -0
- package/dist/core/model.js.map +1 -0
- package/dist/core/mountable-file.d.ts +25 -0
- package/dist/core/mountable-file.d.ts.map +1 -0
- package/dist/core/mountable-file.js +64 -0
- package/dist/core/mountable-file.js.map +1 -0
- package/dist/core/network.d.ts +58 -0
- package/dist/core/network.d.ts.map +1 -0
- package/dist/core/network.js +84 -0
- package/dist/core/network.js.map +1 -0
- package/dist/core/run-id.d.ts +18 -0
- package/dist/core/run-id.d.ts.map +1 -0
- package/dist/core/run-id.js +18 -0
- package/dist/core/run-id.js.map +1 -0
- package/dist/core/wait.d.ts +59 -0
- package/dist/core/wait.d.ts.map +1 -0
- package/dist/core/wait.js +239 -0
- package/dist/core/wait.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/arango.d.ts +14 -0
- package/dist/modules/arango.d.ts.map +1 -0
- package/dist/modules/arango.js +37 -0
- package/dist/modules/arango.js.map +1 -0
- package/dist/modules/clickhouse.d.ts +37 -0
- package/dist/modules/clickhouse.d.ts.map +1 -0
- package/dist/modules/clickhouse.js +69 -0
- package/dist/modules/clickhouse.js.map +1 -0
- package/dist/modules/flink.d.ts +65 -0
- package/dist/modules/flink.d.ts.map +1 -0
- package/dist/modules/flink.js +125 -0
- package/dist/modules/flink.js.map +1 -0
- package/dist/modules/floci.d.ts +46 -0
- package/dist/modules/floci.d.ts.map +1 -0
- package/dist/modules/floci.js +62 -0
- package/dist/modules/floci.js.map +1 -0
- package/dist/modules/index.d.ts +31 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +31 -0
- package/dist/modules/index.js.map +1 -0
- package/dist/modules/kafka.d.ts +11 -0
- package/dist/modules/kafka.d.ts.map +1 -0
- package/dist/modules/kafka.js +41 -0
- package/dist/modules/kafka.js.map +1 -0
- package/dist/modules/keycloak.d.ts +40 -0
- package/dist/modules/keycloak.d.ts.map +1 -0
- package/dist/modules/keycloak.js +67 -0
- package/dist/modules/keycloak.js.map +1 -0
- package/dist/modules/mariadb.d.ts +56 -0
- package/dist/modules/mariadb.d.ts.map +1 -0
- package/dist/modules/mariadb.js +89 -0
- package/dist/modules/mariadb.js.map +1 -0
- package/dist/modules/memcached.d.ts +24 -0
- package/dist/modules/memcached.d.ts.map +1 -0
- package/dist/modules/memcached.js +105 -0
- package/dist/modules/memcached.js.map +1 -0
- package/dist/modules/mongodb.d.ts +19 -0
- package/dist/modules/mongodb.d.ts.map +1 -0
- package/dist/modules/mongodb.js +69 -0
- package/dist/modules/mongodb.js.map +1 -0
- package/dist/modules/mysql.d.ts +61 -0
- package/dist/modules/mysql.d.ts.map +1 -0
- package/dist/modules/mysql.js +101 -0
- package/dist/modules/mysql.js.map +1 -0
- package/dist/modules/neo4j.d.ts +60 -0
- package/dist/modules/neo4j.d.ts.map +1 -0
- package/dist/modules/neo4j.js +83 -0
- package/dist/modules/neo4j.js.map +1 -0
- package/dist/modules/pinot.d.ts +30 -0
- package/dist/modules/pinot.d.ts.map +1 -0
- package/dist/modules/pinot.js +49 -0
- package/dist/modules/pinot.js.map +1 -0
- package/dist/modules/postgres.d.ts +29 -0
- package/dist/modules/postgres.d.ts.map +1 -0
- package/dist/modules/postgres.js +73 -0
- package/dist/modules/postgres.js.map +1 -0
- package/dist/modules/rabbitmq.d.ts +46 -0
- package/dist/modules/rabbitmq.d.ts.map +1 -0
- package/dist/modules/rabbitmq.js +68 -0
- package/dist/modules/rabbitmq.js.map +1 -0
- package/dist/modules/redis.d.ts +15 -0
- package/dist/modules/redis.d.ts.map +1 -0
- package/dist/modules/redis.js +24 -0
- package/dist/modules/redis.js.map +1 -0
- package/dist/modules/redpanda.d.ts +27 -0
- package/dist/modules/redpanda.d.ts.map +1 -0
- package/dist/modules/redpanda.js +58 -0
- package/dist/modules/redpanda.js.map +1 -0
- package/dist/modules/spring-cloud-config.d.ts +18 -0
- package/dist/modules/spring-cloud-config.d.ts.map +1 -0
- package/dist/modules/spring-cloud-config.js +28 -0
- package/dist/modules/spring-cloud-config.js.map +1 -0
- package/dist/modules/wiremock.d.ts +21 -0
- package/dist/modules/wiremock.d.ts.map +1 -0
- package/dist/modules/wiremock.js +32 -0
- package/dist/modules/wiremock.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { FreePorts } from "./free-ports.js";
|
|
2
|
+
import { RunId } from "./run-id.js";
|
|
3
|
+
import { Network } from "./network.js";
|
|
4
|
+
import { PortBindConflictError } from "./errors.js";
|
|
5
|
+
import { Wait } from "./wait.js";
|
|
6
|
+
import { registerSyncCleanup, unregisterSyncCleanup } from "./cleanup.js";
|
|
7
|
+
import { Backends } from "./backends.js";
|
|
8
|
+
const MAX_START_ATTEMPTS = 5;
|
|
9
|
+
let sequence = 0;
|
|
10
|
+
function nextSequence() {
|
|
11
|
+
sequence += 1;
|
|
12
|
+
return sequence;
|
|
13
|
+
}
|
|
14
|
+
/** Typed-first, string-fallback classification: walk the cause chain for a typed conflict, else match the daemon's own wording. */
|
|
15
|
+
function isPortBindConflict(err) {
|
|
16
|
+
let current = err;
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
while (current !== undefined && current !== null && !seen.has(current)) {
|
|
19
|
+
seen.add(current);
|
|
20
|
+
if (current instanceof PortBindConflictError) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
if (current instanceof Error) {
|
|
24
|
+
const message = current.message.toLowerCase();
|
|
25
|
+
if (message.includes("address already in use") || message.includes("already allocated")) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
current = current.cause;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
async function swallow(fn) {
|
|
36
|
+
try {
|
|
37
|
+
await fn();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Best-effort teardown: never let a stop/remove failure escape.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The builder, launcher, and lifecycle guard for a single container — the
|
|
45
|
+
* one class every module (`RedisContainer`, `PostgresContainer`, …)
|
|
46
|
+
* subclasses, and the type you reach for directly for any image without a
|
|
47
|
+
* dedicated module.
|
|
48
|
+
*
|
|
49
|
+
* The instance itself is the guard: after `start()` it implements
|
|
50
|
+
* `Symbol.asyncDispose`, so `await using c = await new GenericContainer(img).start()`
|
|
51
|
+
* tears the container down at scope exit — the direct analog of the Kotlin
|
|
52
|
+
* `AutoCloseable` and Rust's RAII guard, expressed with TC39 explicit
|
|
53
|
+
* resource management. Prefer `await using` in tests; use explicit
|
|
54
|
+
* `start()`/`stop()` where a `using` scope can't span a framework hook
|
|
55
|
+
* (Jest/Vitest `beforeAll`/`afterAll`, `node:test`'s `before`/`after`).
|
|
56
|
+
*
|
|
57
|
+
* Builder methods (`withEnv`, `withExposedPorts`, …) mutate and return
|
|
58
|
+
* `this` for chaining; none of them perform I/O. All I/O happens in
|
|
59
|
+
* `start()`, `stop()`, `exec()`, `logs()`, and `followOutput()`.
|
|
60
|
+
*/
|
|
61
|
+
export class GenericContainer {
|
|
62
|
+
image;
|
|
63
|
+
envPairs = [];
|
|
64
|
+
exposedPorts = [];
|
|
65
|
+
command;
|
|
66
|
+
network;
|
|
67
|
+
aliasNames = [];
|
|
68
|
+
mounts = [];
|
|
69
|
+
waitStrategy = Wait.forListeningPort();
|
|
70
|
+
memoryLimitMb;
|
|
71
|
+
backendOverride;
|
|
72
|
+
handle;
|
|
73
|
+
backend;
|
|
74
|
+
mappedPorts = new Map();
|
|
75
|
+
running = false;
|
|
76
|
+
/** Builds against `image` (e.g. `"redis:8.6-alpine"`); no I/O happens until `start()`. */
|
|
77
|
+
constructor(image) {
|
|
78
|
+
this.image = image;
|
|
79
|
+
}
|
|
80
|
+
/** Convenience: `new GenericContainer(image).start()`. */
|
|
81
|
+
static async start(image) {
|
|
82
|
+
return new GenericContainer(image).start();
|
|
83
|
+
}
|
|
84
|
+
/** Sets an environment variable visible to the workload. Last-write-wins if called again with the same key; insertion order is otherwise preserved. */
|
|
85
|
+
withEnv(key, value) {
|
|
86
|
+
// Last-write-wins, insertion-ordered: drop any prior entry for this key
|
|
87
|
+
// before pushing, so re-setting the same key doesn't duplicate it while
|
|
88
|
+
// still preserving the position of first-set for untouched keys.
|
|
89
|
+
this.envPairs = this.envPairs.filter(([k]) => k !== key);
|
|
90
|
+
this.envPairs.push([key, value]);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
/** Publishes these guest ports to pre-allocated host ports (see `FreePorts`); read back with `getMappedPort`. */
|
|
94
|
+
withExposedPorts(...ports) {
|
|
95
|
+
this.exposedPorts.push(...ports);
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
/** Overrides the image's default ENTRYPOINT/CMD. Omit this call entirely to run the image unmodified. */
|
|
99
|
+
withCommand(...cmd) {
|
|
100
|
+
this.command = cmd;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
/** Joins a `Network`, making this container reachable from (and to) its running siblings by alias. */
|
|
104
|
+
withNetwork(net) {
|
|
105
|
+
this.network = net;
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
/** Names this container answers to on its network — resolved via `Network.resolve(alias, port)` by other members. */
|
|
109
|
+
withNetworkAliases(...names) {
|
|
110
|
+
this.aliasNames.push(...names);
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
/** Copies a host file into the guest before boot, at `guestPath`. Read-only is enforced on docker; advisory only on msb (current microsandbox releases do not enforce guest-side read-only mounts). */
|
|
114
|
+
withCopyFileToContainer(file, guestPath) {
|
|
115
|
+
this.mounts.push({ hostPath: file.path, guestPath, readOnly: true });
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
/** Sets the readiness check `start()` waits on before returning. Defaults to `Wait.forListeningPort()`. */
|
|
119
|
+
waitingFor(strategy) {
|
|
120
|
+
this.waitStrategy = strategy;
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
/** Raises the container's memory ceiling (in MB) above the backend default — required by several JVM-heavy modules; see each module's own doc for its pin. */
|
|
124
|
+
withMemoryLimit(megabytes) {
|
|
125
|
+
this.memoryLimitMb = megabytes;
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
/** Test/advanced seam: pin the backend instead of resolving `Backends.active()`. */
|
|
129
|
+
withBackend(backend) {
|
|
130
|
+
this.backendOverride = backend;
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
/** Module hook: override to adjust the spec once mapped host ports are known (e.g. advertised listeners). */
|
|
134
|
+
customizeSpec(spec, _mapped) {
|
|
135
|
+
return spec;
|
|
136
|
+
}
|
|
137
|
+
/** Module hook: runs after the container is ready (e.g. `rs.initiate` for a Mongo replica set). */
|
|
138
|
+
containerIsStarted() {
|
|
139
|
+
return Promise.resolve();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Module hook: runs the instant `start()` has resolved a backend — before
|
|
143
|
+
* `ensureNetwork`, port allocation, or the container is created/booted at
|
|
144
|
+
* all. The one hook that lets a module reject a backend-incompatible
|
|
145
|
+
* request (e.g. `FlinkContainer.withTaskManager()` on msb) without paying
|
|
146
|
+
* for a boot first. `containerIsStarted()` (above) fires much later, after
|
|
147
|
+
* this container is already fully up — fine for post-readiness setup, but
|
|
148
|
+
* too late to fail fast on a capability the backend never had.
|
|
149
|
+
*/
|
|
150
|
+
containerIsStarting(_backend) {
|
|
151
|
+
return Promise.resolve();
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Module hook: the backend this container actually started against (only
|
|
155
|
+
* valid once `start()` has resolved it — i.e. from within
|
|
156
|
+
* `containerIsStarted()` or later). Lets a module's post-start hook branch
|
|
157
|
+
* on backend capability, e.g. `FlinkContainer.withTaskManager()` throwing a
|
|
158
|
+
* typed `UnsupportedByBackendError` on msb instead of attempting a
|
|
159
|
+
* network-link topology the exec-tunnel emulation can't carry.
|
|
160
|
+
*/
|
|
161
|
+
currentBackend() {
|
|
162
|
+
return this.requireHandle().backend;
|
|
163
|
+
}
|
|
164
|
+
resolveBackend() {
|
|
165
|
+
return this.backendOverride ?? Backends.active();
|
|
166
|
+
}
|
|
167
|
+
buildSpec(name, ports) {
|
|
168
|
+
const spec = {
|
|
169
|
+
name,
|
|
170
|
+
image: this.image,
|
|
171
|
+
env: this.envPairs.map(([k, v]) => [k, v]),
|
|
172
|
+
command: this.command,
|
|
173
|
+
ports: this.exposedPorts.map((guestPort) => ({
|
|
174
|
+
hostPort: ports.get(guestPort) ?? (() => {
|
|
175
|
+
throw new Error(`no allocated host port for guest port ${guestPort}`);
|
|
176
|
+
})(),
|
|
177
|
+
guestPort,
|
|
178
|
+
})),
|
|
179
|
+
mounts: this.mounts,
|
|
180
|
+
networkId: this.network?.id,
|
|
181
|
+
aliases: this.aliasNames,
|
|
182
|
+
runId: RunId.value,
|
|
183
|
+
memoryLimitMb: this.memoryLimitMb,
|
|
184
|
+
};
|
|
185
|
+
return this.customizeSpec(spec, (guest) => {
|
|
186
|
+
const p = ports.get(guest);
|
|
187
|
+
if (p === undefined) {
|
|
188
|
+
throw new Error(`no allocated host port for guest port ${guest}`);
|
|
189
|
+
}
|
|
190
|
+
return p;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async allocatePorts() {
|
|
194
|
+
const ports = new Map();
|
|
195
|
+
for (const guestPort of this.exposedPorts) {
|
|
196
|
+
ports.set(guestPort, await FreePorts.allocate());
|
|
197
|
+
}
|
|
198
|
+
return ports;
|
|
199
|
+
}
|
|
200
|
+
releasePorts(ports) {
|
|
201
|
+
for (const hostPort of ports.values()) {
|
|
202
|
+
FreePorts.release(hostPort);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Resolves a backend, allocates host ports, boots the container, installs
|
|
207
|
+
* any network links, and waits for readiness — in that order. On ANY
|
|
208
|
+
* failure partway through (a bad wait strategy, a network-link rejection,
|
|
209
|
+
* a port conflict exhausting its retries), everything already allocated
|
|
210
|
+
* or started is torn down to completion BEFORE this rejects: a
|
|
211
|
+
* half-started container never leaks, and there is no detached
|
|
212
|
+
* fire-and-forget cleanup running after `start()` has already returned
|
|
213
|
+
* control to the caller.
|
|
214
|
+
*
|
|
215
|
+
* A host-port-bind conflict specifically retries (up to 5 attempts) with
|
|
216
|
+
* a fresh port before giving up — pre-allocated ports can still lose an
|
|
217
|
+
* allocate-then-bind race to an unrelated process.
|
|
218
|
+
*/
|
|
219
|
+
async start() {
|
|
220
|
+
const backend = this.resolveBackend();
|
|
221
|
+
await this.containerIsStarting(backend);
|
|
222
|
+
if (this.network !== undefined) {
|
|
223
|
+
await backend.ensureNetwork(this.network.id);
|
|
224
|
+
}
|
|
225
|
+
let handle;
|
|
226
|
+
let allocatedPorts;
|
|
227
|
+
let lastConflict;
|
|
228
|
+
for (let attempt = 0; attempt < MAX_START_ATTEMPTS; attempt++) {
|
|
229
|
+
const ports = await this.allocatePorts();
|
|
230
|
+
const name = `rz-${RunId.value}-${nextSequence()}`;
|
|
231
|
+
const spec = this.buildSpec(name, ports);
|
|
232
|
+
let createdHandle;
|
|
233
|
+
try {
|
|
234
|
+
createdHandle = await backend.create(spec);
|
|
235
|
+
await backend.start(createdHandle);
|
|
236
|
+
handle = createdHandle;
|
|
237
|
+
allocatedPorts = ports;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
if (createdHandle !== undefined) {
|
|
242
|
+
await swallow(() => backend.stop(createdHandle));
|
|
243
|
+
await swallow(() => backend.remove(createdHandle));
|
|
244
|
+
}
|
|
245
|
+
this.releasePorts(ports);
|
|
246
|
+
if (isPortBindConflict(err)) {
|
|
247
|
+
lastConflict = err;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
throw err;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (handle === undefined || allocatedPorts === undefined) {
|
|
254
|
+
throw new Error(`Failed to start '${this.image}' after ${MAX_START_ATTEMPTS} attempts: every attempt hit a host port already in use by another process.`, { cause: lastConflict });
|
|
255
|
+
}
|
|
256
|
+
// Assign guard fields: getMappedPort/exec/logs become valid from here,
|
|
257
|
+
// even though readiness (below) hasn't been confirmed yet.
|
|
258
|
+
this.handle = handle;
|
|
259
|
+
this.backend = backend;
|
|
260
|
+
this.mappedPorts = allocatedPorts;
|
|
261
|
+
this.running = true;
|
|
262
|
+
// Real synchronous teardown, not a placeholder: if this process dies
|
|
263
|
+
// before stop() ever runs (process.exit / SIGINT / SIGTERM), the exit
|
|
264
|
+
// hook in cleanup.ts calls straight into the backend's own blocking
|
|
265
|
+
// cleanupSync — the same mechanism a crash-recovery reaper would find
|
|
266
|
+
// necessary, just invoked proactively instead of on the next process's
|
|
267
|
+
// startup sweep.
|
|
268
|
+
registerSyncCleanup(handle.id, () => backend.cleanupSync(handle.id));
|
|
269
|
+
try {
|
|
270
|
+
const links = this.network?.linksForNewMember() ?? [];
|
|
271
|
+
await backend.installNetworkLinks(handle, links);
|
|
272
|
+
// Register AFTER links are computed/installed — a container must
|
|
273
|
+
// never see itself in its own linksForNewMember() call.
|
|
274
|
+
if (this.network !== undefined) {
|
|
275
|
+
this.network.register(this, this.aliasNames, backend);
|
|
276
|
+
}
|
|
277
|
+
await this.waitStrategy.waitUntilReady(this.asWaitTarget());
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
// A half-started container never leaks regardless of which step
|
|
281
|
+
// failed: tear down to completion before this rejection surfaces, so
|
|
282
|
+
// start() never returns until cleanup has actually finished.
|
|
283
|
+
await this.stop();
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
await this.containerIsStarted();
|
|
287
|
+
return this;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Stops and removes the backend-native resource, releases its host ports,
|
|
291
|
+
* and clears the port map. Idempotent — stopping a container that never
|
|
292
|
+
* started, or stopping twice, is a harmless no-op the second time. Errors
|
|
293
|
+
* from the backend's own stop/remove calls are swallowed (best-effort
|
|
294
|
+
* teardown); this method itself never throws.
|
|
295
|
+
*/
|
|
296
|
+
async stop() {
|
|
297
|
+
if (!this.running || this.handle === undefined || this.backend === undefined) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const handle = this.handle;
|
|
301
|
+
const backend = this.backend;
|
|
302
|
+
this.running = false;
|
|
303
|
+
await swallow(() => backend.stop(handle));
|
|
304
|
+
await swallow(() => backend.remove(handle));
|
|
305
|
+
unregisterSyncCleanup(handle.id);
|
|
306
|
+
this.releasePorts(this.mappedPorts);
|
|
307
|
+
this.mappedPorts = new Map();
|
|
308
|
+
}
|
|
309
|
+
/** `= stop()`. What `await using c = await new GenericContainer(img).start()` calls at scope exit; never throws. */
|
|
310
|
+
async [Symbol.asyncDispose]() {
|
|
311
|
+
await this.stop();
|
|
312
|
+
}
|
|
313
|
+
/** Whether `start()` has completed and `stop()` has not yet run. */
|
|
314
|
+
get isRunning() {
|
|
315
|
+
return this.running;
|
|
316
|
+
}
|
|
317
|
+
/** Always `"127.0.0.1"` — every published port is bound loopback-only, on both backends. */
|
|
318
|
+
get host() {
|
|
319
|
+
return "127.0.0.1";
|
|
320
|
+
}
|
|
321
|
+
/** Names this container answers to on its network (set via `withNetworkAliases`). Part of the `NetworkMember` contract `Network` uses to compute links. */
|
|
322
|
+
get aliases() {
|
|
323
|
+
return this.aliasNames;
|
|
324
|
+
}
|
|
325
|
+
/** Every guest port this container published (set via `withExposedPorts`). Part of the `NetworkMember` contract `Network` uses to compute links. */
|
|
326
|
+
get exposedGuestPorts() {
|
|
327
|
+
return this.exposedPorts;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The host port bound to `guestPort`. Throws distinct messages for the
|
|
331
|
+
* two ways this can be wrong: not running at all ("call start() first"),
|
|
332
|
+
* versus running but never exposed via `withExposedPorts` ("call
|
|
333
|
+
* withExposedPorts(N)").
|
|
334
|
+
*/
|
|
335
|
+
getMappedPort(guestPort) {
|
|
336
|
+
if (!this.running) {
|
|
337
|
+
throw new Error(`Container is not running — call start() first.`);
|
|
338
|
+
}
|
|
339
|
+
const port = this.mappedPorts.get(guestPort);
|
|
340
|
+
if (port === undefined) {
|
|
341
|
+
throw new Error(`Port ${guestPort} is not exposed — call withExposedPorts(${guestPort}).`);
|
|
342
|
+
}
|
|
343
|
+
return port;
|
|
344
|
+
}
|
|
345
|
+
/** Alias for `getMappedPort`, satisfying the `NetworkMember`/`WaitTarget` shape (which name this method `mappedPort`). */
|
|
346
|
+
mappedPort(guestPort) {
|
|
347
|
+
return this.getMappedPort(guestPort);
|
|
348
|
+
}
|
|
349
|
+
requireHandle() {
|
|
350
|
+
if (!this.running || this.handle === undefined || this.backend === undefined) {
|
|
351
|
+
throw new Error(`Container is not running — call start() first.`);
|
|
352
|
+
}
|
|
353
|
+
return { handle: this.handle, backend: this.backend };
|
|
354
|
+
}
|
|
355
|
+
/** Runs a one-shot command inside the running container and waits for it to exit. Throws if the container is not running. */
|
|
356
|
+
async exec(...cmd) {
|
|
357
|
+
const { handle, backend } = this.requireHandle();
|
|
358
|
+
return backend.exec(handle, cmd);
|
|
359
|
+
}
|
|
360
|
+
/** Fetches the workload's logs so far (a bounded tail), as a single string. Throws if the container is not running. */
|
|
361
|
+
async logs() {
|
|
362
|
+
const { handle, backend } = this.requireHandle();
|
|
363
|
+
return backend.logs(handle);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Streams log lines to `consumer` as they're produced, in order, with no
|
|
367
|
+
* duplicates — including the final unterminated fragment after the
|
|
368
|
+
* workload exits, delivered exactly once. Call `close()` on the returned
|
|
369
|
+
* handle to stop delivery without flushing anything further.
|
|
370
|
+
*/
|
|
371
|
+
async followOutput(consumer) {
|
|
372
|
+
const { handle, backend } = this.requireHandle();
|
|
373
|
+
return backend.followLogs(handle, consumer);
|
|
374
|
+
}
|
|
375
|
+
asWaitTarget() {
|
|
376
|
+
return {
|
|
377
|
+
host: this.host,
|
|
378
|
+
mappedPort: (guestPort) => this.getMappedPort(guestPort),
|
|
379
|
+
exposedGuestPorts: this.exposedPorts,
|
|
380
|
+
currentLogs: () => this.logs(),
|
|
381
|
+
describe: () => `container '${this.handle?.spec.name ?? this.image}' (image ${this.image})`,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
//# sourceMappingURL=generic-container.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generic-container.js","sourceRoot":"","sources":["../../src/core/generic-container.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** A published container port: a host port already chosen, mapped to the port the workload listens on inside the guest. */
|
|
2
|
+
export interface PortBinding {
|
|
3
|
+
/** The host-side port, pre-allocated by `FreePorts`. */
|
|
4
|
+
readonly hostPort: number;
|
|
5
|
+
/** The port the workload listens on inside the guest. */
|
|
6
|
+
readonly guestPort: number;
|
|
7
|
+
}
|
|
8
|
+
/** A host path copied into the guest before boot. `readOnly` defaults to `true` via the builder (`withCopyFileToContainer`). */
|
|
9
|
+
export interface FileMount {
|
|
10
|
+
/** Absolute host path to copy from. */
|
|
11
|
+
readonly hostPath: string;
|
|
12
|
+
/** Destination path inside the guest. */
|
|
13
|
+
readonly guestPath: string;
|
|
14
|
+
/** Enforced on docker; advisory only on msb (current microsandbox releases do not enforce guest-side read-only mounts). */
|
|
15
|
+
readonly readOnly: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** The result of a one-shot `exec` inside a running container. */
|
|
18
|
+
export interface ExecResult {
|
|
19
|
+
/** The command's process exit code. */
|
|
20
|
+
readonly exitCode: number;
|
|
21
|
+
/** Everything the command wrote to stdout. */
|
|
22
|
+
readonly stdout: string;
|
|
23
|
+
/** Everything the command wrote to stderr. */
|
|
24
|
+
readonly stderr: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The immutable, backend-agnostic description of a container to launch.
|
|
28
|
+
* Built by `GenericContainer` from its builder calls and handed to
|
|
29
|
+
* whichever `SandboxBackend` is active — a spec never changes once built,
|
|
30
|
+
* and building one performs no I/O.
|
|
31
|
+
*/
|
|
32
|
+
export interface ContainerSpec {
|
|
33
|
+
/** The container's name, `rz-<runId>-<seq>`. */
|
|
34
|
+
readonly name: string;
|
|
35
|
+
/** The image reference, e.g. `"redis:8.6-alpine"`. */
|
|
36
|
+
readonly image: string;
|
|
37
|
+
/**
|
|
38
|
+
* Array of pairs, not a Map: insertion-ordered, with last-write-wins
|
|
39
|
+
* deduping when a builder overwrites a key.
|
|
40
|
+
*/
|
|
41
|
+
readonly env: ReadonlyArray<readonly [string, string]>;
|
|
42
|
+
/** `undefined` means the image's own ENTRYPOINT/CMD runs unmodified. */
|
|
43
|
+
readonly command: ReadonlyArray<string> | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Already-chosen host ports — the core invariant of this library: a
|
|
46
|
+
* backend binds these, it never allocates its own. See `FreePorts`.
|
|
47
|
+
*/
|
|
48
|
+
readonly ports: ReadonlyArray<PortBinding>;
|
|
49
|
+
/** Host files/directories to copy into the guest before boot. */
|
|
50
|
+
readonly mounts: ReadonlyArray<FileMount>;
|
|
51
|
+
/** The `Network.id` this container joins, if any. */
|
|
52
|
+
readonly networkId: string | undefined;
|
|
53
|
+
/** Names this container answers to on its network. */
|
|
54
|
+
readonly aliases: ReadonlyArray<string>;
|
|
55
|
+
/** The process-wide `RunId.value` that named this container. */
|
|
56
|
+
readonly runId: string;
|
|
57
|
+
/** An explicit memory ceiling in MB, if the module or caller set one via `withMemoryLimit`. */
|
|
58
|
+
readonly memoryLimitMb: number | undefined;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/core/model.ts"],"names":[],"mappings":"AAAA,2HAA2H;AAC3H,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,gIAAgI;AAChI,MAAM,WAAW,SAAS;IACxB,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2HAA2H;IAC3H,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IACpD;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC3C,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAC1C,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+FAA+F;IAC/F,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/core/model.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A file resolved to an absolute host path, ready to pass to
|
|
3
|
+
* `GenericContainer.withCopyFileToContainer`. `path` is always absolute —
|
|
4
|
+
* `forHostPath` resolves it immediately, and `forResource` copies to a
|
|
5
|
+
* cleaned-on-exit temp file first.
|
|
6
|
+
*/
|
|
7
|
+
export declare class MountableFile {
|
|
8
|
+
/** The resolved, absolute host path. */
|
|
9
|
+
readonly path: string;
|
|
10
|
+
private constructor();
|
|
11
|
+
/** Resolves a host filesystem path (relative to `process.cwd()` if not already absolute). */
|
|
12
|
+
static forHostPath(hostPath: string): MountableFile;
|
|
13
|
+
/**
|
|
14
|
+
* JS has no classpath and no cheap "caller's directory": `import.meta.url`
|
|
15
|
+
* only ever gives the DEFINING module's own directory, and recovering the
|
|
16
|
+
* true call site would need a fragile `Error().stack` parse that differs
|
|
17
|
+
* between Node and Bun. So the base is explicit rather than inferred:
|
|
18
|
+
* `relPath` resolves against `baseUrl` (typically the calling module's own
|
|
19
|
+
* `import.meta.url`) when given, and against `process.cwd()` otherwise.
|
|
20
|
+
* There is no classpath-relative resolution here — only an explicit
|
|
21
|
+
* base (an `import.meta.url` you pass in) or the process cwd.
|
|
22
|
+
*/
|
|
23
|
+
static forResource(relPath: string, baseUrl?: string): MountableFile;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=mountable-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mountable-file.d.ts","sourceRoot":"","sources":["../../src/core/mountable-file.ts"],"names":[],"mappings":"AAiCA;;;;;GAKG;AACH,qBAAa,aAAa;IAEtB,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM;IAFvB,OAAO;IAKP,6FAA6F;IAC7F,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa;IAInD;;;;;;;;;OASG;IACH,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa;CAKrE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { randomBytes } from "node:crypto";
|
|
6
|
+
const tempCopies = [];
|
|
7
|
+
let exitHookInstalled = false;
|
|
8
|
+
function installExitCleanup() {
|
|
9
|
+
if (exitHookInstalled) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
exitHookInstalled = true;
|
|
13
|
+
process.on("exit", () => {
|
|
14
|
+
for (const p of tempCopies) {
|
|
15
|
+
try {
|
|
16
|
+
fs.rmSync(p, { force: true });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// best-effort; the OS reclaims tmpdir eventually regardless
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function copyToTemp(sourcePath) {
|
|
25
|
+
const dest = path.join(os.tmpdir(), `rightsize-${randomBytes(6).toString("hex")}-${path.basename(sourcePath)}`);
|
|
26
|
+
fs.copyFileSync(sourcePath, dest);
|
|
27
|
+
tempCopies.push(dest);
|
|
28
|
+
installExitCleanup();
|
|
29
|
+
return dest;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A file resolved to an absolute host path, ready to pass to
|
|
33
|
+
* `GenericContainer.withCopyFileToContainer`. `path` is always absolute —
|
|
34
|
+
* `forHostPath` resolves it immediately, and `forResource` copies to a
|
|
35
|
+
* cleaned-on-exit temp file first.
|
|
36
|
+
*/
|
|
37
|
+
export class MountableFile {
|
|
38
|
+
path;
|
|
39
|
+
constructor(
|
|
40
|
+
/** The resolved, absolute host path. */
|
|
41
|
+
path) {
|
|
42
|
+
this.path = path;
|
|
43
|
+
}
|
|
44
|
+
/** Resolves a host filesystem path (relative to `process.cwd()` if not already absolute). */
|
|
45
|
+
static forHostPath(hostPath) {
|
|
46
|
+
return new MountableFile(path.resolve(hostPath));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* JS has no classpath and no cheap "caller's directory": `import.meta.url`
|
|
50
|
+
* only ever gives the DEFINING module's own directory, and recovering the
|
|
51
|
+
* true call site would need a fragile `Error().stack` parse that differs
|
|
52
|
+
* between Node and Bun. So the base is explicit rather than inferred:
|
|
53
|
+
* `relPath` resolves against `baseUrl` (typically the calling module's own
|
|
54
|
+
* `import.meta.url`) when given, and against `process.cwd()` otherwise.
|
|
55
|
+
* There is no classpath-relative resolution here — only an explicit
|
|
56
|
+
* base (an `import.meta.url` you pass in) or the process cwd.
|
|
57
|
+
*/
|
|
58
|
+
static forResource(relPath, baseUrl) {
|
|
59
|
+
const baseDir = baseUrl === undefined ? process.cwd() : path.dirname(fileURLToPath(baseUrl));
|
|
60
|
+
const resolved = path.resolve(baseDir, relPath);
|
|
61
|
+
return new MountableFile(copyToTemp(resolved));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=mountable-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mountable-file.js","sourceRoot":"","sources":["../../src/core/mountable-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,GAAa,EAAE,CAAC;AAChC,IAAI,iBAAiB,GAAG,KAAK,CAAC;AAE9B,SAAS,kBAAkB;IACzB,IAAI,iBAAiB,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IACD,iBAAiB,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,4DAA4D;YAC9D,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,UAAkB;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,aAAa,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAChH,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAClC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,kBAAkB,EAAE,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IAGb;IAFX;IACE,wCAAwC;IAC/B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IACpB,CAAC;IAEJ,6FAA6F;IAC7F,MAAM,CAAC,WAAW,CAAC,QAAgB;QACjC,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,WAAW,CAAC,OAAe,EAAE,OAAgB;QAClD,MAAM,OAAO,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7F,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,OAAO,IAAI,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,CAAC;CACF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { SandboxBackend, NetworkLink } from "./backend.js";
|
|
2
|
+
/**
|
|
3
|
+
* The subset of a running container `Network` needs in order to compute
|
|
4
|
+
* links for a newly-joining sibling: its aliases, its exposed guest ports
|
|
5
|
+
* mapped to host ports, and whether it is currently running (a stopped
|
|
6
|
+
* container never gets linked to).
|
|
7
|
+
*/
|
|
8
|
+
export interface NetworkMember {
|
|
9
|
+
/** Names this member answers to on the network. */
|
|
10
|
+
readonly aliases: ReadonlyArray<string>;
|
|
11
|
+
/** Whether this member is currently running (a stopped member is never linked to). */
|
|
12
|
+
readonly isRunning: boolean;
|
|
13
|
+
/** Guest ports this member has exposed. */
|
|
14
|
+
readonly exposedGuestPorts: ReadonlyArray<number>;
|
|
15
|
+
/** The host port bound to `guestPort`. */
|
|
16
|
+
mappedPort(guestPort: number): number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Alias-based connectivity between containers, on both backends. Docker
|
|
20
|
+
* implements it with a native bridge network; msb — where each container is
|
|
21
|
+
* a fully isolated microVM — emulates it with an `/etc/hosts` alias plus a
|
|
22
|
+
* TCP relay over the sandbox's exec channel. `Network.resolve` returns the
|
|
23
|
+
* same `"alias:port"` string either way, so code written against it doesn't
|
|
24
|
+
* need to know which backend is active.
|
|
25
|
+
*
|
|
26
|
+
* Implements `Symbol.asyncDispose`, so `await using net = Network.newNetwork()`
|
|
27
|
+
* tears the network down at scope exit alongside its member containers.
|
|
28
|
+
*/
|
|
29
|
+
export declare class Network implements AsyncDisposable {
|
|
30
|
+
/** This network's process-unique id, `rz-net-<8hex>`. */
|
|
31
|
+
readonly id: string;
|
|
32
|
+
private readonly aliasPorts;
|
|
33
|
+
private readonly members;
|
|
34
|
+
private lastBackend;
|
|
35
|
+
private constructor();
|
|
36
|
+
/** Creates a new network with a process-unique id (`rz-net-<8hex>`). Pass it to `withNetwork` on each member container. */
|
|
37
|
+
static newNetwork(): Network;
|
|
38
|
+
/** "alias:guestPort" for a registered (alias, guestPort) pair; throws naming the alias otherwise. */
|
|
39
|
+
resolve(alias: string, guestPort: number): string;
|
|
40
|
+
/**
|
|
41
|
+
* Links a newly-starting container needs installed toward every ALREADY
|
|
42
|
+
* RUNNING sibling, computed before `register` runs — so a container can
|
|
43
|
+
* never end up linked to itself, and only genuinely-running siblings
|
|
44
|
+
* (never a half-started or already-stopped one) contribute links.
|
|
45
|
+
*/
|
|
46
|
+
linksForNewMember(): ReadonlyArray<NetworkLink>;
|
|
47
|
+
/**
|
|
48
|
+
* Registers a container as a network member AFTER its links to existing
|
|
49
|
+
* siblings were computed and installed — never before, or the ordering
|
|
50
|
+
* that prevents self-links breaks.
|
|
51
|
+
*/
|
|
52
|
+
register(member: NetworkMember, aliases: ReadonlyArray<string>, backend: SandboxBackend): void;
|
|
53
|
+
/** Removes the backend-native network. Best-effort: safe to call even if no member ever started. */
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
/** `= close()`. What `await using net = Network.newNetwork()` calls at scope exit. */
|
|
56
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=network.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/core/network.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,sFAAsF;IACtF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAClD,0CAA0C;IAC1C,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CACvC;AAMD;;;;;;;;;;GAUG;AACH,qBAAa,OAAQ,YAAW,eAAe;IAC7C,yDAAyD;IACzD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,WAAW,CAA6B;IAEhD,OAAO;IAIP,2HAA2H;IAC3H,MAAM,CAAC,UAAU,IAAI,OAAO;IAI5B,qGAAqG;IACrG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAQjD;;;;;OAKG;IACH,iBAAiB,IAAI,aAAa,CAAC,WAAW,CAAC;IAe/C;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IAe9F,oGAAoG;IAC9F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,sFAAsF;IAChF,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7C"}
|