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,67 @@
|
|
|
1
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
2
|
+
import { Wait } from "../core/wait.js";
|
|
3
|
+
const HTTP_PORT = 8080;
|
|
4
|
+
const MANAGEMENT_PORT = 9000;
|
|
5
|
+
const STARTUP_TIMEOUT_MS = 120_000;
|
|
6
|
+
/**
|
|
7
|
+
* A single-node Keycloak container started with `start-dev` (an
|
|
8
|
+
* in-memory/dev-mode boot — no external database wiring needed for tests).
|
|
9
|
+
*
|
|
10
|
+
* Two pins here are version-sensitive and were verified against 26.0.8
|
|
11
|
+
* specifically:
|
|
12
|
+
*
|
|
13
|
+
* - **Env var names**: 26.x renamed the bootstrap-admin variables to
|
|
14
|
+
* `KC_BOOTSTRAP_ADMIN_USERNAME`/`KC_BOOTSTRAP_ADMIN_PASSWORD` (older
|
|
15
|
+
* releases used `KEYCLOAK_ADMIN`/`KEYCLOAK_ADMIN_PASSWORD`, which 26.x
|
|
16
|
+
* does not recognize). Confirmed by the boot log: `KC-SERVICES0077:
|
|
17
|
+
* Created temporary admin user with username admin`.
|
|
18
|
+
* - **Health lives on the management port, not the app port.** 26.x splits
|
|
19
|
+
* HTTP (8080) from a separate management interface (9000, confirmed by
|
|
20
|
+
* the boot log: "Management interface listening on http://0.0.0.0:9000")
|
|
21
|
+
* that serves `/health/ready`; `KC_HEALTH_ENABLED=true` is required for
|
|
22
|
+
* that endpoint to exist at all.
|
|
23
|
+
*
|
|
24
|
+
* A four-JVM-adjacent Quarkus boot (Keycloak on Quarkus, with Infinispan +
|
|
25
|
+
* an embedded H2) fits comfortably in `withMemoryLimit(1024)` (measured
|
|
26
|
+
* ~55% utilization at rest) — set by default here.
|
|
27
|
+
*/
|
|
28
|
+
export class KeycloakContainer extends GenericContainer {
|
|
29
|
+
adminUsernameState = "admin";
|
|
30
|
+
adminPasswordState = "admin";
|
|
31
|
+
constructor(image = "quay.io/keycloak/keycloak:26.0") {
|
|
32
|
+
super(image);
|
|
33
|
+
this.withExposedPorts(HTTP_PORT, MANAGEMENT_PORT)
|
|
34
|
+
.withCommand("start-dev")
|
|
35
|
+
.withEnv("KC_BOOTSTRAP_ADMIN_USERNAME", this.adminUsernameState)
|
|
36
|
+
.withEnv("KC_BOOTSTRAP_ADMIN_PASSWORD", this.adminPasswordState)
|
|
37
|
+
.withEnv("KC_HEALTH_ENABLED", "true")
|
|
38
|
+
.withMemoryLimit(1024)
|
|
39
|
+
.waitingFor(Wait.forHttp("/health/ready").forPort(MANAGEMENT_PORT).withStartupTimeout(STARTUP_TIMEOUT_MS));
|
|
40
|
+
}
|
|
41
|
+
static async start(image = "quay.io/keycloak/keycloak:26.0") {
|
|
42
|
+
return (await new KeycloakContainer(image).start());
|
|
43
|
+
}
|
|
44
|
+
/** Overrides `KC_BOOTSTRAP_ADMIN_USERNAME` (default `admin`). */
|
|
45
|
+
withAdminUsername(username) {
|
|
46
|
+
this.adminUsernameState = username;
|
|
47
|
+
return this.withEnv("KC_BOOTSTRAP_ADMIN_USERNAME", username);
|
|
48
|
+
}
|
|
49
|
+
/** Overrides `KC_BOOTSTRAP_ADMIN_PASSWORD` (default `admin`). */
|
|
50
|
+
withAdminPassword(password) {
|
|
51
|
+
this.adminPasswordState = password;
|
|
52
|
+
return this.withEnv("KC_BOOTSTRAP_ADMIN_PASSWORD", password);
|
|
53
|
+
}
|
|
54
|
+
/** The configured bootstrap admin username (default `admin`). */
|
|
55
|
+
get adminUsername() {
|
|
56
|
+
return this.adminUsernameState;
|
|
57
|
+
}
|
|
58
|
+
/** The configured bootstrap admin password (default `admin`). */
|
|
59
|
+
get adminPassword() {
|
|
60
|
+
return this.adminPasswordState;
|
|
61
|
+
}
|
|
62
|
+
/** The auth server's base URL (the app HTTP port, 8080). */
|
|
63
|
+
get authServerUrl() {
|
|
64
|
+
return `http://${this.host}:${this.getMappedPort(HTTP_PORT)}`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=keycloak.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keycloak.js","sourceRoot":"","sources":["../../src/modules/keycloak.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,MAAM,SAAS,GAAG,IAAI,CAAC;AACvB,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,iBAAkB,SAAQ,gBAAgB;IAC7C,kBAAkB,GAAG,OAAO,CAAC;IAC7B,kBAAkB,GAAG,OAAO,CAAC;IAErC,YAAY,KAAK,GAAG,gCAAgC;QAClD,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,eAAe,CAAC;aAC9C,WAAW,CAAC,WAAW,CAAC;aACxB,OAAO,CAAC,6BAA6B,EAAE,IAAI,CAAC,kBAAkB,CAAC;aAC/D,OAAO,CAAC,6BAA6B,EAAE,IAAI,CAAC,kBAAkB,CAAC;aAC/D,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC;aACpC,eAAe,CAAC,IAAI,CAAC;aACrB,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC/G,CAAC;IAED,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,gCAAgC;QAClE,OAAO,CAAC,MAAM,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAsB,CAAC;IAC3E,CAAC;IAED,iEAAiE;IACjE,iBAAiB,CAAC,QAAgB;QAChC,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;QACnC,OAAO,IAAI,CAAC,OAAO,CAAC,6BAA6B,EAAE,QAAQ,CAAC,CAAC;IAC/D,CAAC;IAED,iEAAiE;IACjE,iBAAiB,CAAC,QAAgB;QAChC,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;QACnC,OAAO,IAAI,CAAC,OAAO,CAAC,6BAA6B,EAAE,QAAQ,CAAC,CAAC;IAC/D,CAAC;IAED,iEAAiE;IACjE,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACjC,CAAC;IAED,iEAAiE;IACjE,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACjC,CAAC;IAED,4DAA4D;IAC5D,IAAI,aAAa;QACf,OAAO,UAAU,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;IAChE,CAAC;CACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
2
|
+
/**
|
|
3
|
+
* A single-node MariaDB container. Defaults to a `test`/`test`/`test`
|
|
4
|
+
* user/password/database trio (plus `MARIADB_ROOT_PASSWORD=test`), mirroring
|
|
5
|
+
* `MySQLContainer`'s builder shape; call `withUsername`/`withPassword`/
|
|
6
|
+
* `withDatabase` before `start()` to override any of them. MariaDB speaks
|
|
7
|
+
* the MySQL wire protocol, so `connectionString` uses the same `mysql://`
|
|
8
|
+
* scheme and any MySQL client (e.g. `mysql2`) works against it unmodified.
|
|
9
|
+
*
|
|
10
|
+
* ### Readiness — empirically pinned, following MySQL's precedent exactly
|
|
11
|
+
*
|
|
12
|
+
* Like MySQL 8.4, the official entrypoint boots mariadbd TWICE (a throwaway
|
|
13
|
+
* "temp server" for init scripts, then for real). The wait anchors on the
|
|
14
|
+
* real server's `Version:` line, which carries `port: 3306` and the
|
|
15
|
+
* `mariadb.org binary distribution` marker together, on the same line. The
|
|
16
|
+
* temp-server boot prints the same line shape but with `port: 0` (no port
|
|
17
|
+
* bound yet), so it can never false-match. Captured verbatim from a real
|
|
18
|
+
* `docker run mariadb:11.4` boot with this module's env:
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* ...
|
|
22
|
+
* [Note] mariadbd: ready for connections.
|
|
23
|
+
* Version: '11.4.12-MariaDB-ubu2404' socket: '...' port: 0 mariadb.org binary distribution
|
|
24
|
+
* ...(init scripts run, temp server shuts down)...
|
|
25
|
+
* [Note] Server socket created on IP: '0.0.0.0', port: '3306'.
|
|
26
|
+
* [Note] Server socket created on IP: '::', port: '3306'.
|
|
27
|
+
* [Note] mariadbd: ready for connections.
|
|
28
|
+
* Version: '11.4.12-MariaDB-ubu2404' socket: '...' port: 3306 mariadb.org binary distribution
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* `.*port: 3306.*mariadb\.org binary distribution.*` with `times=1` matches
|
|
32
|
+
* only the last line above — the temp server's equivalent line has `port: 0`
|
|
33
|
+
* instead of `port: 3306`, so it never satisfies the pattern.
|
|
34
|
+
*/
|
|
35
|
+
export declare class MariaDBContainer extends GenericContainer {
|
|
36
|
+
private usernameState;
|
|
37
|
+
private passwordState;
|
|
38
|
+
private databaseState;
|
|
39
|
+
constructor(image?: string);
|
|
40
|
+
static start(image?: string): Promise<MariaDBContainer>;
|
|
41
|
+
/** Overrides `MARIADB_USER` (default `test`). */
|
|
42
|
+
withUsername(username: string): this;
|
|
43
|
+
/** Overrides `MARIADB_PASSWORD` (default `test`). */
|
|
44
|
+
withPassword(password: string): this;
|
|
45
|
+
/** Overrides `MARIADB_DATABASE` (default `test`). */
|
|
46
|
+
withDatabase(database: string): this;
|
|
47
|
+
/** The configured database user (default `test`). */
|
|
48
|
+
get username(): string;
|
|
49
|
+
/** The configured database password (default `test`). */
|
|
50
|
+
get password(): string;
|
|
51
|
+
/** The configured database name (default `test`). */
|
|
52
|
+
get databaseName(): string;
|
|
53
|
+
/** A `mysql://` connection string (MariaDB speaks the MySQL wire protocol) for the running container's `databaseName`. */
|
|
54
|
+
get connectionString(): string;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=mariadb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mariadb.d.ts","sourceRoot":"","sources":["../../src/modules/mariadb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAKhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,gBAAiB,SAAQ,gBAAgB;IACpD,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,aAAa,CAAU;gBAEnB,KAAK,SAAiB;WAcZ,KAAK,CAAC,KAAK,SAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI9E,iDAAiD;IACjD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,qDAAqD;IACrD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,qDAAqD;IACrD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,qDAAqD;IACrD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,yDAAyD;IACzD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,qDAAqD;IACrD,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,0HAA0H;IAC1H,IAAI,gBAAgB,IAAI,MAAM,CAE7B;CACF"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
2
|
+
import { Wait } from "../core/wait.js";
|
|
3
|
+
const GUEST_PORT = 3306;
|
|
4
|
+
/**
|
|
5
|
+
* A single-node MariaDB container. Defaults to a `test`/`test`/`test`
|
|
6
|
+
* user/password/database trio (plus `MARIADB_ROOT_PASSWORD=test`), mirroring
|
|
7
|
+
* `MySQLContainer`'s builder shape; call `withUsername`/`withPassword`/
|
|
8
|
+
* `withDatabase` before `start()` to override any of them. MariaDB speaks
|
|
9
|
+
* the MySQL wire protocol, so `connectionString` uses the same `mysql://`
|
|
10
|
+
* scheme and any MySQL client (e.g. `mysql2`) works against it unmodified.
|
|
11
|
+
*
|
|
12
|
+
* ### Readiness — empirically pinned, following MySQL's precedent exactly
|
|
13
|
+
*
|
|
14
|
+
* Like MySQL 8.4, the official entrypoint boots mariadbd TWICE (a throwaway
|
|
15
|
+
* "temp server" for init scripts, then for real). The wait anchors on the
|
|
16
|
+
* real server's `Version:` line, which carries `port: 3306` and the
|
|
17
|
+
* `mariadb.org binary distribution` marker together, on the same line. The
|
|
18
|
+
* temp-server boot prints the same line shape but with `port: 0` (no port
|
|
19
|
+
* bound yet), so it can never false-match. Captured verbatim from a real
|
|
20
|
+
* `docker run mariadb:11.4` boot with this module's env:
|
|
21
|
+
*
|
|
22
|
+
* ```
|
|
23
|
+
* ...
|
|
24
|
+
* [Note] mariadbd: ready for connections.
|
|
25
|
+
* Version: '11.4.12-MariaDB-ubu2404' socket: '...' port: 0 mariadb.org binary distribution
|
|
26
|
+
* ...(init scripts run, temp server shuts down)...
|
|
27
|
+
* [Note] Server socket created on IP: '0.0.0.0', port: '3306'.
|
|
28
|
+
* [Note] Server socket created on IP: '::', port: '3306'.
|
|
29
|
+
* [Note] mariadbd: ready for connections.
|
|
30
|
+
* Version: '11.4.12-MariaDB-ubu2404' socket: '...' port: 3306 mariadb.org binary distribution
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* `.*port: 3306.*mariadb\.org binary distribution.*` with `times=1` matches
|
|
34
|
+
* only the last line above — the temp server's equivalent line has `port: 0`
|
|
35
|
+
* instead of `port: 3306`, so it never satisfies the pattern.
|
|
36
|
+
*/
|
|
37
|
+
export class MariaDBContainer extends GenericContainer {
|
|
38
|
+
usernameState = "test";
|
|
39
|
+
passwordState = "test";
|
|
40
|
+
databaseState = "test";
|
|
41
|
+
constructor(image = "mariadb:11.4") {
|
|
42
|
+
super(image);
|
|
43
|
+
this.withExposedPorts(GUEST_PORT)
|
|
44
|
+
.withEnv("MARIADB_USER", this.usernameState)
|
|
45
|
+
.withEnv("MARIADB_PASSWORD", this.passwordState)
|
|
46
|
+
.withEnv("MARIADB_DATABASE", this.databaseState)
|
|
47
|
+
.withEnv("MARIADB_ROOT_PASSWORD", "test")
|
|
48
|
+
// Anchored on the real server's line (see the class doc for the
|
|
49
|
+
// captured log excerpt).
|
|
50
|
+
.waitingFor(Wait.forLogMessage(".*port: 3306.*mariadb\\.org binary distribution.*", 1));
|
|
51
|
+
// No withMemoryLimit override, matching the MySQL precedent: MariaDB's
|
|
52
|
+
// InnoDB default footprint fits msb's default microVM RAM comfortably.
|
|
53
|
+
}
|
|
54
|
+
static async start(image = "mariadb:11.4") {
|
|
55
|
+
return (await new MariaDBContainer(image).start());
|
|
56
|
+
}
|
|
57
|
+
/** Overrides `MARIADB_USER` (default `test`). */
|
|
58
|
+
withUsername(username) {
|
|
59
|
+
this.usernameState = username;
|
|
60
|
+
return this.withEnv("MARIADB_USER", username);
|
|
61
|
+
}
|
|
62
|
+
/** Overrides `MARIADB_PASSWORD` (default `test`). */
|
|
63
|
+
withPassword(password) {
|
|
64
|
+
this.passwordState = password;
|
|
65
|
+
return this.withEnv("MARIADB_PASSWORD", password);
|
|
66
|
+
}
|
|
67
|
+
/** Overrides `MARIADB_DATABASE` (default `test`). */
|
|
68
|
+
withDatabase(database) {
|
|
69
|
+
this.databaseState = database;
|
|
70
|
+
return this.withEnv("MARIADB_DATABASE", database);
|
|
71
|
+
}
|
|
72
|
+
/** The configured database user (default `test`). */
|
|
73
|
+
get username() {
|
|
74
|
+
return this.usernameState;
|
|
75
|
+
}
|
|
76
|
+
/** The configured database password (default `test`). */
|
|
77
|
+
get password() {
|
|
78
|
+
return this.passwordState;
|
|
79
|
+
}
|
|
80
|
+
/** The configured database name (default `test`). */
|
|
81
|
+
get databaseName() {
|
|
82
|
+
return this.databaseState;
|
|
83
|
+
}
|
|
84
|
+
/** A `mysql://` connection string (MariaDB speaks the MySQL wire protocol) for the running container's `databaseName`. */
|
|
85
|
+
get connectionString() {
|
|
86
|
+
return `mysql://${this.usernameState}:${this.passwordState}@${this.host}:${this.getMappedPort(GUEST_PORT)}/${this.databaseState}`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=mariadb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mariadb.js","sourceRoot":"","sources":["../../src/modules/mariadb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,OAAO,gBAAiB,SAAQ,gBAAgB;IAC5C,aAAa,GAAG,MAAM,CAAC;IACvB,aAAa,GAAG,MAAM,CAAC;IACvB,aAAa,GAAG,MAAM,CAAC;IAE/B,YAAY,KAAK,GAAG,cAAc;QAChC,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC;aAC9B,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,aAAa,CAAC;aAC3C,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,aAAa,CAAC;aAC/C,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,aAAa,CAAC;aAC/C,OAAO,CAAC,uBAAuB,EAAE,MAAM,CAAC;YACzC,gEAAgE;YAChE,yBAAyB;aACxB,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,mDAAmD,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1F,uEAAuE;QACvE,uEAAuE;IACzE,CAAC;IAED,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,cAAc;QAChD,OAAO,CAAC,MAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAqB,CAAC;IACzE,CAAC;IAED,iDAAiD;IACjD,YAAY,CAAC,QAAgB;QAC3B,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,qDAAqD;IACrD,YAAY,CAAC,QAAgB;QAC3B,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC;IAED,qDAAqD;IACrD,YAAY,CAAC,QAAgB;QAC3B,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC;IAED,qDAAqD;IACrD,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,yDAAyD;IACzD,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,qDAAqD;IACrD,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,0HAA0H;IAC1H,IAAI,gBAAgB;QAClB,OAAO,WAAW,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;IACpI,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
2
|
+
import type { WaitStrategy, WaitTarget } from "../core/wait.js";
|
|
3
|
+
/**
|
|
4
|
+
* Speaks the memcached text protocol instead of trusting a bare TCP
|
|
5
|
+
* connect: the docker userland proxy (and the msb port-publish emulation)
|
|
6
|
+
* accepts the host port before the guest's memcached process is actually
|
|
7
|
+
* listening, and memcached itself never logs anything on startup, so
|
|
8
|
+
* there is no log line to wait on either. Sending `version` and requiring
|
|
9
|
+
* a `VERSION` reply proves a real memcached is on the other end.
|
|
10
|
+
*/
|
|
11
|
+
export declare class MemcachedRespondsStrategy implements WaitStrategy {
|
|
12
|
+
private timeoutMs;
|
|
13
|
+
withStartupTimeout(ms: number): this;
|
|
14
|
+
private probe;
|
|
15
|
+
waitUntilReady(target: WaitTarget): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/** A single-node Memcached container, ready-checked with a protocol-level `version` probe. */
|
|
18
|
+
export declare class MemcachedContainer extends GenericContainer {
|
|
19
|
+
constructor(image?: string);
|
|
20
|
+
static start(image?: string): Promise<MemcachedContainer>;
|
|
21
|
+
/** The `host:port` address of the running container. */
|
|
22
|
+
get address(): string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=memcached.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memcached.d.ts","sourceRoot":"","sources":["../../src/modules/memcached.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAYhE;;;;;;;GAOG;AACH,qBAAa,yBAA0B,YAAW,YAAY;IAC5D,OAAO,CAAC,SAAS,CAAsB;IAEvC,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKpC,OAAO,CAAC,KAAK;IA0CP,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CA0BxD;AAED,8FAA8F;AAC9F,qBAAa,kBAAmB,SAAQ,gBAAgB;gBAC1C,KAAK,SAAyB;WAKpB,KAAK,CAAC,KAAK,SAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIxF,wDAAwD;IACxD,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as net from "node:net";
|
|
2
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
3
|
+
import { ContainerLaunchError } from "../core/errors.js";
|
|
4
|
+
const GUEST_PORT = 11211;
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
6
|
+
const POLL_INTERVAL_MS = 250;
|
|
7
|
+
const PROBE_TIMEOUT_MS = 1_000;
|
|
8
|
+
const LOG_TAIL_LINES = 50;
|
|
9
|
+
function sleep(ms) {
|
|
10
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Speaks the memcached text protocol instead of trusting a bare TCP
|
|
14
|
+
* connect: the docker userland proxy (and the msb port-publish emulation)
|
|
15
|
+
* accepts the host port before the guest's memcached process is actually
|
|
16
|
+
* listening, and memcached itself never logs anything on startup, so
|
|
17
|
+
* there is no log line to wait on either. Sending `version` and requiring
|
|
18
|
+
* a `VERSION` reply proves a real memcached is on the other end.
|
|
19
|
+
*/
|
|
20
|
+
export class MemcachedRespondsStrategy {
|
|
21
|
+
timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
22
|
+
withStartupTimeout(ms) {
|
|
23
|
+
this.timeoutMs = ms;
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
probe(host, port) {
|
|
27
|
+
return new Promise((resolveProbe) => {
|
|
28
|
+
const socket = new net.Socket();
|
|
29
|
+
let settled = false;
|
|
30
|
+
const finish = (ready) => {
|
|
31
|
+
if (settled) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
settled = true;
|
|
35
|
+
clearTimeout(hardTimer);
|
|
36
|
+
socket.removeAllListeners();
|
|
37
|
+
socket.destroy();
|
|
38
|
+
resolveProbe(ready);
|
|
39
|
+
};
|
|
40
|
+
// Defense in depth: socket.setTimeout() below is the primary bound,
|
|
41
|
+
// but a plain JS timer as a backstop costs nothing and closes a class
|
|
42
|
+
// of bug this codebase has hit before (see the connect/response
|
|
43
|
+
// timeout added to DockerClient.send()) — a socket-level timeout that,
|
|
44
|
+
// on at least one runtime, intermittently never fires for a connection
|
|
45
|
+
// that never completes, leaving nothing else to ever settle this
|
|
46
|
+
// promise. waitUntilReady's own polling loop can't rescue a probe()
|
|
47
|
+
// call that never resolves in the first place.
|
|
48
|
+
const hardTimer = setTimeout(() => finish(false), PROBE_TIMEOUT_MS + 500);
|
|
49
|
+
socket.setTimeout(PROBE_TIMEOUT_MS);
|
|
50
|
+
socket.once("timeout", () => finish(false));
|
|
51
|
+
socket.once("error", () => finish(false));
|
|
52
|
+
socket.connect(port, host, () => {
|
|
53
|
+
socket.write("version\r\n");
|
|
54
|
+
});
|
|
55
|
+
let buffered = "";
|
|
56
|
+
socket.on("data", (chunk) => {
|
|
57
|
+
buffered += chunk.toString("utf8");
|
|
58
|
+
const line = buffered.split("\r\n")[0] ?? "";
|
|
59
|
+
if (line.length > 0) {
|
|
60
|
+
finish(line.startsWith("VERSION"));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async waitUntilReady(target) {
|
|
66
|
+
const guestPort = target.exposedGuestPorts[0];
|
|
67
|
+
if (guestPort === undefined) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const hostPort = target.mappedPort(guestPort);
|
|
71
|
+
const deadline = Date.now() + this.timeoutMs;
|
|
72
|
+
for (;;) {
|
|
73
|
+
if (await this.probe(target.host, hostPort)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (Date.now() >= deadline) {
|
|
77
|
+
let logs;
|
|
78
|
+
try {
|
|
79
|
+
logs = await target.currentLogs();
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
logs = "";
|
|
83
|
+
}
|
|
84
|
+
const tail = logs.split("\n").slice(-LOG_TAIL_LINES).join("\n");
|
|
85
|
+
throw new ContainerLaunchError(`Timed out waiting for ${target.describe()} to reply to a VERSION probe.\n${tail}`);
|
|
86
|
+
}
|
|
87
|
+
await sleep(POLL_INTERVAL_MS);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** A single-node Memcached container, ready-checked with a protocol-level `version` probe. */
|
|
92
|
+
export class MemcachedContainer extends GenericContainer {
|
|
93
|
+
constructor(image = "memcached:1.6-alpine") {
|
|
94
|
+
super(image);
|
|
95
|
+
this.withExposedPorts(GUEST_PORT).waitingFor(new MemcachedRespondsStrategy());
|
|
96
|
+
}
|
|
97
|
+
static async start(image = "memcached:1.6-alpine") {
|
|
98
|
+
return (await new MemcachedContainer(image).start());
|
|
99
|
+
}
|
|
100
|
+
/** The `host:port` address of the running container. */
|
|
101
|
+
get address() {
|
|
102
|
+
return `${this.host}:${this.getMappedPort(GUEST_PORT)}`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=memcached.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memcached.js","sourceRoot":"","sources":["../../src/modules/memcached.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
2
|
+
/**
|
|
3
|
+
* A single-node MongoDB container running as a one-member replica set
|
|
4
|
+
* (required for transactions/change streams). `containerIsStarted`
|
|
5
|
+
* initiates the replica set and waits for a primary to be elected before
|
|
6
|
+
* `start()` returns, so `connectionString` is always usable immediately
|
|
7
|
+
* after the container boots.
|
|
8
|
+
*/
|
|
9
|
+
export declare class MongoDBContainer extends GenericContainer {
|
|
10
|
+
constructor(image?: string);
|
|
11
|
+
static start(image?: string): Promise<MongoDBContainer>;
|
|
12
|
+
protected containerIsStarted(): Promise<void>;
|
|
13
|
+
private pollUntil;
|
|
14
|
+
/** A `mongodb://` connection string for the running container's `test` database. */
|
|
15
|
+
get connectionString(): string;
|
|
16
|
+
/** Alias for `connectionString`; the container is always a (single-node) replica set. */
|
|
17
|
+
get replicaSetUrl(): string;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=mongodb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongodb.d.ts","sourceRoot":"","sources":["../../src/modules/mongodb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAWhE;;;;;;GAMG;AACH,qBAAa,gBAAiB,SAAQ,gBAAgB;gBACxC,KAAK,SAAc;WAOT,KAAK,CAAC,KAAK,SAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;cAIlD,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;YAqB9C,SAAS;IAqBvB,oFAAoF;IACpF,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED,yFAAyF;IACzF,IAAI,aAAa,IAAI,MAAM,CAE1B;CACF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
2
|
+
import { Wait } from "../core/wait.js";
|
|
3
|
+
const GUEST_PORT = 27017;
|
|
4
|
+
const REPLICA_SET_TIMEOUT_MS = 60_000;
|
|
5
|
+
const POLL_INTERVAL_MS = 500;
|
|
6
|
+
function sleep(ms) {
|
|
7
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A single-node MongoDB container running as a one-member replica set
|
|
11
|
+
* (required for transactions/change streams). `containerIsStarted`
|
|
12
|
+
* initiates the replica set and waits for a primary to be elected before
|
|
13
|
+
* `start()` returns, so `connectionString` is always usable immediately
|
|
14
|
+
* after the container boots.
|
|
15
|
+
*/
|
|
16
|
+
export class MongoDBContainer extends GenericContainer {
|
|
17
|
+
constructor(image = "mongo:8.0") {
|
|
18
|
+
super(image);
|
|
19
|
+
this.withExposedPorts(GUEST_PORT)
|
|
20
|
+
.withCommand("mongod", "--replSet", "docker-rs", "--bind_ip_all")
|
|
21
|
+
.waitingFor(Wait.forListeningPort());
|
|
22
|
+
}
|
|
23
|
+
static async start(image = "mongo:8.0") {
|
|
24
|
+
return (await new MongoDBContainer(image).start());
|
|
25
|
+
}
|
|
26
|
+
async containerIsStarted() {
|
|
27
|
+
await this.pollUntil("rs.initiate to succeed", async () => {
|
|
28
|
+
const result = await this.exec("mongosh", "--quiet", "--eval", "try { rs.status() } catch (e) { rs.initiate() }");
|
|
29
|
+
return result.exitCode === 0;
|
|
30
|
+
});
|
|
31
|
+
await this.pollUntil("a PRIMARY to be elected", async () => {
|
|
32
|
+
const result = await this.exec("mongosh", "--quiet", "--eval", "db.hello().isWritablePrimary");
|
|
33
|
+
return result.stdout.trim().endsWith("true");
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Bounded retry through the race between the proxy accepting the
|
|
37
|
+
// published port and mongod actually listening behind it: the same
|
|
38
|
+
// early-accept window the read-probe wait strategy handles for a plain
|
|
39
|
+
// TCP wait also affects the first exec against the guest, so
|
|
40
|
+
// rs.initiate/db.hello() need their own poll rather than a single shot.
|
|
41
|
+
async pollUntil(what, cond) {
|
|
42
|
+
const deadline = Date.now() + REPLICA_SET_TIMEOUT_MS;
|
|
43
|
+
for (;;) {
|
|
44
|
+
let ready = false;
|
|
45
|
+
try {
|
|
46
|
+
ready = await cond();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
ready = false;
|
|
50
|
+
}
|
|
51
|
+
if (ready) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (Date.now() >= deadline) {
|
|
55
|
+
throw new Error(`Mongo replica set on ${this.host}:${this.getMappedPort(GUEST_PORT)} did not reach '${what}' within ${REPLICA_SET_TIMEOUT_MS / 1000}s`);
|
|
56
|
+
}
|
|
57
|
+
await sleep(POLL_INTERVAL_MS);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** A `mongodb://` connection string for the running container's `test` database. */
|
|
61
|
+
get connectionString() {
|
|
62
|
+
return `mongodb://${this.host}:${this.getMappedPort(GUEST_PORT)}/test?directConnection=true`;
|
|
63
|
+
}
|
|
64
|
+
/** Alias for `connectionString`; the container is always a (single-node) replica set. */
|
|
65
|
+
get replicaSetUrl() {
|
|
66
|
+
return this.connectionString;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=mongodb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongodb.js","sourceRoot":"","sources":["../../src/modules/mongodb.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
2
|
+
/**
|
|
3
|
+
* A single-node MySQL container. Defaults to a `test`/`test`/`test`
|
|
4
|
+
* user/password/database trio (plus `MYSQL_ROOT_PASSWORD=test`) so
|
|
5
|
+
* `connectionString` is usable with zero configuration; call
|
|
6
|
+
* `withUsername`/`withPassword`/`withDatabase` before `start()` to override
|
|
7
|
+
* any of them.
|
|
8
|
+
*
|
|
9
|
+
* ### Readiness — empirically pinned, not guessed
|
|
10
|
+
*
|
|
11
|
+
* The official entrypoint boots mysqld TWICE: once as a throwaway "temp
|
|
12
|
+
* server" to run init scripts, then for real. Both prints, plus the X
|
|
13
|
+
* Plugin's own "ready for connections" line, contain the substring `ready
|
|
14
|
+
* for connections`, and naively counting occurrences is a trap: the temp
|
|
15
|
+
* server's X Plugin binds `port: 33060` — whose digits start with `3306`,
|
|
16
|
+
* so an unanchored `port: 3306` regex false-matches it too. Captured
|
|
17
|
+
* verbatim from a real `docker run mysql:8.4` boot with this module's env
|
|
18
|
+
* (`MYSQL_USER=test`, `MYSQL_DATABASE=test`, `MYSQL_ROOT_PASSWORD=test`):
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* ...
|
|
22
|
+
* [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock
|
|
23
|
+
* [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.10' socket: '/var/run/mysqld/mysqld.sock' port: 0 MySQL Community Server - GPL.
|
|
24
|
+
* ...(init scripts run, temp server shuts down)...
|
|
25
|
+
* [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
|
|
26
|
+
* [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.10' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* Four lines contain `ready for connections`; only the last is the real
|
|
30
|
+
* server bound to 3306. The temp server prints `port: 0` (no port yet) and
|
|
31
|
+
* the X Plugin lines print `33060`, whose `3306` prefix would satisfy an
|
|
32
|
+
* unanchored match — so `times=N` counting is fragile here (N would have to
|
|
33
|
+
* track exactly which of the 4 lines are "real", and a naive `times=2`
|
|
34
|
+
* fires on the temp server's own X-Plugin + mysqld pair, one full boot
|
|
35
|
+
* early). Instead this pins a regex anchored on the real server's `port:
|
|
36
|
+
* 3306` with a trailing non-digit-or-end boundary, so it cannot match
|
|
37
|
+
* `33060`, and `times=1` is then unambiguous (that exact line appears once,
|
|
38
|
+
* only after the real server is up).
|
|
39
|
+
*/
|
|
40
|
+
export declare class MySQLContainer extends GenericContainer {
|
|
41
|
+
private usernameState;
|
|
42
|
+
private passwordState;
|
|
43
|
+
private databaseState;
|
|
44
|
+
constructor(image?: string);
|
|
45
|
+
static start(image?: string): Promise<MySQLContainer>;
|
|
46
|
+
/** Overrides `MYSQL_USER` (default `test`). */
|
|
47
|
+
withUsername(username: string): this;
|
|
48
|
+
/** Overrides `MYSQL_PASSWORD` (default `test`). */
|
|
49
|
+
withPassword(password: string): this;
|
|
50
|
+
/** Overrides `MYSQL_DATABASE` (default `test`). */
|
|
51
|
+
withDatabase(database: string): this;
|
|
52
|
+
/** The configured database user (default `test`). */
|
|
53
|
+
get username(): string;
|
|
54
|
+
/** The configured database password (default `test`). */
|
|
55
|
+
get password(): string;
|
|
56
|
+
/** The configured database name (default `test`). */
|
|
57
|
+
get databaseName(): string;
|
|
58
|
+
/** A `mysql://` connection string for the running container's `databaseName`. */
|
|
59
|
+
get connectionString(): string;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=mysql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../src/modules/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAKhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBAAa,cAAe,SAAQ,gBAAgB;IAClD,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,aAAa,CAAU;gBAEnB,KAAK,SAAc;WAuBT,KAAK,CAAC,KAAK,SAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAIzE,+CAA+C;IAC/C,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,mDAAmD;IACnD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,mDAAmD;IACnD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,qDAAqD;IACrD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,yDAAyD;IACzD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,qDAAqD;IACrD,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,iFAAiF;IACjF,IAAI,gBAAgB,IAAI,MAAM,CAE7B;CACF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { GenericContainer } from "../core/generic-container.js";
|
|
2
|
+
import { Wait } from "../core/wait.js";
|
|
3
|
+
const GUEST_PORT = 3306;
|
|
4
|
+
/**
|
|
5
|
+
* A single-node MySQL container. Defaults to a `test`/`test`/`test`
|
|
6
|
+
* user/password/database trio (plus `MYSQL_ROOT_PASSWORD=test`) so
|
|
7
|
+
* `connectionString` is usable with zero configuration; call
|
|
8
|
+
* `withUsername`/`withPassword`/`withDatabase` before `start()` to override
|
|
9
|
+
* any of them.
|
|
10
|
+
*
|
|
11
|
+
* ### Readiness — empirically pinned, not guessed
|
|
12
|
+
*
|
|
13
|
+
* The official entrypoint boots mysqld TWICE: once as a throwaway "temp
|
|
14
|
+
* server" to run init scripts, then for real. Both prints, plus the X
|
|
15
|
+
* Plugin's own "ready for connections" line, contain the substring `ready
|
|
16
|
+
* for connections`, and naively counting occurrences is a trap: the temp
|
|
17
|
+
* server's X Plugin binds `port: 33060` — whose digits start with `3306`,
|
|
18
|
+
* so an unanchored `port: 3306` regex false-matches it too. Captured
|
|
19
|
+
* verbatim from a real `docker run mysql:8.4` boot with this module's env
|
|
20
|
+
* (`MYSQL_USER=test`, `MYSQL_DATABASE=test`, `MYSQL_ROOT_PASSWORD=test`):
|
|
21
|
+
*
|
|
22
|
+
* ```
|
|
23
|
+
* ...
|
|
24
|
+
* [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock
|
|
25
|
+
* [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.10' socket: '/var/run/mysqld/mysqld.sock' port: 0 MySQL Community Server - GPL.
|
|
26
|
+
* ...(init scripts run, temp server shuts down)...
|
|
27
|
+
* [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
|
|
28
|
+
* [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.10' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Four lines contain `ready for connections`; only the last is the real
|
|
32
|
+
* server bound to 3306. The temp server prints `port: 0` (no port yet) and
|
|
33
|
+
* the X Plugin lines print `33060`, whose `3306` prefix would satisfy an
|
|
34
|
+
* unanchored match — so `times=N` counting is fragile here (N would have to
|
|
35
|
+
* track exactly which of the 4 lines are "real", and a naive `times=2`
|
|
36
|
+
* fires on the temp server's own X-Plugin + mysqld pair, one full boot
|
|
37
|
+
* early). Instead this pins a regex anchored on the real server's `port:
|
|
38
|
+
* 3306` with a trailing non-digit-or-end boundary, so it cannot match
|
|
39
|
+
* `33060`, and `times=1` is then unambiguous (that exact line appears once,
|
|
40
|
+
* only after the real server is up).
|
|
41
|
+
*/
|
|
42
|
+
export class MySQLContainer extends GenericContainer {
|
|
43
|
+
usernameState = "test";
|
|
44
|
+
passwordState = "test";
|
|
45
|
+
databaseState = "test";
|
|
46
|
+
constructor(image = "mysql:8.4") {
|
|
47
|
+
super(image);
|
|
48
|
+
this.withExposedPorts(GUEST_PORT)
|
|
49
|
+
.withEnv("MYSQL_USER", this.usernameState)
|
|
50
|
+
.withEnv("MYSQL_PASSWORD", this.passwordState)
|
|
51
|
+
.withEnv("MYSQL_DATABASE", this.databaseState)
|
|
52
|
+
.withEnv("MYSQL_ROOT_PASSWORD", "test")
|
|
53
|
+
// Anchored on the real server's line (see the class doc for the
|
|
54
|
+
// captured log excerpt and why an unanchored "port: 3306" or a naive
|
|
55
|
+
// times=2 both misfire on the temp-server boot).
|
|
56
|
+
// First boot initializes the datafiles and boots mysqld twice (the
|
|
57
|
+
// temp server for init scripts, then the real one). On a fast host
|
|
58
|
+
// that finishes well under 60s, but a loaded Windows CI runner's
|
|
59
|
+
// first boot overruns the 60s default — 120s absorbs the slow case
|
|
60
|
+
// without masking a real hang.
|
|
61
|
+
.waitingFor(Wait.forLogMessage(".*mysqld: ready for connections.*port: 3306($|[^0-9]).*", 1).withStartupTimeout(120_000));
|
|
62
|
+
// No withMemoryLimit override: unlike SpringCloudConfig's Paketo JVM
|
|
63
|
+
// image, MySQL 8.4's InnoDB default footprint fits msb's default ~450M
|
|
64
|
+
// microVM RAM, so no module-level memory floor is warranted here.
|
|
65
|
+
}
|
|
66
|
+
static async start(image = "mysql:8.4") {
|
|
67
|
+
return (await new MySQLContainer(image).start());
|
|
68
|
+
}
|
|
69
|
+
/** Overrides `MYSQL_USER` (default `test`). */
|
|
70
|
+
withUsername(username) {
|
|
71
|
+
this.usernameState = username;
|
|
72
|
+
return this.withEnv("MYSQL_USER", username);
|
|
73
|
+
}
|
|
74
|
+
/** Overrides `MYSQL_PASSWORD` (default `test`). */
|
|
75
|
+
withPassword(password) {
|
|
76
|
+
this.passwordState = password;
|
|
77
|
+
return this.withEnv("MYSQL_PASSWORD", password);
|
|
78
|
+
}
|
|
79
|
+
/** Overrides `MYSQL_DATABASE` (default `test`). */
|
|
80
|
+
withDatabase(database) {
|
|
81
|
+
this.databaseState = database;
|
|
82
|
+
return this.withEnv("MYSQL_DATABASE", database);
|
|
83
|
+
}
|
|
84
|
+
/** The configured database user (default `test`). */
|
|
85
|
+
get username() {
|
|
86
|
+
return this.usernameState;
|
|
87
|
+
}
|
|
88
|
+
/** The configured database password (default `test`). */
|
|
89
|
+
get password() {
|
|
90
|
+
return this.passwordState;
|
|
91
|
+
}
|
|
92
|
+
/** The configured database name (default `test`). */
|
|
93
|
+
get databaseName() {
|
|
94
|
+
return this.databaseState;
|
|
95
|
+
}
|
|
96
|
+
/** A `mysql://` connection string for the running container's `databaseName`. */
|
|
97
|
+
get connectionString() {
|
|
98
|
+
return `mysql://${this.usernameState}:${this.passwordState}@${this.host}:${this.getMappedPort(GUEST_PORT)}/${this.databaseState}`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=mysql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../src/modules/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,OAAO,cAAe,SAAQ,gBAAgB;IAC1C,aAAa,GAAG,MAAM,CAAC;IACvB,aAAa,GAAG,MAAM,CAAC;IACvB,aAAa,GAAG,MAAM,CAAC;IAE/B,YAAY,KAAK,GAAG,WAAW;QAC7B,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC;aAC9B,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,aAAa,CAAC;aACzC,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC,aAAa,CAAC;aAC7C,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC,aAAa,CAAC;aAC7C,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC;YACvC,gEAAgE;YAChE,qEAAqE;YACrE,iDAAiD;YACjD,mEAAmE;YACnE,mEAAmE;YACnE,iEAAiE;YACjE,mEAAmE;YACnE,+BAA+B;aAC9B,UAAU,CACT,IAAI,CAAC,aAAa,CAAC,yDAAyD,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAC7G,CAAC;QACJ,qEAAqE;QACrE,uEAAuE;QACvE,kEAAkE;IACpE,CAAC;IAED,MAAM,CAAU,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,WAAW;QAC7C,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAmB,CAAC;IACrE,CAAC;IAED,+CAA+C;IAC/C,YAAY,CAAC,QAAgB;QAC3B,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED,mDAAmD;IACnD,YAAY,CAAC,QAAgB;QAC3B,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED,mDAAmD;IACnD,YAAY,CAAC,QAAgB;QAC3B,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED,qDAAqD;IACrD,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,yDAAyD;IACzD,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,qDAAqD;IACrD,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,iFAAiF;IACjF,IAAI,gBAAgB;QAClB,OAAO,WAAW,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;IACpI,CAAC;CACF"}
|