testcontainers 9.5.0 → 9.7.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/dist/package.json +14 -14
- package/dist/src/docker/docker-client-config.js +42 -36
- package/dist/src/docker/docker-client.d.ts +2 -0
- package/dist/src/docker/docker-client.js +6 -5
- package/dist/src/docker/functions/container/attach-container.js +1 -1
- package/dist/src/docker/functions/container/create-container.js +6 -4
- package/dist/src/docker/functions/container/exec-container.d.ts +2 -1
- package/dist/src/docker/functions/container/exec-container.js +15 -8
- package/dist/src/docker/functions/container/get-container.js +1 -1
- package/dist/src/docker/functions/container/inspect-container.js +1 -1
- package/dist/src/docker/functions/container/put-container-archive.js +5 -2
- package/dist/src/docker/functions/container/remove-container.js +1 -1
- package/dist/src/docker/functions/container/restart-container.js +1 -1
- package/dist/src/docker/functions/container/start-container.js +1 -1
- package/dist/src/docker/functions/container/stop-container.js +2 -2
- package/dist/src/docker/functions/create-labels.js +0 -3
- package/dist/src/docker/functions/image/build-image.js +8 -3
- package/dist/src/docker/functions/image/image-exists.js +11 -8
- package/dist/src/docker/functions/image/pull-image.js +3 -3
- package/dist/src/docker/functions/network/connect-network.js +3 -2
- package/dist/src/docker/functions/network/create-network.js +2 -1
- package/dist/src/docker/functions/network/remove-network.js +3 -2
- package/dist/src/docker/functions/run-in-container.js +6 -6
- package/dist/src/docker/pull-stream-parser.d.ts +0 -1
- package/dist/src/docker/pull-stream-parser.js +2 -41
- package/dist/src/docker/resolve-host.d.ts +2 -1
- package/dist/src/docker/resolve-host.js +7 -6
- package/dist/src/docker-compose/default-docker-compose-options.js +4 -2
- package/dist/src/docker-compose/docker-compose-options.d.ts +2 -0
- package/dist/src/docker-compose/functions/docker-compose-down.js +1 -1
- package/dist/src/docker-compose/functions/docker-compose-pull.js +4 -4
- package/dist/src/docker-compose/functions/docker-compose-stop.js +1 -1
- package/dist/src/docker-compose/functions/docker-compose-up.js +1 -1
- package/dist/src/docker-compose-environment/docker-compose-environment.d.ts +1 -1
- package/dist/src/docker-compose-environment/docker-compose-environment.js +11 -10
- package/dist/src/generic-container/generic-container-builder.js +1 -1
- package/dist/src/generic-container/generic-container.d.ts +6 -2
- package/dist/src/generic-container/generic-container.js +30 -17
- package/dist/src/generic-container/started-generic-container.d.ts +2 -0
- package/dist/src/generic-container/started-generic-container.js +26 -7
- package/dist/src/images.js +1 -1
- package/dist/src/logger.d.ts +20 -31
- package/dist/src/logger.js +32 -42
- package/dist/src/modules/elasticsearch/elasticsearch-container.js +1 -1
- package/dist/src/modules/nats/nats-container.js +1 -1
- package/dist/src/network.d.ts +1 -0
- package/dist/src/network.js +11 -5
- package/dist/src/port-check.d.ts +4 -1
- package/dist/src/port-check.js +10 -4
- package/dist/src/port-forwarder.js +5 -3
- package/dist/src/reaper.js +11 -7
- package/dist/src/registry-auth-locator/credential-provider.js +3 -5
- package/dist/src/registry-auth-locator/get-auth-config.js +3 -3
- package/dist/src/system-info.js +2 -5
- package/dist/src/test-container.d.ts +2 -6
- package/dist/src/uuid.js +1 -1
- package/dist/src/wait-for-container.d.ts +1 -1
- package/dist/src/wait-for-container.js +6 -6
- package/dist/src/wait-strategy/composite-wait-strategy.d.ts +11 -0
- package/dist/src/wait-strategy/composite-wait-strategy.js +54 -0
- package/dist/src/wait-strategy/health-check-wait-strategy.js +7 -3
- package/dist/src/wait-strategy/host-port-wait-strategy.d.ts +1 -5
- package/dist/src/wait-strategy/host-port-wait-strategy.js +21 -17
- package/dist/src/wait-strategy/http-wait-strategy.d.ts +1 -1
- package/dist/src/wait-strategy/http-wait-strategy.js +8 -2
- package/dist/src/wait-strategy/log-wait-strategy.d.ts +1 -1
- package/dist/src/wait-strategy/log-wait-strategy.js +8 -7
- package/dist/src/wait-strategy/shell-wait-strategy.js +7 -4
- package/dist/src/wait-strategy/startup-check-strategy.js +6 -1
- package/dist/src/wait-strategy/wait-strategy.d.ts +8 -3
- package/dist/src/wait-strategy/wait-strategy.js +8 -0
- package/dist/src/wait-strategy/wait.d.ts +3 -0
- package/dist/src/wait-strategy/wait.js +8 -0
- package/package.json +14 -14
- package/dist/src/docker/functions/image/list-images.d.ts +0 -3
- package/dist/src/docker/functions/image/list-images.js +0 -34
- package/dist/src/wait-strategy/default-wait-strategy.d.ts +0 -3
- package/dist/src/wait-strategy/default-wait-strategy.js +0 -7
|
@@ -26,7 +26,7 @@ class ElasticsearchContainer extends __1.GenericContainer {
|
|
|
26
26
|
.withEnvironment({ "discovery.type": "single-node" })
|
|
27
27
|
.withCopyContentToContainer([
|
|
28
28
|
{
|
|
29
|
-
content: "-
|
|
29
|
+
content: "-Xmx2G\n",
|
|
30
30
|
target: "/usr/share/elasticsearch/config/jvm.options.d/elasticsearch-default-memory-vm.options",
|
|
31
31
|
},
|
|
32
32
|
])
|
|
@@ -19,7 +19,7 @@ const HTTP_MANAGEMENT_PORT = 8222;
|
|
|
19
19
|
const USER_ARGUMENT_KEY = "--user";
|
|
20
20
|
const PASS_ARGUMENT_KEY = "--pass";
|
|
21
21
|
class NatsContainer extends generic_container_1.GenericContainer {
|
|
22
|
-
constructor(image = "nats:2.8-alpine") {
|
|
22
|
+
constructor(image = "nats:2.8.4-alpine") {
|
|
23
23
|
super(image);
|
|
24
24
|
this.args = {};
|
|
25
25
|
this.args[USER_ARGUMENT_KEY] = "test";
|
package/dist/src/network.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Uuid } from "./uuid";
|
|
|
2
2
|
import { CreateNetworkOptions } from "./docker/functions/network/create-network";
|
|
3
3
|
export declare class Network {
|
|
4
4
|
private readonly createNetworkOptions;
|
|
5
|
+
private readonly uuid;
|
|
5
6
|
constructor(createNetworkOptions?: Partial<CreateNetworkOptions>, uuid?: Uuid);
|
|
6
7
|
start(): Promise<StartedNetwork>;
|
|
7
8
|
}
|
package/dist/src/network.js
CHANGED
|
@@ -17,14 +17,19 @@ const create_network_1 = require("./docker/functions/network/create-network");
|
|
|
17
17
|
const remove_network_1 = require("./docker/functions/network/remove-network");
|
|
18
18
|
class Network {
|
|
19
19
|
constructor(createNetworkOptions = {}, uuid = new uuid_1.RandomUuid()) {
|
|
20
|
-
this.createNetworkOptions =
|
|
20
|
+
this.createNetworkOptions = createNetworkOptions;
|
|
21
|
+
this.uuid = uuid;
|
|
22
|
+
this.createNetworkOptions = createNetworkOptions;
|
|
21
23
|
}
|
|
22
24
|
start() {
|
|
23
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
const name = this.uuid.nextUuid();
|
|
27
|
+
const options = Object.assign({ name, driver: "bridge", checkDuplicate: true, internal: false, attachable: false, ingress: false, enableIPv6: false }, this.createNetworkOptions);
|
|
24
28
|
yield reaper_1.ReaperInstance.getInstance();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
logger_1.log.info(`Starting network "${name}"...`);
|
|
30
|
+
const id = yield (0, create_network_1.createNetwork)(options);
|
|
31
|
+
logger_1.log.info(`Started network "${name}" with ID "${id}"`);
|
|
32
|
+
return new StartedNetwork(id, options);
|
|
28
33
|
});
|
|
29
34
|
}
|
|
30
35
|
}
|
|
@@ -42,8 +47,9 @@ class StartedNetwork {
|
|
|
42
47
|
}
|
|
43
48
|
stop() {
|
|
44
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
-
logger_1.log.info(`Stopping network with ID
|
|
50
|
+
logger_1.log.info(`Stopping network with ID "${this.id}"...`);
|
|
46
51
|
yield (0, remove_network_1.removeNetwork)(this.id);
|
|
52
|
+
logger_1.log.info(`Stopped network with ID "${this.id}"`);
|
|
47
53
|
return new StoppedNetwork();
|
|
48
54
|
});
|
|
49
55
|
}
|
package/dist/src/port-check.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Dockerode from "dockerode";
|
|
2
|
+
import { Provider } from "./docker/docker-client";
|
|
2
3
|
export interface PortCheck {
|
|
3
4
|
isBound(port: number): Promise<boolean>;
|
|
4
5
|
}
|
|
@@ -8,10 +9,12 @@ export declare class HostPortCheck implements PortCheck {
|
|
|
8
9
|
isBound(port: number): Promise<boolean>;
|
|
9
10
|
}
|
|
10
11
|
export declare class InternalPortCheck implements PortCheck {
|
|
12
|
+
private readonly dockerode;
|
|
13
|
+
private readonly provider;
|
|
11
14
|
private readonly container;
|
|
12
15
|
private isDistroless;
|
|
13
16
|
private commandOutputs;
|
|
14
|
-
constructor(container: Dockerode.Container);
|
|
17
|
+
constructor(dockerode: Dockerode, provider: Provider, container: Dockerode.Container);
|
|
15
18
|
isBound(port: number): Promise<boolean>;
|
|
16
19
|
private commandOutputsKey;
|
|
17
20
|
}
|
package/dist/src/port-check.js
CHANGED
|
@@ -39,7 +39,9 @@ class HostPortCheck {
|
|
|
39
39
|
}
|
|
40
40
|
exports.HostPortCheck = HostPortCheck;
|
|
41
41
|
class InternalPortCheck {
|
|
42
|
-
constructor(container) {
|
|
42
|
+
constructor(dockerode, provider, container) {
|
|
43
|
+
this.dockerode = dockerode;
|
|
44
|
+
this.provider = provider;
|
|
43
45
|
this.container = container;
|
|
44
46
|
this.isDistroless = false;
|
|
45
47
|
this.commandOutputs = new Set();
|
|
@@ -52,14 +54,16 @@ class InternalPortCheck {
|
|
|
52
54
|
["/bin/sh", "-c", `nc -vz -w 1 localhost ${port}`],
|
|
53
55
|
["/bin/bash", "-c", `</dev/tcp/localhost/${port}`],
|
|
54
56
|
];
|
|
55
|
-
const commandResults = yield Promise.all(commands.map((command) => (0, exec_container_1.execContainer)(this.container, command, false)));
|
|
57
|
+
const commandResults = yield Promise.all(commands.map((command) => (0, exec_container_1.execContainer)(this.dockerode, this.provider, this.container, command, false)));
|
|
56
58
|
const isBound = commandResults.some((result) => result.exitCode === 0);
|
|
57
59
|
if (!isBound && logger_1.log.enabled()) {
|
|
58
60
|
const shellExists = commandResults.some((result) => result.exitCode !== 126);
|
|
59
61
|
if (!shellExists) {
|
|
60
62
|
if (!this.isDistroless) {
|
|
61
63
|
this.isDistroless = true;
|
|
62
|
-
logger_1.log.error(`The HostPortWaitStrategy will not work on a distroless image, use an alternate wait strategy
|
|
64
|
+
logger_1.log.error(`The HostPortWaitStrategy will not work on a distroless image, use an alternate wait strategy`, {
|
|
65
|
+
containerId: this.container.id,
|
|
66
|
+
});
|
|
63
67
|
}
|
|
64
68
|
}
|
|
65
69
|
else {
|
|
@@ -68,7 +72,9 @@ class InternalPortCheck {
|
|
|
68
72
|
.filter((result) => result.exitCode !== 126 && result.output.length > 0)
|
|
69
73
|
.forEach((result) => {
|
|
70
74
|
if (!this.commandOutputs.has(this.commandOutputsKey(result.output))) {
|
|
71
|
-
logger_1.log.trace(`Port check result
|
|
75
|
+
logger_1.log.trace(`Port check result exit code ${result.exitCode}: ${result.output}`, {
|
|
76
|
+
containerId: this.container.id,
|
|
77
|
+
});
|
|
72
78
|
this.commandOutputs.add(this.commandOutputsKey(result.output));
|
|
73
79
|
}
|
|
74
80
|
});
|
|
@@ -24,8 +24,9 @@ class PortForwarder {
|
|
|
24
24
|
}
|
|
25
25
|
exposeHostPort(port) {
|
|
26
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
-
logger_1.log.info(`Exposing host port ${port}
|
|
27
|
+
logger_1.log.info(`Exposing host port ${port}...`);
|
|
28
28
|
yield this.sshConnection.remoteForward("localhost", port);
|
|
29
|
+
logger_1.log.info(`Exposed host port ${port}`);
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
32
|
getNetworkId() {
|
|
@@ -53,7 +54,7 @@ class PortForwarderInstance {
|
|
|
53
54
|
}
|
|
54
55
|
static createInstance() {
|
|
55
56
|
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
-
logger_1.log.debug(`Creating new Port Forwarder
|
|
57
|
+
logger_1.log.debug(`Creating new Port Forwarder...`);
|
|
57
58
|
const username = "root";
|
|
58
59
|
const password = new uuid_1.RandomUuid().nextUuid();
|
|
59
60
|
const containerPort = process.env["TESTCONTAINERS_SSHD_PORT"]
|
|
@@ -71,8 +72,9 @@ class PortForwarderInstance {
|
|
|
71
72
|
.start();
|
|
72
73
|
const host = (yield (0, docker_client_1.dockerClient)()).host;
|
|
73
74
|
const port = container.getMappedPort(22);
|
|
74
|
-
logger_1.log.debug(`Connecting to Port Forwarder on ${host}:${port}
|
|
75
|
+
logger_1.log.debug(`Connecting to Port Forwarder on "${host}:${port}"...`);
|
|
75
76
|
const connection = yield (0, ssh_remote_port_forward_1.createSshConnection)({ host, port, username, password });
|
|
77
|
+
logger_1.log.debug(`Connected to Port Forwarder on "${host}:${port}"`);
|
|
76
78
|
connection.unref();
|
|
77
79
|
return new PortForwarder(connection, container);
|
|
78
80
|
});
|
package/dist/src/reaper.js
CHANGED
|
@@ -77,7 +77,7 @@ class ReaperInstance {
|
|
|
77
77
|
return process.env.TESTCONTAINERS_RYUK_PRIVILEGED === "true";
|
|
78
78
|
}
|
|
79
79
|
static createDisabledInstance() {
|
|
80
|
-
logger_1.log.debug(`Not creating new Reaper for session
|
|
80
|
+
logger_1.log.debug(`Not creating new Reaper for session "${session_id_1.sessionId}"`);
|
|
81
81
|
return Promise.resolve(new DisabledReaper());
|
|
82
82
|
}
|
|
83
83
|
static createRealInstance() {
|
|
@@ -85,7 +85,7 @@ class ReaperInstance {
|
|
|
85
85
|
const containerPort = process.env["TESTCONTAINERS_RYUK_PORT"]
|
|
86
86
|
? { container: 8080, host: Number(process.env["TESTCONTAINERS_RYUK_PORT"]) }
|
|
87
87
|
: 8080;
|
|
88
|
-
logger_1.log.debug(`Creating new Reaper for session
|
|
88
|
+
logger_1.log.debug(`Creating new Reaper for session "${session_id_1.sessionId}"...`);
|
|
89
89
|
const container = new generic_container_1.GenericContainer(images_1.REAPER_IMAGE)
|
|
90
90
|
.withName(`testcontainers-ryuk-${session_id_1.sessionId}`)
|
|
91
91
|
.withExposedPorts(containerPort)
|
|
@@ -106,7 +106,7 @@ class ReaperInstance {
|
|
|
106
106
|
const retryStrategy = new retry_strategy_1.IntervalRetryStrategy(1000);
|
|
107
107
|
const retryResult = yield retryStrategy.retryUntil((attempt) => {
|
|
108
108
|
return new Promise((resolve) => {
|
|
109
|
-
logger_1.log.debug(`Connecting to Reaper (attempt ${attempt + 1})
|
|
109
|
+
logger_1.log.debug(`Connecting to Reaper (attempt ${attempt + 1}) on "${host}:${port}"...`, { containerId });
|
|
110
110
|
const socket = new net_1.Socket();
|
|
111
111
|
socket
|
|
112
112
|
.unref()
|
|
@@ -114,21 +114,25 @@ class ReaperInstance {
|
|
|
114
114
|
.on("error", (err) => logger_1.log.error(`Reaper ${containerId} socket error: ${err}`))
|
|
115
115
|
.on("close", (hadError) => {
|
|
116
116
|
if (hadError) {
|
|
117
|
-
logger_1.log.error(`Connection to Reaper
|
|
117
|
+
logger_1.log.error(`Connection to Reaper closed with error`, { containerId });
|
|
118
118
|
}
|
|
119
119
|
else {
|
|
120
|
-
logger_1.log.warn(`Connection to Reaper
|
|
120
|
+
logger_1.log.warn(`Connection to Reaper closed`, { containerId });
|
|
121
121
|
}
|
|
122
122
|
resolve(undefined);
|
|
123
123
|
})
|
|
124
124
|
.connect((0, port_1.getContainerPort)(port), host, () => {
|
|
125
|
-
logger_1.log.debug(`Connected to Reaper
|
|
125
|
+
logger_1.log.debug(`Connected to Reaper`, { containerId });
|
|
126
126
|
socket.write(`label=${labels_1.LABEL_TESTCONTAINERS_SESSION_ID}=${session_id_1.sessionId}\r\n`);
|
|
127
127
|
const reaper = new RealReaper(startedContainer, socket);
|
|
128
128
|
resolve(reaper);
|
|
129
129
|
});
|
|
130
130
|
});
|
|
131
|
-
}, (result) => result !== undefined, () =>
|
|
131
|
+
}, (result) => result !== undefined, () => {
|
|
132
|
+
const message = `Failed to connect to Reaper`;
|
|
133
|
+
logger_1.log.error(message, { containerId });
|
|
134
|
+
return new Error(message);
|
|
135
|
+
}, 4000);
|
|
132
136
|
if (retryResult instanceof RealReaper) {
|
|
133
137
|
return retryResult;
|
|
134
138
|
}
|
|
@@ -21,20 +21,18 @@ class CredentialProvider {
|
|
|
21
21
|
return undefined;
|
|
22
22
|
}
|
|
23
23
|
const programName = `docker-credential-${credentialProviderName}`;
|
|
24
|
-
logger_1.log.debug(`Executing Docker credential provider
|
|
24
|
+
logger_1.log.debug(`Executing Docker credential provider "${programName}"`);
|
|
25
25
|
const credentials = yield this.listCredentials(programName);
|
|
26
26
|
if (!Object.keys(credentials).some((aRegistry) => (0, registry_matches_1.registryMatches)(aRegistry, registry))) {
|
|
27
|
-
logger_1.log.debug(`No credential found for registry
|
|
27
|
+
logger_1.log.debug(`No credential found for registry "${registry}"`);
|
|
28
28
|
return undefined;
|
|
29
29
|
}
|
|
30
30
|
const response = yield this.runCredentialProvider(registry, programName);
|
|
31
|
-
|
|
31
|
+
return {
|
|
32
32
|
username: response.Username,
|
|
33
33
|
password: response.Secret,
|
|
34
34
|
registryAddress: response.ServerURL,
|
|
35
35
|
};
|
|
36
|
-
logger_1.log.debug(`Docker credential provider found auth config for ${registry}`);
|
|
37
|
-
return authConfig;
|
|
38
36
|
});
|
|
39
37
|
}
|
|
40
38
|
listCredentials(providerName) {
|
|
@@ -47,18 +47,18 @@ const registryAuthLocators = [new cred_helpers_1.CredHelpers(), new creds_store_
|
|
|
47
47
|
const authsCache = new Map();
|
|
48
48
|
const getAuthConfig = (registry) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
49
|
if (authsCache.has(registry)) {
|
|
50
|
-
logger_1.log.debug(`
|
|
50
|
+
logger_1.log.debug(`Auth config cache hit for registry "${registry}"`);
|
|
51
51
|
return authsCache.get(registry);
|
|
52
52
|
}
|
|
53
53
|
for (const registryAuthLocator of registryAuthLocators) {
|
|
54
54
|
const authConfig = yield registryAuthLocator.getAuthConfig(registry, yield dockerConfig);
|
|
55
55
|
if (authConfig) {
|
|
56
|
-
logger_1.log.debug(`
|
|
56
|
+
logger_1.log.debug(`Auth config found for registry "${registry}": ${registryAuthLocator.getName()}`);
|
|
57
57
|
authsCache.set(registry, authConfig);
|
|
58
58
|
return authConfig;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
logger_1.log.debug(`No registry auth locator found for registry
|
|
61
|
+
logger_1.log.debug(`No registry auth locator found for registry "${registry}"`);
|
|
62
62
|
authsCache.set(registry, undefined);
|
|
63
63
|
return undefined;
|
|
64
64
|
});
|
package/dist/src/system-info.js
CHANGED
|
@@ -19,15 +19,12 @@ const getSystemInfo = (dockerode) => __awaiter(void 0, void 0, void 0, function*
|
|
|
19
19
|
if (systemInfo !== undefined) {
|
|
20
20
|
return systemInfo;
|
|
21
21
|
}
|
|
22
|
-
logger_1.log.debug("Fetching system info");
|
|
22
|
+
logger_1.log.debug("Fetching system info...");
|
|
23
23
|
const nodeInfo = getNodeInfo();
|
|
24
24
|
const dockerInfo = yield (0, get_info_1.getDockerInfo)(dockerode);
|
|
25
25
|
const dockerComposeInfo = yield getDockerComposeInfo();
|
|
26
26
|
systemInfo = { nodeInfo, dockerInfo, dockerComposeInfo };
|
|
27
|
-
logger_1.log.debug(`
|
|
28
|
-
System: Node version: ${nodeInfo.version}, Platform: ${nodeInfo.platform}, Arch: ${nodeInfo.architecture}
|
|
29
|
-
Container runtime: OS: ${dockerInfo.operatingSystem}, Version: ${dockerInfo.serverVersion}, Arch: ${dockerInfo.architecture}, CPUs: ${dockerInfo.cpus}, Memory: ${dockerInfo.memory}
|
|
30
|
-
Compose: Installed: ${dockerComposeInfo !== undefined}, Version: ${(_a = dockerComposeInfo === null || dockerComposeInfo === void 0 ? void 0 : dockerComposeInfo.version) !== null && _a !== void 0 ? _a : "N/A"}`);
|
|
27
|
+
logger_1.log.debug(`Node version: ${nodeInfo.version}, Platform: ${nodeInfo.platform}, Arch: ${nodeInfo.architecture}, OS: ${dockerInfo.operatingSystem}, Version: ${dockerInfo.serverVersion}, Arch: ${dockerInfo.architecture}, CPUs: ${dockerInfo.cpus}, Memory: ${dockerInfo.memory}, Compose installed: ${dockerComposeInfo !== undefined}, Compose version: ${(_a = dockerComposeInfo === null || dockerComposeInfo === void 0 ? void 0 : dockerComposeInfo.version) !== null && _a !== void 0 ? _a : "N/A"}`);
|
|
31
28
|
return systemInfo;
|
|
32
29
|
});
|
|
33
30
|
exports.getSystemInfo = getSystemInfo;
|
|
@@ -30,6 +30,7 @@ export interface TestContainer {
|
|
|
30
30
|
withCopyContentToContainer(contentsToCopy: ContentToCopy[]): this;
|
|
31
31
|
withWorkingDir(workingDir: string): this;
|
|
32
32
|
withResourcesQuota(resourcesQuota: ResourcesQuota): this;
|
|
33
|
+
withLogConsumer(logConsumer: (stream: Readable) => unknown): this;
|
|
33
34
|
}
|
|
34
35
|
export interface RestartOptions {
|
|
35
36
|
timeout: number;
|
|
@@ -42,11 +43,6 @@ export interface DockerComposeDownOptions {
|
|
|
42
43
|
timeout: number;
|
|
43
44
|
removeVolumes: boolean;
|
|
44
45
|
}
|
|
45
|
-
export interface ExecOptions {
|
|
46
|
-
tty: boolean;
|
|
47
|
-
detach: boolean;
|
|
48
|
-
stdin: boolean;
|
|
49
|
-
}
|
|
50
46
|
export interface StartedTestContainer {
|
|
51
47
|
stop(options?: Partial<StopOptions>): Promise<StoppedTestContainer>;
|
|
52
48
|
restart(options?: Partial<RestartOptions>): Promise<void>;
|
|
@@ -59,7 +55,7 @@ export interface StartedTestContainer {
|
|
|
59
55
|
getNetworkNames(): string[];
|
|
60
56
|
getNetworkId(networkName: string): string;
|
|
61
57
|
getIpAddress(networkName: string): string;
|
|
62
|
-
exec(command: string[]
|
|
58
|
+
exec(command: string[]): Promise<ExecResult>;
|
|
63
59
|
logs(): Promise<Readable>;
|
|
64
60
|
}
|
|
65
61
|
export interface StoppedTestContainer {
|
package/dist/src/uuid.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.RandomUuid = void 0;
|
|
|
7
7
|
const crypto_1 = __importDefault(require("crypto"));
|
|
8
8
|
class RandomUuid {
|
|
9
9
|
nextUuid() {
|
|
10
|
-
return crypto_1.default.randomUUID();
|
|
10
|
+
return crypto_1.default.createHash("md5").update(crypto_1.default.randomUUID()).digest("hex").substring(0, 12);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
exports.RandomUuid = RandomUuid;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import Dockerode from "dockerode";
|
|
2
2
|
import { BoundPorts } from "./bound-ports";
|
|
3
3
|
import { WaitStrategy } from "./wait-strategy/wait-strategy";
|
|
4
|
-
export declare const waitForContainer: (container: Dockerode.Container, waitStrategy: WaitStrategy,
|
|
4
|
+
export declare const waitForContainer: (container: Dockerode.Container, waitStrategy: WaitStrategy, boundPorts: BoundPorts, startTime?: Date) => Promise<void>;
|
|
@@ -13,20 +13,20 @@ exports.waitForContainer = void 0;
|
|
|
13
13
|
const logger_1 = require("./logger");
|
|
14
14
|
const stop_container_1 = require("./docker/functions/container/stop-container");
|
|
15
15
|
const remove_container_1 = require("./docker/functions/container/remove-container");
|
|
16
|
-
const waitForContainer = (container, waitStrategy,
|
|
17
|
-
logger_1.log.debug(`Waiting for container to be ready:
|
|
16
|
+
const waitForContainer = (container, waitStrategy, boundPorts, startTime) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
|
+
logger_1.log.debug(`Waiting for container to be ready...`, { containerId: container.id });
|
|
18
18
|
try {
|
|
19
|
-
yield waitStrategy.waitUntilReady(container,
|
|
20
|
-
logger_1.log.info(`Container is ready:
|
|
19
|
+
yield waitStrategy.waitUntilReady(container, boundPorts, startTime);
|
|
20
|
+
logger_1.log.info(`Container is ready`, { containerId: container.id });
|
|
21
21
|
}
|
|
22
22
|
catch (err) {
|
|
23
|
-
logger_1.log.error(`Container failed to be ready: ${container.id
|
|
23
|
+
logger_1.log.error(`Container failed to be ready: ${err}`, { containerId: container.id });
|
|
24
24
|
try {
|
|
25
25
|
yield (0, stop_container_1.stopContainer)(container, { timeout: 0 });
|
|
26
26
|
yield (0, remove_container_1.removeContainer)(container, { removeVolumes: true });
|
|
27
27
|
}
|
|
28
28
|
catch (stopErr) {
|
|
29
|
-
logger_1.log.error(`Failed to stop container after it failed to be ready: ${container.id
|
|
29
|
+
logger_1.log.error(`Failed to stop container after it failed to be ready: ${stopErr}`, { containerId: container.id });
|
|
30
30
|
}
|
|
31
31
|
throw err;
|
|
32
32
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AbstractWaitStrategy, WaitStrategy } from "./wait-strategy";
|
|
2
|
+
import Dockerode from "dockerode";
|
|
3
|
+
import { BoundPorts } from "../bound-ports";
|
|
4
|
+
export declare class CompositeWaitStrategy extends AbstractWaitStrategy {
|
|
5
|
+
private readonly waitStrategies;
|
|
6
|
+
private deadline?;
|
|
7
|
+
constructor(waitStrategies: WaitStrategy[]);
|
|
8
|
+
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void>;
|
|
9
|
+
withStartupTimeout(startupTimeout: number): this;
|
|
10
|
+
withDeadline(deadline: number): this;
|
|
11
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.CompositeWaitStrategy = void 0;
|
|
13
|
+
const wait_strategy_1 = require("./wait-strategy");
|
|
14
|
+
const logger_1 = require("../logger");
|
|
15
|
+
class CompositeWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
16
|
+
constructor(waitStrategies) {
|
|
17
|
+
super();
|
|
18
|
+
this.waitStrategies = waitStrategies;
|
|
19
|
+
}
|
|
20
|
+
waitUntilReady(container, boundPorts, startTime) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
logger_1.log.debug(`Waiting for composite...`, { containerId: container.id });
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
let deadlineTimeout;
|
|
25
|
+
if (this.deadline !== undefined) {
|
|
26
|
+
deadlineTimeout = setTimeout(() => {
|
|
27
|
+
const message = `Composite wait strategy not successful after ${this.deadline}ms`;
|
|
28
|
+
logger_1.log.error(message, { containerId: container.id });
|
|
29
|
+
reject(new Error(message));
|
|
30
|
+
}, this.deadline);
|
|
31
|
+
}
|
|
32
|
+
Promise.all(this.waitStrategies.map((waitStrategy) => waitStrategy.waitUntilReady(container, boundPorts, startTime)))
|
|
33
|
+
.then(() => resolve())
|
|
34
|
+
.catch((err) => reject(err))
|
|
35
|
+
.finally(() => {
|
|
36
|
+
if (deadlineTimeout) {
|
|
37
|
+
clearTimeout(deadlineTimeout);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
withStartupTimeout(startupTimeout) {
|
|
44
|
+
this.waitStrategies
|
|
45
|
+
.filter((waitStrategy) => !waitStrategy.isStartupTimeoutSet())
|
|
46
|
+
.forEach((waitStrategy) => waitStrategy.withStartupTimeout(startupTimeout));
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
withDeadline(deadline) {
|
|
50
|
+
this.deadline = deadline;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.CompositeWaitStrategy = CompositeWaitStrategy;
|
|
@@ -17,13 +17,17 @@ const wait_strategy_1 = require("./wait-strategy");
|
|
|
17
17
|
class HealthCheckWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
18
18
|
waitUntilReady(container) {
|
|
19
19
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
-
logger_1.log.debug(`Waiting for health check
|
|
20
|
+
logger_1.log.debug(`Waiting for health check...`, { containerId: container.id });
|
|
21
21
|
const status = yield new retry_strategy_1.IntervalRetryStrategy(100).retryUntil(() => __awaiter(this, void 0, void 0, function* () { return (yield (0, inspect_container_1.inspectContainer)(container)).healthCheckStatus; }), (healthCheckStatus) => healthCheckStatus === "healthy" || healthCheckStatus === "unhealthy", () => {
|
|
22
22
|
const timeout = this.startupTimeout;
|
|
23
|
-
|
|
23
|
+
const message = `Health check not healthy after ${timeout}ms`;
|
|
24
|
+
logger_1.log.error(message, { containerId: container.id });
|
|
25
|
+
throw new Error(message);
|
|
24
26
|
}, this.startupTimeout);
|
|
25
27
|
if (status !== "healthy") {
|
|
26
|
-
|
|
28
|
+
const message = `Health check failed: ${status}`;
|
|
29
|
+
logger_1.log.error(message, { containerId: container.id });
|
|
30
|
+
throw new Error(message);
|
|
27
31
|
}
|
|
28
32
|
});
|
|
29
33
|
}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { PortCheck } from "../port-check";
|
|
2
1
|
import Dockerode from "dockerode";
|
|
3
2
|
import { BoundPorts } from "../bound-ports";
|
|
4
3
|
import { AbstractWaitStrategy } from "./wait-strategy";
|
|
5
4
|
export declare class HostPortWaitStrategy extends AbstractWaitStrategy {
|
|
6
|
-
|
|
7
|
-
private readonly internalPortCheck;
|
|
8
|
-
constructor(hostPortCheck: PortCheck, internalPortCheck: PortCheck);
|
|
9
|
-
waitUntilReady(container: Dockerode.Container, host: string, boundPorts: BoundPorts): Promise<void>;
|
|
5
|
+
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts): Promise<void>;
|
|
10
6
|
private waitForHostPorts;
|
|
11
7
|
private waitForInternalPorts;
|
|
12
8
|
private waitForPort;
|
|
@@ -10,43 +10,47 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.HostPortWaitStrategy = void 0;
|
|
13
|
+
const port_check_1 = require("../port-check");
|
|
13
14
|
const logger_1 = require("../logger");
|
|
14
15
|
const retry_strategy_1 = require("../retry-strategy");
|
|
15
16
|
const wait_strategy_1 = require("./wait-strategy");
|
|
17
|
+
const docker_client_1 = require("../docker/docker-client");
|
|
16
18
|
class HostPortWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
17
|
-
|
|
18
|
-
super();
|
|
19
|
-
this.hostPortCheck = hostPortCheck;
|
|
20
|
-
this.internalPortCheck = internalPortCheck;
|
|
21
|
-
}
|
|
22
|
-
waitUntilReady(container, host, boundPorts) {
|
|
19
|
+
waitUntilReady(container, boundPorts) {
|
|
23
20
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
-
|
|
21
|
+
const { dockerode, provider, host } = yield (0, docker_client_1.dockerClient)();
|
|
22
|
+
const hostPortCheck = new port_check_1.HostPortCheck(host);
|
|
23
|
+
const internalPortCheck = new port_check_1.InternalPortCheck(dockerode, provider, container);
|
|
24
|
+
yield Promise.all([
|
|
25
|
+
this.waitForHostPorts(hostPortCheck, container, boundPorts),
|
|
26
|
+
this.waitForInternalPorts(internalPortCheck, container, boundPorts),
|
|
27
|
+
]);
|
|
25
28
|
});
|
|
26
29
|
}
|
|
27
|
-
waitForHostPorts(container, boundPorts) {
|
|
30
|
+
waitForHostPorts(portCheck, container, boundPorts) {
|
|
28
31
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29
32
|
for (const [, hostPort] of boundPorts.iterator()) {
|
|
30
|
-
logger_1.log.debug(`Waiting for host port ${hostPort}
|
|
31
|
-
yield this.waitForPort(container, hostPort,
|
|
32
|
-
logger_1.log.debug(`Host port ${hostPort} ready
|
|
33
|
+
logger_1.log.debug(`Waiting for host port ${hostPort}...`, { containerId: container.id });
|
|
34
|
+
yield this.waitForPort(container, hostPort, portCheck);
|
|
35
|
+
logger_1.log.debug(`Host port ${hostPort} ready`, { containerId: container.id });
|
|
33
36
|
}
|
|
34
37
|
});
|
|
35
38
|
}
|
|
36
|
-
waitForInternalPorts(container, boundPorts) {
|
|
39
|
+
waitForInternalPorts(portCheck, container, boundPorts) {
|
|
37
40
|
return __awaiter(this, void 0, void 0, function* () {
|
|
38
41
|
for (const [internalPort] of boundPorts.iterator()) {
|
|
39
|
-
logger_1.log.debug(`Waiting for internal port ${internalPort}
|
|
40
|
-
yield this.waitForPort(container, internalPort,
|
|
41
|
-
logger_1.log.debug(`Internal port ${internalPort} ready
|
|
42
|
+
logger_1.log.debug(`Waiting for internal port ${internalPort}...`, { containerId: container.id });
|
|
43
|
+
yield this.waitForPort(container, internalPort, portCheck);
|
|
44
|
+
logger_1.log.debug(`Internal port ${internalPort} ready`, { containerId: container.id });
|
|
42
45
|
}
|
|
43
46
|
});
|
|
44
47
|
}
|
|
45
48
|
waitForPort(container, port, portCheck) {
|
|
46
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
47
50
|
yield new retry_strategy_1.IntervalRetryStrategy(100).retryUntil(() => portCheck.isBound(port), (isBound) => isBound, () => {
|
|
48
|
-
const
|
|
49
|
-
|
|
51
|
+
const message = `Port ${port} not bound after ${this.startupTimeout}ms`;
|
|
52
|
+
logger_1.log.error(message, { containerId: container.id });
|
|
53
|
+
throw new Error(message);
|
|
50
54
|
}, this.startupTimeout);
|
|
51
55
|
});
|
|
52
56
|
}
|
|
@@ -22,6 +22,6 @@ export declare class HttpWaitStrategy extends AbstractWaitStrategy {
|
|
|
22
22
|
withReadTimeout(readTimeout: number): HttpWaitStrategy;
|
|
23
23
|
usingTls(): HttpWaitStrategy;
|
|
24
24
|
allowInsecure(): HttpWaitStrategy;
|
|
25
|
-
waitUntilReady(container: Dockerode.Container,
|
|
25
|
+
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts): Promise<void>;
|
|
26
26
|
private getAgent;
|
|
27
27
|
}
|
|
@@ -17,6 +17,8 @@ const wait_strategy_1 = require("./wait-strategy");
|
|
|
17
17
|
const retry_strategy_1 = require("../retry-strategy");
|
|
18
18
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
19
19
|
const https_1 = __importDefault(require("https"));
|
|
20
|
+
const docker_client_1 = require("../docker/docker-client");
|
|
21
|
+
const logger_1 = require("../logger");
|
|
20
22
|
class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
21
23
|
constructor(path, port) {
|
|
22
24
|
super();
|
|
@@ -66,8 +68,10 @@ class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
|
66
68
|
this._allowInsecure = true;
|
|
67
69
|
return this;
|
|
68
70
|
}
|
|
69
|
-
waitUntilReady(container,
|
|
71
|
+
waitUntilReady(container, boundPorts) {
|
|
70
72
|
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
logger_1.log.debug(`Waiting for HTTP...`, { containerId: container.id });
|
|
74
|
+
const { host } = yield (0, docker_client_1.dockerClient)();
|
|
71
75
|
yield new retry_strategy_1.IntervalRetryStrategy(this.readTimeout).retryUntil(() => __awaiter(this, void 0, void 0, function* () {
|
|
72
76
|
try {
|
|
73
77
|
const url = `${this.protocol}://${host}:${boundPorts.getBinding(this.port)}${this.path}`;
|
|
@@ -98,7 +102,9 @@ class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
|
98
102
|
return true;
|
|
99
103
|
}
|
|
100
104
|
}), () => {
|
|
101
|
-
|
|
105
|
+
const message = `URL ${this.path} not accessible after ${this.startupTimeout}ms`;
|
|
106
|
+
logger_1.log.error(message, { containerId: container.id });
|
|
107
|
+
throw new Error(message);
|
|
102
108
|
}, this.startupTimeout);
|
|
103
109
|
});
|
|
104
110
|
}
|
|
@@ -6,5 +6,5 @@ export declare class LogWaitStrategy extends AbstractWaitStrategy {
|
|
|
6
6
|
private readonly message;
|
|
7
7
|
private readonly times;
|
|
8
8
|
constructor(message: Log | RegExp, times: number);
|
|
9
|
-
waitUntilReady(container: Dockerode.Container,
|
|
9
|
+
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void>;
|
|
10
10
|
}
|
|
@@ -23,17 +23,16 @@ class LogWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
|
23
23
|
this.message = message;
|
|
24
24
|
this.times = times;
|
|
25
25
|
}
|
|
26
|
-
waitUntilReady(container,
|
|
26
|
+
waitUntilReady(container, boundPorts, startTime) {
|
|
27
27
|
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
-
logger_1.log.debug(`Waiting for log message "${this.message}"
|
|
28
|
+
logger_1.log.debug(`Waiting for log message "${this.message}"...`, { containerId: container.id });
|
|
29
29
|
const stream = yield (0, container_logs_1.containerLogs)(container, { since: startTime });
|
|
30
30
|
return new Promise((resolve, reject) => {
|
|
31
|
-
const startupTimeout = this.startupTimeout;
|
|
32
31
|
const timeout = setTimeout(() => {
|
|
33
|
-
const message = `Log message "${this.message}" not received after ${startupTimeout}ms
|
|
34
|
-
logger_1.log.error(message);
|
|
32
|
+
const message = `Log message "${this.message}" not received after ${this.startupTimeout}ms`;
|
|
33
|
+
logger_1.log.error(message, { containerId: container.id });
|
|
35
34
|
reject(new Error(message));
|
|
36
|
-
}, startupTimeout);
|
|
35
|
+
}, this.startupTimeout);
|
|
37
36
|
const comparisonFn = (line) => {
|
|
38
37
|
if (this.message instanceof RegExp) {
|
|
39
38
|
return this.message.test(line);
|
|
@@ -58,7 +57,9 @@ class LogWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
|
58
57
|
.on("end", () => {
|
|
59
58
|
stream.destroy();
|
|
60
59
|
clearTimeout(timeout);
|
|
61
|
-
|
|
60
|
+
const message = `Log stream ended and message "${this.message}" was not received`;
|
|
61
|
+
logger_1.log.error(message, { containerId: container.id });
|
|
62
|
+
reject(new Error(message));
|
|
62
63
|
});
|
|
63
64
|
});
|
|
64
65
|
});
|