testcontainers 9.1.2 → 9.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/docker/functions/container/container-logs.d.ts +1 -1
- package/dist/docker/functions/container/container-logs.js +2 -2
- package/dist/docker/functions/container/inspect-container.d.ts +2 -0
- package/dist/docker/functions/container/inspect-container.js +7 -1
- package/dist/generic-container/generic-container.d.ts +1 -1
- package/dist/generic-container/generic-container.js +18 -11
- package/dist/http-wait-strategy.js +9 -3
- package/dist/retry-strategy.d.ts +3 -3
- package/dist/retry-strategy.js +1 -1
- package/dist/test-container.d.ts +1 -1
- package/dist/wait-strategy.d.ts +2 -0
- package/dist/wait-strategy.js +17 -1
- package/package.json +14 -12
package/README.md
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import Dockerode from "dockerode";
|
|
2
2
|
import { Readable } from "stream";
|
|
3
|
-
export declare const containerLogs: (container: Dockerode.Container) => Promise<Readable>;
|
|
3
|
+
export declare const containerLogs: (container: Dockerode.Container, options?: Omit<Dockerode.ContainerLogsOptions, "follow" | "stdout" | "stderr">) => Promise<Readable>;
|
|
@@ -13,9 +13,9 @@ exports.containerLogs = void 0;
|
|
|
13
13
|
const logger_1 = require("../../../logger");
|
|
14
14
|
const demux_stream_1 = require("../demux-stream");
|
|
15
15
|
const docker_client_1 = require("../../docker-client");
|
|
16
|
-
const containerLogs = (container) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
const containerLogs = (container, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
17
|
try {
|
|
18
|
-
const stream = (yield container.logs({ follow: true, stdout: true, stderr: true }));
|
|
18
|
+
const stream = (yield container.logs(Object.assign({ follow: true, stdout: true, stderr: true }, options)));
|
|
19
19
|
stream.socket.unref();
|
|
20
20
|
return (0, demux_stream_1.demuxStream)((yield (0, docker_client_1.dockerClient)()).dockerode, stream);
|
|
21
21
|
}
|
|
@@ -14,13 +14,19 @@ const logger_1 = require("../../../logger");
|
|
|
14
14
|
const inspectContainer = (container) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
15
|
try {
|
|
16
16
|
const inspectResult = yield container.inspect();
|
|
17
|
+
const finishedAt = new Date(inspectResult.State.FinishedAt);
|
|
17
18
|
return {
|
|
18
19
|
name: inspectResult.Name,
|
|
19
20
|
hostname: inspectResult.Config.Hostname,
|
|
20
21
|
ports: getPorts(inspectResult),
|
|
21
22
|
healthCheckStatus: getHealthCheckStatus(inspectResult),
|
|
22
23
|
networkSettings: getNetworkSettings(inspectResult),
|
|
23
|
-
state: {
|
|
24
|
+
state: {
|
|
25
|
+
status: inspectResult.State.Status,
|
|
26
|
+
running: inspectResult.State.Running,
|
|
27
|
+
startedAt: new Date(inspectResult.State.StartedAt),
|
|
28
|
+
finishedAt: finishedAt.getTime() < 0 ? undefined : finishedAt,
|
|
29
|
+
},
|
|
24
30
|
labels: inspectResult.Config.Labels,
|
|
25
31
|
};
|
|
26
32
|
}
|
|
@@ -59,7 +59,7 @@ export declare class GenericContainer implements TestContainer {
|
|
|
59
59
|
withExposedPorts(...ports: PortWithOptionalBinding[]): this;
|
|
60
60
|
withBindMounts(bindMounts: BindMount[]): this;
|
|
61
61
|
withHealthCheck(healthCheck: HealthCheck): this;
|
|
62
|
-
withStartupTimeout(
|
|
62
|
+
withStartupTimeout(startupTimeoutMs: number): this;
|
|
63
63
|
withWaitStrategy(waitStrategy: WaitStrategy): this;
|
|
64
64
|
withDefaultLogDriver(): this;
|
|
65
65
|
withPrivilegedMode(): this;
|
|
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.GenericContainer = void 0;
|
|
16
16
|
const archiver_1 = __importDefault(require("archiver"));
|
|
17
|
+
const async_lock_1 = __importDefault(require("async-lock"));
|
|
17
18
|
const bound_ports_1 = require("../bound-ports");
|
|
18
19
|
const logger_1 = require("../logger");
|
|
19
20
|
const pull_policy_1 = require("../pull-policy");
|
|
@@ -36,6 +37,7 @@ const hash_1 = require("../hash");
|
|
|
36
37
|
const get_container_1 = require("../docker/functions/container/get-container");
|
|
37
38
|
const labels_1 = require("../labels");
|
|
38
39
|
const wait_for_container_1 = require("../wait-for-container");
|
|
40
|
+
const reusableContainerCreationLock = new async_lock_1.default();
|
|
39
41
|
class GenericContainer {
|
|
40
42
|
constructor(image) {
|
|
41
43
|
this.image = image;
|
|
@@ -101,15 +103,20 @@ class GenericContainer {
|
|
|
101
103
|
const containerHash = (0, hash_1.hash)(JSON.stringify(createContainerOptions));
|
|
102
104
|
createContainerOptions.labels = Object.assign(Object.assign({}, createContainerOptions.labels), { [labels_1.LABEL_CONTAINER_HASH]: containerHash });
|
|
103
105
|
logger_1.log.debug(`Container reuse has been enabled, hash: ${containerHash}`);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
// We might have several async processes try to create a reusable container
|
|
107
|
+
// at once, to avoid possibly creating too many of these, use a lock
|
|
108
|
+
// on the containerHash, this ensures that only single reusable instance is created
|
|
109
|
+
return reusableContainerCreationLock.acquire(containerHash, () => __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
const container = yield (0, get_container_1.getContainerByHash)(containerHash);
|
|
111
|
+
if (container !== undefined) {
|
|
112
|
+
logger_1.log.debug(`Found container to reuse with hash: ${containerHash}`);
|
|
113
|
+
return this.reuseContainer(container);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
logger_1.log.debug("No container found to reuse");
|
|
117
|
+
return this.startContainer(createContainerOptions);
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
113
120
|
}
|
|
114
121
|
else {
|
|
115
122
|
return this.startContainer(createContainerOptions);
|
|
@@ -240,8 +247,8 @@ class GenericContainer {
|
|
|
240
247
|
this.healthCheck = healthCheck;
|
|
241
248
|
return this;
|
|
242
249
|
}
|
|
243
|
-
withStartupTimeout(
|
|
244
|
-
this.startupTimeout =
|
|
250
|
+
withStartupTimeout(startupTimeoutMs) {
|
|
251
|
+
this.startupTimeout = startupTimeoutMs;
|
|
245
252
|
return this;
|
|
246
253
|
}
|
|
247
254
|
withWaitStrategy(waitStrategy) {
|
|
@@ -81,7 +81,7 @@ class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
|
81
81
|
catch (_a) {
|
|
82
82
|
return undefined;
|
|
83
83
|
}
|
|
84
|
-
}), (response) => {
|
|
84
|
+
}), (response) => __awaiter(this, void 0, void 0, function* () {
|
|
85
85
|
if (response === undefined) {
|
|
86
86
|
return false;
|
|
87
87
|
}
|
|
@@ -89,9 +89,15 @@ class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
|
|
|
89
89
|
return response.ok;
|
|
90
90
|
}
|
|
91
91
|
else {
|
|
92
|
-
|
|
92
|
+
for (const predicate of this.predicates) {
|
|
93
|
+
const result = yield predicate(response);
|
|
94
|
+
if (!result) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
93
99
|
}
|
|
94
|
-
}, () => {
|
|
100
|
+
}), () => {
|
|
95
101
|
throw new Error(`URL ${this.path} not accessible after ${this.startupTimeout}ms for ${container.id}`);
|
|
96
102
|
}, this.startupTimeout);
|
|
97
103
|
});
|
package/dist/retry-strategy.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { Clock, Time } from "./clock";
|
|
2
2
|
export interface RetryStrategy<T, U> {
|
|
3
|
-
retryUntil(fn: () => Promise<T>, predicate: (result: T) => boolean
|
|
3
|
+
retryUntil(fn: () => Promise<T>, predicate: (result: T) => boolean | Promise<boolean>, onTimeout: () => U, timeout: number): Promise<T | U>;
|
|
4
4
|
}
|
|
5
5
|
declare abstract class AbstractRetryStrategy<T, U> implements RetryStrategy<T, U> {
|
|
6
6
|
protected readonly clock: Clock;
|
|
7
7
|
protected constructor(clock?: Clock);
|
|
8
|
-
abstract retryUntil(fn: () => Promise<T>, predicate: (result: T) => boolean
|
|
8
|
+
abstract retryUntil(fn: () => Promise<T>, predicate: (result: T) => boolean | Promise<boolean>, onTimeout: () => U, timeout: number): Promise<T | U>;
|
|
9
9
|
protected hasTimedOut(timeout: number, startTime: Time): boolean;
|
|
10
10
|
protected wait(duration: number): Promise<void>;
|
|
11
11
|
}
|
|
12
12
|
export declare class IntervalRetryStrategy<T, U> extends AbstractRetryStrategy<T, U> {
|
|
13
13
|
private readonly interval;
|
|
14
14
|
constructor(interval: number);
|
|
15
|
-
retryUntil(fn: (attempt: number) => Promise<T>, predicate: (result: T) => boolean
|
|
15
|
+
retryUntil(fn: (attempt: number) => Promise<T>, predicate: (result: T) => boolean | Promise<boolean>, onTimeout: () => U, timeout: number): Promise<T | U>;
|
|
16
16
|
}
|
|
17
17
|
export {};
|
package/dist/retry-strategy.js
CHANGED
|
@@ -32,7 +32,7 @@ class IntervalRetryStrategy extends AbstractRetryStrategy {
|
|
|
32
32
|
const startTime = this.clock.getTime();
|
|
33
33
|
let attemptNumber = 0;
|
|
34
34
|
let result = yield fn(attemptNumber++);
|
|
35
|
-
while (!predicate(result)) {
|
|
35
|
+
while (!(yield predicate(result))) {
|
|
36
36
|
if (this.hasTimedOut(timeout, startTime)) {
|
|
37
37
|
return onTimeout();
|
|
38
38
|
}
|
package/dist/test-container.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface TestContainer {
|
|
|
17
17
|
withExposedPorts(...ports: PortWithOptionalBinding[]): this;
|
|
18
18
|
withBindMounts(bindMounts: BindMount[]): this;
|
|
19
19
|
withWaitStrategy(waitStrategy: WaitStrategy): this;
|
|
20
|
-
withStartupTimeout(
|
|
20
|
+
withStartupTimeout(startupTimeoutMs: number): this;
|
|
21
21
|
withNetwork(network: StartedNetwork): this;
|
|
22
22
|
withNetworkMode(networkMode: string): this;
|
|
23
23
|
withExtraHosts(extraHosts: ExtraHost[]): this;
|
package/dist/wait-strategy.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export declare type Log = string;
|
|
|
23
23
|
export declare class LogWaitStrategy extends AbstractWaitStrategy {
|
|
24
24
|
private readonly message;
|
|
25
25
|
constructor(message: Log | RegExp);
|
|
26
|
+
private containerRestartedLogOptions;
|
|
27
|
+
private hasContainerRestarted;
|
|
26
28
|
waitUntilReady(container: Dockerode.Container): Promise<void>;
|
|
27
29
|
}
|
|
28
30
|
export declare class HealthCheckWaitStrategy extends AbstractWaitStrategy {
|
package/dist/wait-strategy.js
CHANGED
|
@@ -74,10 +74,26 @@ class LogWaitStrategy extends AbstractWaitStrategy {
|
|
|
74
74
|
super();
|
|
75
75
|
this.message = message;
|
|
76
76
|
}
|
|
77
|
+
containerRestartedLogOptions(inspectResult) {
|
|
78
|
+
const { finishedAt } = inspectResult.state;
|
|
79
|
+
if (finishedAt === undefined) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
since: finishedAt.getTime() / 1000,
|
|
84
|
+
timestamps: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
hasContainerRestarted(inspectResult) {
|
|
88
|
+
const { finishedAt, status } = inspectResult.state;
|
|
89
|
+
return finishedAt !== undefined && status === "running";
|
|
90
|
+
}
|
|
77
91
|
waitUntilReady(container) {
|
|
78
92
|
return __awaiter(this, void 0, void 0, function* () {
|
|
79
93
|
logger_1.log.debug(`Waiting for log message "${this.message}"`);
|
|
80
|
-
const
|
|
94
|
+
const inspect = yield (0, inspect_container_1.inspectContainer)(container);
|
|
95
|
+
const logsOptions = this.hasContainerRestarted(inspect) ? this.containerRestartedLogOptions(inspect) : undefined;
|
|
96
|
+
const stream = yield (0, container_logs_1.containerLogs)(container, logsOptions);
|
|
81
97
|
return new Promise((resolve, reject) => {
|
|
82
98
|
const startupTimeout = this.startupTimeout;
|
|
83
99
|
const timeout = setTimeout(() => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testcontainers",
|
|
3
3
|
"author": "Cristian Greco",
|
|
4
|
-
"version": "9.1.
|
|
4
|
+
"version": "9.1.3",
|
|
5
5
|
"main": "dist/index",
|
|
6
6
|
"types": "dist/index",
|
|
7
7
|
"keywords": [
|
|
@@ -35,26 +35,28 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@balena/dockerignore": "^1.0.2",
|
|
37
37
|
"@types/archiver": "^5.3.1",
|
|
38
|
-
"@types/dockerode": "^3.3.
|
|
38
|
+
"@types/dockerode": "^3.3.14",
|
|
39
39
|
"archiver": "^5.3.1",
|
|
40
|
+
"async-lock": "^1.4.0",
|
|
40
41
|
"byline": "^5.0.0",
|
|
41
42
|
"debug": "^4.3.4",
|
|
42
|
-
"docker-compose": "^0.23.
|
|
43
|
+
"docker-compose": "^0.23.19",
|
|
43
44
|
"dockerode": "^3.3.4",
|
|
44
45
|
"get-port": "^5.1.1",
|
|
45
|
-
"node-fetch": "^2.6.
|
|
46
|
+
"node-fetch": "^2.6.9",
|
|
46
47
|
"properties-reader": "^2.2.0",
|
|
47
48
|
"ssh-remote-port-forward": "^1.0.4",
|
|
48
49
|
"tar-fs": "^2.1.1"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@elastic/elasticsearch": "^7.17.0",
|
|
53
|
+
"@types/async-lock": "^1.4.0",
|
|
52
54
|
"@types/byline": "^4.2.33",
|
|
53
55
|
"@types/debug": "4.1.7",
|
|
54
|
-
"@types/jest": "^29.2.
|
|
55
|
-
"@types/node": "^18.11.
|
|
56
|
+
"@types/jest": "^29.2.6",
|
|
57
|
+
"@types/node": "^18.11.19",
|
|
56
58
|
"@types/node-fetch": "^2.6.2",
|
|
57
|
-
"@types/pg": "^8.6.
|
|
59
|
+
"@types/pg": "^8.6.6",
|
|
58
60
|
"@types/properties-reader": "^2.1.1",
|
|
59
61
|
"@types/tar-fs": "^2.0.1",
|
|
60
62
|
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
|
@@ -63,18 +65,18 @@
|
|
|
63
65
|
"cross-env": "^7.0.3",
|
|
64
66
|
"eslint": "^8.26.0",
|
|
65
67
|
"eslint-config-prettier": "^8.5.0",
|
|
66
|
-
"husky": "^8.0.
|
|
68
|
+
"husky": "^8.0.3",
|
|
67
69
|
"jest": "^29.2.2",
|
|
68
|
-
"kafkajs": "^2.2.
|
|
69
|
-
"lint-staged": "^13.0.
|
|
70
|
+
"kafkajs": "^2.2.3",
|
|
71
|
+
"lint-staged": "^13.0.4",
|
|
72
|
+
"mongoose": "^6.7.5",
|
|
70
73
|
"mysql2": "^2.3.3",
|
|
71
|
-
"mongoose": "^6.7.0",
|
|
72
74
|
"nats": "^2.8.0",
|
|
73
75
|
"neo4j-driver": "^5.2.0",
|
|
74
76
|
"pg": "^8.8.0",
|
|
75
77
|
"prettier": "^2.7.1",
|
|
76
78
|
"rimraf": "^3.0.2",
|
|
77
|
-
"ts-jest": "^29.0.
|
|
79
|
+
"ts-jest": "^29.0.5",
|
|
78
80
|
"ts-node": "^10.9.1",
|
|
79
81
|
"typescript": "^4.8.4",
|
|
80
82
|
"wait-for-expect": "^3.0.2"
|