testcontainers 7.23.0 → 8.0.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/README.md +38 -12
- package/dist/bound-ports.d.ts +3 -0
- package/dist/bound-ports.js +14 -0
- package/dist/docker/dockerode.js +2 -2
- package/dist/docker/functions/container/create-container.d.ts +2 -2
- package/dist/docker/functions/container/create-container.js +8 -8
- package/dist/docker/functions/container/exec-container.js +16 -12
- package/dist/docker/functions/container/inspect-container.d.ts +3 -4
- package/dist/docker/functions/container/inspect-container.js +10 -8
- package/dist/docker/functions/image/build-image.js +1 -1
- package/dist/docker/get-docker-host.js +9 -5
- package/dist/docker/types.d.ts +3 -0
- package/dist/docker-compose/default-docker-compose-options.d.ts +1 -1
- package/dist/docker-compose/docker-compose.d.ts +2 -0
- package/dist/docker-compose/docker-compose.js +11 -0
- package/dist/docker-compose/functions/docker-compose-down.js +1 -1
- package/dist/docker-compose/functions/docker-compose-stop.js +1 -1
- package/dist/docker-compose/functions/docker-compose-up.js +1 -1
- package/dist/docker-compose-environment/docker-compose-environment.d.ts +0 -1
- package/dist/docker-compose-environment/docker-compose-environment.js +3 -11
- package/dist/docker-ignore.d.ts +1 -1
- package/dist/docker-ignore.js +2 -1
- package/dist/generic-container/generic-container.d.ts +6 -2
- package/dist/generic-container/generic-container.js +23 -7
- package/dist/log-system-diagnostics.js +2 -21
- package/dist/modules/kafka/kafka-container.d.ts +6 -17
- package/dist/modules/kafka/kafka-container.js +56 -68
- package/dist/reaper.d.ts +1 -0
- package/dist/reaper.js +18 -12
- package/dist/test-container.d.ts +2 -1
- package/dist/testcontainers-properties-file.d.ts +1 -0
- package/dist/testcontainers-properties-file.js +35 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
[](https://github.com/testcontainers/testcontainers-node/actions/workflows/node.js.yml)
|
|
6
6
|
[](https://www.npmjs.com/package/testcontainers)
|
|
7
7
|
[](https://www.npmjs.com/package/testcontainers)
|
|
8
|
+
[](https://foresight.thundra.live/testRuns/57175ecb-d5ac-4129-957a-9dd617fd27d0)
|
|
9
|
+
[](https://foresight.thundra.live/testruns/57175ecb-d5ac-4129-957a-9dd617fd27d0)
|
|
8
10
|
|
|
9
11
|
## Install
|
|
10
12
|
|
|
@@ -16,12 +18,18 @@ npm i -D testcontainers
|
|
|
16
18
|
|
|
17
19
|
The following environment variables are supported:
|
|
18
20
|
|
|
19
|
-
- `DEBUG=testcontainers` See
|
|
20
|
-
- `DEBUG=testcontainers:containers` See container
|
|
21
|
-
- `DEBUG=testcontainers*` See all
|
|
22
|
-
- `DOCKER_HOST=tcp://docker:2375`
|
|
21
|
+
- `DEBUG=testcontainers` See testcontainers logs
|
|
22
|
+
- `DEBUG=testcontainers:containers` See container logs
|
|
23
|
+
- `DEBUG=testcontainers*` See all logs
|
|
24
|
+
- `DOCKER_HOST=tcp://docker:2375` Sets the URL of the docker daemon
|
|
25
|
+
- `DOCKER_TLS_VERIFY=1` When set to anything other than an empty string, enables TLS communication with the docker
|
|
26
|
+
daemon
|
|
27
|
+
- `DOCKER_CERT_PATH=/some/path` Configures the path to the ca.pem, cert.pem, and key.pem files used for TLS
|
|
28
|
+
verification. Defaults to ~/.docker
|
|
23
29
|
- `TESTCONTAINERS_HOST_OVERRIDE=docker.svc.local` Docker's host on which ports are exposed
|
|
24
|
-
- `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock` Path to Docker's socket. Used by [ryuk](#ryuk) and
|
|
30
|
+
- `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock` Path to Docker's socket. Used by [ryuk](#ryuk) and
|
|
31
|
+
other [auxiliary containers](#auxiliary-containers) that need to perform Docker actions
|
|
32
|
+
- `TESTCONTAINERS_RYUK_PRIVILEGED=true` Run [ryuk](#ryuk) as a privileged container
|
|
25
33
|
- `TESTCONTAINERS_RYUK_DISABLED=true` Disable [ryuk](#ryuk)
|
|
26
34
|
- `RYUK_CONTAINER_IMAGE=registry.mycompany.com/mirror/ryuk:0.3.0` Custom image for [ryuk](#ryuk)
|
|
27
35
|
- `SSHD_CONTAINER_IMAGE=registry.mycompany.com/mirror/sshd:1.0.0` Custom image for [SSHd](#SSHd)
|
|
@@ -136,7 +144,7 @@ Creating a container with a command:
|
|
|
136
144
|
const { GenericContainer } = require("testcontainers");
|
|
137
145
|
|
|
138
146
|
const container = await new GenericContainer("alpine")
|
|
139
|
-
.withCmd(["
|
|
147
|
+
.withCmd(["sleep", "infinity"])
|
|
140
148
|
.start();
|
|
141
149
|
```
|
|
142
150
|
|
|
@@ -146,7 +154,7 @@ Execute commands inside a running container:
|
|
|
146
154
|
const { GenericContainer } = require("testcontainers");
|
|
147
155
|
|
|
148
156
|
const container = await new GenericContainer("alpine")
|
|
149
|
-
.withCmd(["
|
|
157
|
+
.withCmd(["sleep", "infinity"])
|
|
150
158
|
.start();
|
|
151
159
|
|
|
152
160
|
const { output, exitCode } = await container.exec(["echo", "hello", "world"]);
|
|
@@ -263,12 +271,12 @@ const network = await new Network()
|
|
|
263
271
|
.start();
|
|
264
272
|
|
|
265
273
|
const container = await new GenericContainer("alpine")
|
|
266
|
-
.withCmd(["
|
|
274
|
+
.withCmd(["sleep", "infinity"])
|
|
267
275
|
.withNetworkMode(network.getName())
|
|
268
276
|
.start();
|
|
269
277
|
|
|
270
278
|
const fooContainer = await new GenericContainer("alpine")
|
|
271
|
-
.withCmd(["
|
|
279
|
+
.withCmd(["sleep", "infinity"])
|
|
272
280
|
.withNetworkMode(network.getName())
|
|
273
281
|
.withNetworkAliases("foo", "bar")
|
|
274
282
|
.start();
|
|
@@ -276,6 +284,22 @@ const fooContainer = await new GenericContainer("alpine")
|
|
|
276
284
|
expect((await container.exec(["nslookup", "foo"])).exitCode).toBe(0);
|
|
277
285
|
```
|
|
278
286
|
|
|
287
|
+
Add hostname mappings:
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
const { GenericContainer } = require("testcontainers");
|
|
291
|
+
|
|
292
|
+
const container = await new GenericContainer("alpine")
|
|
293
|
+
.withExtraHosts(
|
|
294
|
+
{ host: "foo", ipAddress: "10.11.12.13" },
|
|
295
|
+
{ host: "bar", ipAddress: "11.12.13.14" }
|
|
296
|
+
)
|
|
297
|
+
.start();
|
|
298
|
+
|
|
299
|
+
expect((await container.exec(["nslookup", "foo"])).exitCode).toBe(0);
|
|
300
|
+
expect((await container.exec(["nslookup", "bar"])).exitCode).toBe(0);
|
|
301
|
+
```
|
|
302
|
+
|
|
279
303
|
Specifying a pull policy. Note that if omitted will use the `DefaultPullPolicy` which will use a locally cached image
|
|
280
304
|
if one already exists, this is usually the preferred option. In cases where there is a local image for a given tag
|
|
281
305
|
but the remote image with the same tag may have changed (for example when using the `latest` tag), you can tell
|
|
@@ -401,7 +425,7 @@ server.listen(8000);
|
|
|
401
425
|
await TestContainers.exposeHostPorts(8000);
|
|
402
426
|
|
|
403
427
|
const container = await new GenericContainer("alpine")
|
|
404
|
-
.withCmd(["
|
|
428
|
+
.withCmd(["sleep", "infinity"])
|
|
405
429
|
.start();
|
|
406
430
|
|
|
407
431
|
const { output } = await container.exec(["curl", `http://host.testcontainers.internal:8000`]);
|
|
@@ -634,8 +658,9 @@ Once started, this container keeps track of containers/images/networks/volumes c
|
|
|
634
658
|
automatically clean them up 10s after connectivity with testcontainers is lost. This is useful for example if a test
|
|
635
659
|
starts a container and then terminates unexpectedly, as it will be automatically removed.
|
|
636
660
|
|
|
637
|
-
ryuk
|
|
638
|
-
disabled by setting the environment
|
|
661
|
+
ryuk by default does not run privileged, if necessary this can be overridden by setting the environment variable
|
|
662
|
+
`TESTCONTAINERS_RYUK_PRIVILEGED` to `true`. If necessary, ryuk can be disabled enirely by setting the environment
|
|
663
|
+
variable `TESTCONTAINERS_RYUK_DISABLED` to `true`.
|
|
639
664
|
|
|
640
665
|
### SSHd
|
|
641
666
|
|
|
@@ -665,3 +690,4 @@ depending on file sizes and network constraints. It's unlikely that the default
|
|
|
665
690
|
timeouts set by test frameworks are sufficient.
|
|
666
691
|
|
|
667
692
|
- Increase the test timeout via the methods provided by the testing framework.
|
|
693
|
+
|
package/dist/bound-ports.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Port } from "./port";
|
|
2
|
+
import { InspectResult } from "./docker/functions/container/inspect-container";
|
|
2
3
|
export declare class BoundPorts {
|
|
3
4
|
private readonly ports;
|
|
4
5
|
getBinding(port: Port): Port;
|
|
5
6
|
setBinding(key: Port, value: Port): void;
|
|
6
7
|
iterator(): Iterable<[Port, Port]>;
|
|
8
|
+
filter(ports: Port[]): BoundPorts;
|
|
9
|
+
static fromInspectResult(inspectResult: InspectResult): BoundPorts;
|
|
7
10
|
}
|
package/dist/bound-ports.js
CHANGED
|
@@ -18,5 +18,19 @@ class BoundPorts {
|
|
|
18
18
|
iterator() {
|
|
19
19
|
return this.ports;
|
|
20
20
|
}
|
|
21
|
+
filter(ports) {
|
|
22
|
+
const boundPorts = new BoundPorts();
|
|
23
|
+
for (const [internalPort, hostPort] of this.iterator()) {
|
|
24
|
+
if (ports.includes(internalPort)) {
|
|
25
|
+
boundPorts.setBinding(internalPort, hostPort);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return boundPorts;
|
|
29
|
+
}
|
|
30
|
+
static fromInspectResult(inspectResult) {
|
|
31
|
+
const boundPorts = new BoundPorts();
|
|
32
|
+
Object.entries(inspectResult.ports).forEach(([internalPort, hostPort]) => boundPorts.setBinding(parseInt(internalPort), hostPort));
|
|
33
|
+
return boundPorts;
|
|
34
|
+
}
|
|
21
35
|
}
|
|
22
36
|
exports.BoundPorts = BoundPorts;
|
package/dist/docker/dockerode.js
CHANGED
|
@@ -5,6 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.dockerode = void 0;
|
|
7
7
|
const dockerode_1 = __importDefault(require("dockerode"));
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const testcontainers_properties_file_1 = require("../testcontainers-properties-file");
|
|
9
|
+
testcontainers_properties_file_1.loadTestcontainersPropertiesFile();
|
|
10
10
|
exports.dockerode = new dockerode_1.default();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DockerImageName } from "../../../docker-image-name";
|
|
2
|
-
import { BoundPorts } from "../../../bound-ports";
|
|
3
2
|
import Dockerode from "dockerode";
|
|
3
|
+
import { Port } from "../../../port";
|
|
4
4
|
import { BindMount, Command, ContainerName, Env, ExtraHost, HealthCheck, NetworkMode, TmpFs } from "../../types";
|
|
5
5
|
export declare type CreateContainerOptions = {
|
|
6
6
|
imageName: DockerImageName;
|
|
@@ -8,7 +8,7 @@ export declare type CreateContainerOptions = {
|
|
|
8
8
|
cmd: Command[];
|
|
9
9
|
bindMounts: BindMount[];
|
|
10
10
|
tmpFs: TmpFs;
|
|
11
|
-
|
|
11
|
+
exposedPorts: Port[];
|
|
12
12
|
name?: ContainerName;
|
|
13
13
|
networkMode?: NetworkMode;
|
|
14
14
|
healthCheck?: HealthCheck;
|
|
@@ -21,7 +21,7 @@ const createContainer = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
21
21
|
User: options.user,
|
|
22
22
|
Image: options.imageName.toString(),
|
|
23
23
|
Env: getEnv(options.env),
|
|
24
|
-
ExposedPorts: getExposedPorts(options.
|
|
24
|
+
ExposedPorts: getExposedPorts(options.exposedPorts),
|
|
25
25
|
Cmd: options.cmd,
|
|
26
26
|
Labels: create_labels_1.createLabels(options.imageName),
|
|
27
27
|
// @ts-ignore
|
|
@@ -31,7 +31,7 @@ const createContainer = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
31
31
|
ExtraHosts: getExtraHosts(options.extraHosts),
|
|
32
32
|
AutoRemove: options.autoRemove,
|
|
33
33
|
NetworkMode: options.networkMode,
|
|
34
|
-
PortBindings: getPortBindings(options.
|
|
34
|
+
PortBindings: getPortBindings(options.exposedPorts),
|
|
35
35
|
Binds: getBindMounts(options.bindMounts),
|
|
36
36
|
Tmpfs: options.tmpFs,
|
|
37
37
|
LogConfig: getLogConfig(options.useDefaultLogDriver),
|
|
@@ -46,20 +46,20 @@ const createContainer = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
46
46
|
});
|
|
47
47
|
exports.createContainer = createContainer;
|
|
48
48
|
const getEnv = (env) => Object.entries(env).reduce((dockerodeEnvironment, [key, value]) => [...dockerodeEnvironment, `${key}=${value}`], []);
|
|
49
|
-
const getExposedPorts = (
|
|
49
|
+
const getExposedPorts = (exposedPorts) => {
|
|
50
50
|
const dockerodeExposedPorts = {};
|
|
51
|
-
for (const
|
|
52
|
-
dockerodeExposedPorts[
|
|
51
|
+
for (const exposedPort of exposedPorts) {
|
|
52
|
+
dockerodeExposedPorts[exposedPort.toString()] = {};
|
|
53
53
|
}
|
|
54
54
|
return dockerodeExposedPorts;
|
|
55
55
|
};
|
|
56
56
|
const getExtraHosts = (extraHosts) => {
|
|
57
57
|
return extraHosts.map((extraHost) => `${extraHost.host}:${extraHost.ipAddress}`);
|
|
58
58
|
};
|
|
59
|
-
const getPortBindings = (
|
|
59
|
+
const getPortBindings = (exposedPorts) => {
|
|
60
60
|
const dockerodePortBindings = {};
|
|
61
|
-
for (const
|
|
62
|
-
dockerodePortBindings[
|
|
61
|
+
for (const exposedPort of exposedPorts) {
|
|
62
|
+
dockerodePortBindings[exposedPort] = [{ HostPort: "0" }];
|
|
63
63
|
}
|
|
64
64
|
return dockerodePortBindings;
|
|
65
65
|
};
|
|
@@ -19,18 +19,11 @@ const execContainer = (container, command) => __awaiter(void 0, void 0, void 0,
|
|
|
19
19
|
AttachStderr: true,
|
|
20
20
|
});
|
|
21
21
|
const stream = yield startExec(exec);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!running) {
|
|
28
|
-
clearInterval(interval);
|
|
29
|
-
stream.destroy();
|
|
30
|
-
resolve({ output: chunks.join(""), exitCode });
|
|
31
|
-
}
|
|
32
|
-
}), 100);
|
|
33
|
-
});
|
|
22
|
+
const chunks = [];
|
|
23
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
24
|
+
const exitCode = yield waitForExec(exec);
|
|
25
|
+
stream.destroy();
|
|
26
|
+
return { output: chunks.join(""), exitCode };
|
|
34
27
|
}
|
|
35
28
|
catch (err) {
|
|
36
29
|
logger_1.log.error(`Failed to exec container ${container.id} with command "${command.join(" ")}": ${err}`);
|
|
@@ -72,3 +65,14 @@ const inspectExec = (exec) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
72
65
|
throw err;
|
|
73
66
|
}
|
|
74
67
|
});
|
|
68
|
+
const waitForExec = (exec) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const interval = setInterval(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
|
+
const { running, exitCode } = yield inspectExec(exec);
|
|
72
|
+
if (!running) {
|
|
73
|
+
clearInterval(interval);
|
|
74
|
+
resolve(exitCode);
|
|
75
|
+
}
|
|
76
|
+
}), 100);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { ContainerName, HealthCheckStatus, NetworkSettings } from "../../types";
|
|
2
|
-
import { Port } from "../../../port";
|
|
1
|
+
import { ContainerName, HealthCheckStatus, NetworkSettings, Ports } from "../../types";
|
|
3
2
|
import Dockerode from "dockerode";
|
|
4
3
|
export declare type InspectResult = {
|
|
5
4
|
name: ContainerName;
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
hostname: string;
|
|
6
|
+
ports: Ports;
|
|
8
7
|
healthCheckStatus: HealthCheckStatus;
|
|
9
8
|
networkSettings: {
|
|
10
9
|
[networkName: string]: NetworkSettings;
|
|
@@ -15,9 +15,9 @@ const inspectContainer = (container) => __awaiter(void 0, void 0, void 0, functi
|
|
|
15
15
|
try {
|
|
16
16
|
const inspectResult = yield container.inspect();
|
|
17
17
|
return {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
name: inspectResult.Name,
|
|
19
|
+
hostname: inspectResult.Config.Hostname,
|
|
20
|
+
ports: getPorts(inspectResult),
|
|
21
21
|
healthCheckStatus: getHealthCheckStatus(inspectResult),
|
|
22
22
|
networkSettings: getNetworkSettings(inspectResult),
|
|
23
23
|
state: { status: inspectResult.State.Status, running: inspectResult.State.Running },
|
|
@@ -29,11 +29,13 @@ const inspectContainer = (container) => __awaiter(void 0, void 0, void 0, functi
|
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
exports.inspectContainer = inspectContainer;
|
|
32
|
-
const
|
|
33
|
-
.filter((
|
|
34
|
-
.map((
|
|
35
|
-
const
|
|
36
|
-
|
|
32
|
+
const getPorts = (inspectInfo) => Object.entries(inspectInfo.NetworkSettings.Ports)
|
|
33
|
+
.filter(([, hostPorts]) => hostPorts !== null)
|
|
34
|
+
.map(([internalPort, hostPorts]) => {
|
|
35
|
+
const hostPort = hostPorts[0].HostPort;
|
|
36
|
+
return { [parseInt(internalPort.split("/")[0])]: parseInt(hostPort) };
|
|
37
|
+
})
|
|
38
|
+
.reduce((acc, curr) => (Object.assign(Object.assign({}, acc), curr)), {});
|
|
37
39
|
const getHealthCheckStatus = (inspectResult) => {
|
|
38
40
|
const health = inspectResult.State.Health;
|
|
39
41
|
if (health === undefined) {
|
|
@@ -23,7 +23,7 @@ const create_labels_1 = require("../create-labels");
|
|
|
23
23
|
const buildImage = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
24
|
try {
|
|
25
25
|
logger_1.log.info(`Building image '${options.imageName.toString()}' with context '${options.context}'`);
|
|
26
|
-
const dockerIgnoreFiles = yield docker_ignore_1.findDockerIgnoreFiles(options.context);
|
|
26
|
+
const dockerIgnoreFiles = yield docker_ignore_1.findDockerIgnoreFiles(options.context, options.dockerfileName);
|
|
27
27
|
const tarStream = tar_fs_1.default.pack(options.context, { ignore: (name) => dockerIgnoreFiles.has(slash_1.default(name)) });
|
|
28
28
|
return new Promise((resolve) => dockerode_1.dockerode
|
|
29
29
|
.buildImage(tarStream, {
|
|
@@ -16,16 +16,15 @@ exports.getDockerHost = void 0;
|
|
|
16
16
|
const logger_1 = require("../logger");
|
|
17
17
|
const run_in_container_1 = require("./functions/run-in-container");
|
|
18
18
|
const fs_1 = __importDefault(require("fs"));
|
|
19
|
+
const url_1 = require("url");
|
|
19
20
|
const DEFAULT_HOST = "localhost";
|
|
20
21
|
const getDockerHost = (dockerode) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
|
-
if (process.env.DOCKER_HOST) {
|
|
22
|
-
logger_1.log.info(`Detected DOCKER_HOST environment variable: ${process.env.DOCKER_HOST}`);
|
|
23
|
-
}
|
|
24
22
|
for (const [hostStrategyName, hostStrategy] of Object.entries(hostStrategies(dockerode))) {
|
|
25
23
|
const result = yield hostStrategy();
|
|
26
24
|
if (result) {
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
const hostname = result === DEFAULT_HOST ? DEFAULT_HOST : new url_1.URL(result).hostname;
|
|
26
|
+
logger_1.log.info(`Docker host strategy ${hostStrategyName}: ${hostname}`);
|
|
27
|
+
return hostname;
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
logger_1.log.info(`Docker host strategy FALLBACK: ${DEFAULT_HOST}`);
|
|
@@ -33,6 +32,11 @@ const getDockerHost = (dockerode) => __awaiter(void 0, void 0, void 0, function*
|
|
|
33
32
|
});
|
|
34
33
|
exports.getDockerHost = getDockerHost;
|
|
35
34
|
const hostStrategies = (dockerode) => ({
|
|
35
|
+
DOCKER_HOST: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
|
+
if (process.env.DOCKER_HOST) {
|
|
37
|
+
return process.env.DOCKER_HOST;
|
|
38
|
+
}
|
|
39
|
+
}),
|
|
36
40
|
MODEM: () => __awaiter(void 0, void 0, void 0, function* () { return dockerode.modem.host; }),
|
|
37
41
|
TESTCONTAINERS_HOST_OVERRIDE: () => __awaiter(void 0, void 0, void 0, function* () { return process.env["TESTCONTAINERS_HOST_OVERRIDE"]; }),
|
|
38
42
|
OUTSIDE_CONTAINER: () => __awaiter(void 0, void 0, void 0, function* () {
|
package/dist/docker/types.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export declare type ExtraHost = {
|
|
|
28
28
|
};
|
|
29
29
|
export declare type NetworkMode = string;
|
|
30
30
|
export declare type ContainerName = string;
|
|
31
|
+
export declare type Ports = {
|
|
32
|
+
[internalPort: number]: number;
|
|
33
|
+
};
|
|
31
34
|
export declare type AuthConfig = {
|
|
32
35
|
username: string;
|
|
33
36
|
password: string;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { IDockerComposeOptions } from "docker-compose";
|
|
1
|
+
import { IDockerComposeOptions } from "./docker-compose";
|
|
2
2
|
import { DockerComposeOptions } from "./docker-compose-options";
|
|
3
3
|
export declare const defaultDockerComposeOptions: ({ env, ...options }: DockerComposeOptions) => Partial<IDockerComposeOptions>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.version = exports.stop = exports.down = exports.upAll = exports.upMany = void 0;
|
|
4
|
+
const testcontainers_properties_file_1 = require("../testcontainers-properties-file");
|
|
5
|
+
testcontainers_properties_file_1.loadTestcontainersPropertiesFile();
|
|
6
|
+
var docker_compose_1 = require("docker-compose");
|
|
7
|
+
Object.defineProperty(exports, "upMany", { enumerable: true, get: function () { return docker_compose_1.upMany; } });
|
|
8
|
+
Object.defineProperty(exports, "upAll", { enumerable: true, get: function () { return docker_compose_1.upAll; } });
|
|
9
|
+
Object.defineProperty(exports, "down", { enumerable: true, get: function () { return docker_compose_1.down; } });
|
|
10
|
+
Object.defineProperty(exports, "stop", { enumerable: true, get: function () { return docker_compose_1.stop; } });
|
|
11
|
+
Object.defineProperty(exports, "version", { enumerable: true, get: function () { return docker_compose_1.version; } });
|
|
@@ -10,9 +10,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.dockerComposeDown = void 0;
|
|
13
|
-
const docker_compose_1 = require("docker-compose");
|
|
14
13
|
const logger_1 = require("../../logger");
|
|
15
14
|
const default_docker_compose_options_1 = require("../default-docker-compose-options");
|
|
15
|
+
const docker_compose_1 = require("../docker-compose");
|
|
16
16
|
const dockerComposeDown = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
17
|
logger_1.log.info(`Downing DockerCompose environment`);
|
|
18
18
|
try {
|
|
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.dockerComposeStop = void 0;
|
|
13
13
|
const logger_1 = require("../../logger");
|
|
14
|
-
const docker_compose_1 = require("docker-compose");
|
|
14
|
+
const docker_compose_1 = require("../docker-compose");
|
|
15
15
|
const default_docker_compose_options_1 = require("../default-docker-compose-options");
|
|
16
16
|
const dockerComposeStop = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
17
|
logger_1.log.info(`Stopping DockerCompose environment`);
|
|
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.dockerComposeUp = void 0;
|
|
13
13
|
const logger_1 = require("../../logger");
|
|
14
|
-
const docker_compose_1 = require("docker-compose");
|
|
14
|
+
const docker_compose_1 = require("../docker-compose");
|
|
15
15
|
const default_docker_compose_options_1 = require("../default-docker-compose-options");
|
|
16
16
|
const docker_compose_down_1 = require("./docker-compose-down");
|
|
17
17
|
const dockerComposeUp = (options, services) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -20,7 +20,6 @@ export declare class DockerComposeEnvironment {
|
|
|
20
20
|
withWaitStrategy(containerName: string, waitStrategy: WaitStrategy): this;
|
|
21
21
|
withStartupTimeout(startupTimeout: number): this;
|
|
22
22
|
up(services?: Array<string>): Promise<StartedDockerComposeEnvironment>;
|
|
23
|
-
private getBoundPorts;
|
|
24
23
|
private waitForContainer;
|
|
25
24
|
private getWaitStrategy;
|
|
26
25
|
}
|
|
@@ -80,10 +80,7 @@ class DockerComposeEnvironment {
|
|
|
80
80
|
commandOptions.push("--no-recreate");
|
|
81
81
|
}
|
|
82
82
|
const composeOptions = [];
|
|
83
|
-
this.profiles.forEach((profile) =>
|
|
84
|
-
composeOptions.push("--profile");
|
|
85
|
-
composeOptions.push(profile);
|
|
86
|
-
});
|
|
83
|
+
this.profiles.forEach((profile) => composeOptions.push("--profile", profile));
|
|
87
84
|
yield docker_compose_up_1.dockerComposeUp(Object.assign(Object.assign({}, options), { commandOptions, composeOptions, env: this.env }), services);
|
|
88
85
|
const startedContainers = (yield list_containers_1.listContainers()).filter((container) => container.Labels["com.docker.compose.project"] === this.projectName);
|
|
89
86
|
const startedContainerNames = startedContainers.reduce((containerNames, startedContainer) => [
|
|
@@ -98,7 +95,7 @@ class DockerComposeEnvironment {
|
|
|
98
95
|
.on("data", (data) => logger_1.containerLog.trace(`${containerName}: ${data}`))
|
|
99
96
|
.on("err", (data) => logger_1.containerLog.error(`${containerName}: ${data}`));
|
|
100
97
|
const inspectResult = yield inspect_container_1.inspectContainer(container);
|
|
101
|
-
const boundPorts =
|
|
98
|
+
const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(inspectResult);
|
|
102
99
|
try {
|
|
103
100
|
logger_1.log.info(`Waiting for container ${containerName} to be ready`);
|
|
104
101
|
yield this.waitForContainer(container, containerName, boundPorts);
|
|
@@ -120,14 +117,9 @@ class DockerComposeEnvironment {
|
|
|
120
117
|
return Object.assign(Object.assign({}, map), { [containerName]: startedGenericContainer });
|
|
121
118
|
}, {});
|
|
122
119
|
logger_1.log.info(`DockerCompose environment started: ${Object.keys(startedGenericContainers).join(", ")}`);
|
|
123
|
-
return new started_docker_compose_environment_1.StartedDockerComposeEnvironment(startedGenericContainers, options);
|
|
120
|
+
return new started_docker_compose_environment_1.StartedDockerComposeEnvironment(startedGenericContainers, Object.assign(Object.assign({}, options), { composeOptions, env: this.env }));
|
|
124
121
|
});
|
|
125
122
|
}
|
|
126
|
-
getBoundPorts(containerInfo) {
|
|
127
|
-
const boundPorts = new bound_ports_1.BoundPorts();
|
|
128
|
-
containerInfo.Ports.filter((port) => port.PublicPort !== undefined).forEach((port) => boundPorts.setBinding(port.PrivatePort, port.PublicPort));
|
|
129
|
-
return boundPorts;
|
|
130
|
-
}
|
|
131
123
|
waitForContainer(container, containerName, boundPorts) {
|
|
132
124
|
return __awaiter(this, void 0, void 0, function* () {
|
|
133
125
|
const waitStrategy = this.getWaitStrategy(yield docker_host_1.dockerHost, container, containerName);
|
package/dist/docker-ignore.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const findDockerIgnoreFiles: (context: string) => Promise<Set<string>>;
|
|
1
|
+
export declare const findDockerIgnoreFiles: (context: string, dockerfileName: string) => Promise<Set<string>>;
|
package/dist/docker-ignore.js
CHANGED
|
@@ -17,7 +17,7 @@ const fs_1 = require("fs");
|
|
|
17
17
|
const os_1 = __importDefault(require("os"));
|
|
18
18
|
const glob_1 = __importDefault(require("glob"));
|
|
19
19
|
const path_1 = __importDefault(require("path"));
|
|
20
|
-
const findDockerIgnoreFiles = (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
+
const findDockerIgnoreFiles = (context, dockerfileName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
21
|
const dockerIgnoreFilePath = path_1.default.join(context, ".dockerignore");
|
|
22
22
|
if (!fs_1.existsSync(dockerIgnoreFilePath)) {
|
|
23
23
|
return new Set();
|
|
@@ -25,6 +25,7 @@ const findDockerIgnoreFiles = (context) => __awaiter(void 0, void 0, void 0, fun
|
|
|
25
25
|
const dockerIgnorePatterns = (yield fs_1.promises.readFile(dockerIgnoreFilePath, { encoding: "utf-8" }))
|
|
26
26
|
.toString()
|
|
27
27
|
.split(os_1.default.EOL)
|
|
28
|
+
.filter((dockerIgnorePattern) => dockerIgnorePattern !== dockerfileName)
|
|
28
29
|
.map((dockerIgnorePattern) => path_1.default.resolve(context, dockerIgnorePattern));
|
|
29
30
|
const dockerIgnoreMatches = (yield Promise.all(dockerIgnorePatterns.map(findIgnoredFilesForPattern))).reduce((prev, next) => [...prev, ...next]);
|
|
30
31
|
return new Set(dockerIgnoreMatches);
|
|
@@ -6,7 +6,8 @@ import { PullPolicy } from "../pull-policy";
|
|
|
6
6
|
import { StartedTestContainer, TestContainer } from "../test-container";
|
|
7
7
|
import { WaitStrategy } from "../wait-strategy";
|
|
8
8
|
import { Readable } from "stream";
|
|
9
|
-
import { BindMode, BindMount, BuildContext, Command, ContainerName, Dir, Env, EnvKey, EnvValue, HealthCheck, NetworkMode, TmpFs } from "../docker/types";
|
|
9
|
+
import { BindMode, BindMount, BuildContext, Command, ContainerName, Dir, Env, EnvKey, EnvValue, ExtraHost, HealthCheck, NetworkMode, TmpFs } from "../docker/types";
|
|
10
|
+
import { InspectResult } from "../docker/functions/container/inspect-container";
|
|
10
11
|
import { GenericContainerBuilder } from "./generic-container-builder";
|
|
11
12
|
export declare class GenericContainer implements TestContainer {
|
|
12
13
|
readonly image: string;
|
|
@@ -31,14 +32,18 @@ export declare class GenericContainer implements TestContainer {
|
|
|
31
32
|
protected tarToCopy?: archiver.Archiver;
|
|
32
33
|
private extraHosts;
|
|
33
34
|
constructor(image: string);
|
|
35
|
+
protected preStart?(): Promise<void>;
|
|
34
36
|
start(): Promise<StartedTestContainer>;
|
|
37
|
+
protected postStart?(container: StartedTestContainer, inspectResult: InspectResult, boundPorts: BoundPorts): Promise<void>;
|
|
35
38
|
withCmd(cmd: Command[]): this;
|
|
36
39
|
withName(name: ContainerName): this;
|
|
37
40
|
withEnv(key: EnvKey, value: EnvValue): this;
|
|
38
41
|
withTmpFs(tmpFs: TmpFs): this;
|
|
39
42
|
withNetworkMode(networkMode: NetworkMode): this;
|
|
40
43
|
withNetworkAliases(...networkAliases: string[]): this;
|
|
44
|
+
withExtraHosts(...extraHosts: ExtraHost[]): this;
|
|
41
45
|
withExposedPorts(...ports: Port[]): this;
|
|
46
|
+
protected addExposedPorts(...ports: Port[]): this;
|
|
42
47
|
withBindMount(source: Dir, target: Dir, bindMode?: BindMode): this;
|
|
43
48
|
withHealthCheck(healthCheck: HealthCheck): this;
|
|
44
49
|
withStartupTimeout(startupTimeout: number): this;
|
|
@@ -51,7 +56,6 @@ export declare class GenericContainer implements TestContainer {
|
|
|
51
56
|
withCopyFileToContainer(sourcePath: string, containerPath: string): this;
|
|
52
57
|
withCopyContentToContainer(content: string | Buffer | Readable, containerPath: string): this;
|
|
53
58
|
protected getTarToCopy(): archiver.Archiver;
|
|
54
|
-
protected preCreate?(boundPorts: BoundPorts): Promise<void>;
|
|
55
59
|
private waitForContainer;
|
|
56
60
|
private getWaitStrategy;
|
|
57
61
|
}
|
|
@@ -14,8 +14,8 @@ 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 bound_ports_1 = require("../bound-ports");
|
|
17
18
|
const logger_1 = require("../logger");
|
|
18
|
-
const port_binder_1 = require("../port-binder");
|
|
19
19
|
const port_check_1 = require("../port-check");
|
|
20
20
|
const pull_policy_1 = require("../pull-policy");
|
|
21
21
|
const reaper_1 = require("../reaper");
|
|
@@ -61,12 +61,11 @@ class GenericContainer {
|
|
|
61
61
|
force: this.pullPolicy.shouldPull(),
|
|
62
62
|
authConfig: yield registry_auth_locator_1.getAuthConfig(this.imageName.registry),
|
|
63
63
|
});
|
|
64
|
-
const boundPorts = yield new port_binder_1.PortBinder().bind(this.ports);
|
|
65
64
|
if (!this.imageName.isReaper()) {
|
|
66
65
|
yield reaper_1.ReaperInstance.getInstance();
|
|
67
66
|
}
|
|
68
|
-
if (this.
|
|
69
|
-
yield this.
|
|
67
|
+
if (this.preStart) {
|
|
68
|
+
yield this.preStart();
|
|
70
69
|
}
|
|
71
70
|
if (!this.imageName.isHelperContainer() && port_forwarder_1.PortForwarderInstance.isRunning()) {
|
|
72
71
|
const portForwarder = yield port_forwarder_1.PortForwarderInstance.getInstance();
|
|
@@ -78,7 +77,7 @@ class GenericContainer {
|
|
|
78
77
|
cmd: this.cmd,
|
|
79
78
|
bindMounts: this.bindMounts,
|
|
80
79
|
tmpFs: this.tmpFs,
|
|
81
|
-
|
|
80
|
+
exposedPorts: this.ports,
|
|
82
81
|
name: this.name,
|
|
83
82
|
networkMode: this.networkAliases.length > 0 ? undefined : this.networkMode,
|
|
84
83
|
healthCheck: this.healthCheck,
|
|
@@ -94,7 +93,11 @@ class GenericContainer {
|
|
|
94
93
|
const portForwarderNetworkId = portForwarder.getNetworkId();
|
|
95
94
|
const excludedNetworks = [portForwarderNetworkId, "none", "host"];
|
|
96
95
|
if (!this.networkMode || !excludedNetworks.includes(this.networkMode)) {
|
|
97
|
-
yield connect_network_1.connectNetwork({
|
|
96
|
+
yield connect_network_1.connectNetwork({
|
|
97
|
+
containerId: container.id,
|
|
98
|
+
networkId: portForwarderNetworkId,
|
|
99
|
+
networkAliases: [],
|
|
100
|
+
});
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
if (this.networkMode && this.networkAliases.length > 0) {
|
|
@@ -114,8 +117,13 @@ class GenericContainer {
|
|
|
114
117
|
.on("data", (data) => logger_1.containerLog.trace(`${container.id}: ${data.trim()}`))
|
|
115
118
|
.on("err", (data) => logger_1.containerLog.error(`${container.id}: ${data.trim()}`));
|
|
116
119
|
const inspectResult = yield inspect_container_1.inspectContainer(container);
|
|
120
|
+
const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(inspectResult).filter(this.ports);
|
|
117
121
|
yield this.waitForContainer(container, boundPorts);
|
|
118
|
-
|
|
122
|
+
const startedContainer = new started_generic_container_1.StartedGenericContainer(container, yield docker_host_1.dockerHost, inspectResult, boundPorts, inspectResult.name);
|
|
123
|
+
if (this.postStart) {
|
|
124
|
+
yield this.postStart(startedContainer, inspectResult, boundPorts);
|
|
125
|
+
}
|
|
126
|
+
return startedContainer;
|
|
119
127
|
});
|
|
120
128
|
}
|
|
121
129
|
withCmd(cmd) {
|
|
@@ -142,10 +150,18 @@ class GenericContainer {
|
|
|
142
150
|
this.networkAliases = networkAliases;
|
|
143
151
|
return this;
|
|
144
152
|
}
|
|
153
|
+
withExtraHosts(...extraHosts) {
|
|
154
|
+
this.extraHosts.push(...extraHosts);
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
145
157
|
withExposedPorts(...ports) {
|
|
146
158
|
this.ports = ports;
|
|
147
159
|
return this;
|
|
148
160
|
}
|
|
161
|
+
addExposedPorts(...ports) {
|
|
162
|
+
this.ports.push(...ports);
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
149
165
|
withBindMount(source, target, bindMode = "rw") {
|
|
150
166
|
this.bindMounts.push({ source, target, bindMode });
|
|
151
167
|
return this;
|
|
@@ -1,23 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
22
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
23
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -30,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
30
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
12
|
exports.logSystemDiagnostics = void 0;
|
|
32
13
|
const logger_1 = require("./logger");
|
|
33
|
-
const
|
|
14
|
+
const docker_compose_1 = require("./docker-compose/docker-compose");
|
|
34
15
|
const get_info_1 = require("./docker/functions/get-info");
|
|
35
16
|
const logSystemDiagnostics = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
17
|
const info = {
|
|
@@ -51,7 +32,7 @@ const getNodeInfo = () => {
|
|
|
51
32
|
const getDockerComposeInfo = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
33
|
try {
|
|
53
34
|
return {
|
|
54
|
-
version: (yield
|
|
35
|
+
version: (yield docker_compose_1.version()).data.version,
|
|
55
36
|
};
|
|
56
37
|
}
|
|
57
38
|
catch (err) {
|
|
@@ -1,34 +1,23 @@
|
|
|
1
1
|
import { GenericContainer } from "../../generic-container/generic-container";
|
|
2
2
|
import { BoundPorts } from "../../bound-ports";
|
|
3
|
-
import { StartedNetwork } from "../../network";
|
|
4
3
|
import { Port } from "../../port";
|
|
5
|
-
import { StartedTestContainer
|
|
6
|
-
import { StopOptions } from "../../test-container";
|
|
4
|
+
import { StartedTestContainer } from "../..";
|
|
7
5
|
import { AbstractStartedContainer } from "../abstract-started-container";
|
|
8
6
|
import { Host } from "../../docker/types";
|
|
7
|
+
import { InspectResult } from "../../docker/functions/container/inspect-container";
|
|
9
8
|
export declare const KAFKA_IMAGE = "confluentinc/cp-kafka:5.5.4";
|
|
10
|
-
export declare const ZK_IMAGE = "confluentinc/cp-zookeeper:5.5.4";
|
|
11
9
|
export declare class KafkaContainer extends GenericContainer {
|
|
12
|
-
private readonly zooKeeperImage;
|
|
13
|
-
private readonly host;
|
|
14
10
|
private readonly uuid;
|
|
15
11
|
private readonly portGenerator;
|
|
16
12
|
private isZooKeeperProvided;
|
|
17
13
|
private zooKeeperHost?;
|
|
18
14
|
private zooKeeperPort?;
|
|
19
|
-
|
|
20
|
-
private zooKeeperContainer?;
|
|
21
|
-
constructor(image?: string, host?: Host, zooKeeperImage?: string);
|
|
15
|
+
constructor(image?: string);
|
|
22
16
|
withZooKeeper(host: Host, port: Port): this;
|
|
23
|
-
protected
|
|
17
|
+
protected preStart(): Promise<void>;
|
|
24
18
|
start(): Promise<StartedKafkaContainer>;
|
|
19
|
+
protected postStart(container: StartedTestContainer, inspectResult: InspectResult, boundPorts: BoundPorts): Promise<void>;
|
|
25
20
|
}
|
|
26
21
|
export declare class StartedKafkaContainer extends AbstractStartedContainer {
|
|
27
|
-
|
|
28
|
-
private readonly zooKeeperContainer?;
|
|
29
|
-
private readonly zooKeeperPort?;
|
|
30
|
-
constructor(startedTestContainer: StartedTestContainer, network?: StartedNetwork | undefined, zooKeeperContainer?: StartedTestContainer | undefined, zooKeeperPort?: number | undefined);
|
|
31
|
-
stop(options?: Partial<StopOptions>): Promise<StoppedTestContainer>;
|
|
32
|
-
getZookeeperName(): string | undefined;
|
|
33
|
-
getZookeeperPort(): number | undefined;
|
|
22
|
+
constructor(startedTestContainer: StartedTestContainer);
|
|
34
23
|
}
|
|
@@ -9,26 +9,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.StartedKafkaContainer = exports.KafkaContainer = exports.
|
|
12
|
+
exports.StartedKafkaContainer = exports.KafkaContainer = exports.KAFKA_IMAGE = void 0;
|
|
13
13
|
const generic_container_1 = require("../../generic-container/generic-container");
|
|
14
|
-
const network_1 = require("../../network");
|
|
15
14
|
const uuid_1 = require("../../uuid");
|
|
16
|
-
const logger_1 = require("../../logger");
|
|
17
15
|
const abstract_started_container_1 = require("../abstract-started-container");
|
|
18
16
|
const port_generator_1 = require("../../port-generator");
|
|
19
17
|
const docker_host_1 = require("../../docker/docker-host");
|
|
20
18
|
const KAFKA_PORT = 9093;
|
|
21
19
|
const KAFKA_BROKER_PORT = 9092;
|
|
22
20
|
exports.KAFKA_IMAGE = "confluentinc/cp-kafka:5.5.4";
|
|
23
|
-
exports.ZK_IMAGE = "confluentinc/cp-zookeeper:5.5.4";
|
|
24
21
|
class KafkaContainer extends generic_container_1.GenericContainer {
|
|
25
|
-
constructor(image = exports.KAFKA_IMAGE
|
|
22
|
+
constructor(image = exports.KAFKA_IMAGE) {
|
|
26
23
|
super(image);
|
|
27
|
-
this.zooKeeperImage = zooKeeperImage;
|
|
28
24
|
this.uuid = new uuid_1.RandomUuid();
|
|
29
25
|
this.portGenerator = new port_generator_1.RandomUniquePortGenerator();
|
|
30
26
|
this.isZooKeeperProvided = false;
|
|
31
|
-
this.
|
|
27
|
+
this.withExposedPorts(KAFKA_PORT)
|
|
28
|
+
.withStartupTimeout(180000)
|
|
29
|
+
.withEnv("KAFKA_LISTENERS", `PLAINTEXT://0.0.0.0:${KAFKA_PORT},BROKER://0.0.0.0:${KAFKA_BROKER_PORT}`)
|
|
30
|
+
.withEnv("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP", "BROKER:PLAINTEXT,PLAINTEXT:PLAINTEXT")
|
|
31
|
+
.withEnv("KAFKA_INTER_BROKER_LISTENER_NAME", "BROKER")
|
|
32
|
+
.withEnv("KAFKA_BROKER_ID", "1")
|
|
33
|
+
.withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", "1")
|
|
34
|
+
.withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", "1")
|
|
35
|
+
.withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", "1")
|
|
36
|
+
.withEnv("KAFKA_LOG_FLUSH_INTERVAL_MESSAGES", "9223372036854775807")
|
|
37
|
+
.withEnv("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS", "0")
|
|
38
|
+
.withEnv("KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE", "false");
|
|
32
39
|
}
|
|
33
40
|
withZooKeeper(host, port) {
|
|
34
41
|
this.isZooKeeperProvided = true;
|
|
@@ -36,10 +43,27 @@ class KafkaContainer extends generic_container_1.GenericContainer {
|
|
|
36
43
|
this.zooKeeperPort = port;
|
|
37
44
|
return this;
|
|
38
45
|
}
|
|
39
|
-
|
|
46
|
+
preStart() {
|
|
40
47
|
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
-
const
|
|
42
|
-
this.withEnv("KAFKA_ADVERTISED_LISTENERS", `
|
|
48
|
+
const network = this.networkMode && this.networkAliases.length > 0 ? this.networkAliases[0] : "localhost";
|
|
49
|
+
this.withEnv("KAFKA_ADVERTISED_LISTENERS", `BROKER://${network}:${KAFKA_BROKER_PORT}`);
|
|
50
|
+
let command = "#!/bin/bash\n";
|
|
51
|
+
if (this.isZooKeeperProvided) {
|
|
52
|
+
this.withEnv("KAFKA_ZOOKEEPER_CONNECT", `${this.zooKeeperHost}:${this.zooKeeperPort}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
this.zooKeeperHost = this.uuid.nextUuid();
|
|
56
|
+
this.zooKeeperPort = yield this.portGenerator.generatePort();
|
|
57
|
+
this.addExposedPorts(this.zooKeeperPort);
|
|
58
|
+
this.withEnv("KAFKA_ZOOKEEPER_CONNECT", `localhost:${this.zooKeeperPort}`);
|
|
59
|
+
command += "echo 'clientPort=" + this.zooKeeperPort + "' > zookeeper.properties\n";
|
|
60
|
+
command += "echo 'dataDir=/var/lib/zookeeper/data' >> zookeeper.properties\n";
|
|
61
|
+
command += "echo 'dataLogDir=/var/lib/zookeeper/log' >> zookeeper.properties\n";
|
|
62
|
+
command += "zookeeper-server-start zookeeper.properties &\n";
|
|
63
|
+
}
|
|
64
|
+
command += "echo '' > /etc/confluent/docker/ensure \n";
|
|
65
|
+
command += "/etc/confluent/docker/run \n";
|
|
66
|
+
this.withCmd(["sh", "-c", command]);
|
|
43
67
|
});
|
|
44
68
|
}
|
|
45
69
|
start() {
|
|
@@ -47,71 +71,35 @@ class KafkaContainer extends generic_container_1.GenericContainer {
|
|
|
47
71
|
start: { get: () => super.start }
|
|
48
72
|
});
|
|
49
73
|
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
this.network = yield new network_1.Network().start();
|
|
74
|
-
this.withNetworkMode(this.network.getName());
|
|
75
|
-
zookeeperContainer.withNetworkMode(this.network.getName());
|
|
76
|
-
}
|
|
77
|
-
this.zooKeeperContainer = yield zookeeperContainer.start();
|
|
74
|
+
return new StartedKafkaContainer(yield _super.start.call(this));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
postStart(container, inspectResult, boundPorts) {
|
|
78
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
const brokerAdvertisedListener = `BROKER://${inspectResult.hostname}:${KAFKA_BROKER_PORT}`;
|
|
80
|
+
const bootstrapServers = `PLAINTEXT://${yield docker_host_1.dockerHost}:${boundPorts.getBinding(KAFKA_PORT)}`;
|
|
81
|
+
const { output, exitCode } = yield container.exec([
|
|
82
|
+
"kafka-configs",
|
|
83
|
+
"--alter",
|
|
84
|
+
"--bootstrap-server",
|
|
85
|
+
brokerAdvertisedListener,
|
|
86
|
+
"--entity-type",
|
|
87
|
+
"brokers",
|
|
88
|
+
"--entity-name",
|
|
89
|
+
this.env["KAFKA_BROKER_ID"],
|
|
90
|
+
"--add-config",
|
|
91
|
+
`advertised.listeners=[${bootstrapServers},${brokerAdvertisedListener}]`,
|
|
92
|
+
]);
|
|
93
|
+
if (exitCode !== 0) {
|
|
94
|
+
throw new Error(`Kafka container configuration failed with exit code ${exitCode}: ${output}`);
|
|
78
95
|
}
|
|
79
|
-
return new StartedKafkaContainer(yield _super.start.call(this), this.network, this.zooKeeperContainer, zooKeeperPort);
|
|
80
96
|
});
|
|
81
97
|
}
|
|
82
98
|
}
|
|
83
99
|
exports.KafkaContainer = KafkaContainer;
|
|
84
100
|
class StartedKafkaContainer extends abstract_started_container_1.AbstractStartedContainer {
|
|
85
|
-
constructor(startedTestContainer
|
|
101
|
+
constructor(startedTestContainer) {
|
|
86
102
|
super(startedTestContainer);
|
|
87
|
-
this.network = network;
|
|
88
|
-
this.zooKeeperContainer = zooKeeperContainer;
|
|
89
|
-
this.zooKeeperPort = zooKeeperPort;
|
|
90
|
-
}
|
|
91
|
-
stop(options) {
|
|
92
|
-
const _super = Object.create(null, {
|
|
93
|
-
stop: { get: () => super.stop }
|
|
94
|
-
});
|
|
95
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
-
logger_1.log.debug("Stopping Kafka container");
|
|
97
|
-
const stoppedContainer = yield _super.stop.call(this, options);
|
|
98
|
-
if (this.zooKeeperContainer) {
|
|
99
|
-
logger_1.log.debug("Stopping ZooKeeper container");
|
|
100
|
-
yield this.zooKeeperContainer.stop(options);
|
|
101
|
-
}
|
|
102
|
-
if (this.network) {
|
|
103
|
-
logger_1.log.debug("Stopping Kafka network");
|
|
104
|
-
yield this.network.stop();
|
|
105
|
-
}
|
|
106
|
-
return stoppedContainer;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
getZookeeperName() {
|
|
110
|
-
var _a;
|
|
111
|
-
return (_a = this.zooKeeperContainer) === null || _a === void 0 ? void 0 : _a.getName();
|
|
112
|
-
}
|
|
113
|
-
getZookeeperPort() {
|
|
114
|
-
return this.zooKeeperPort;
|
|
115
103
|
}
|
|
116
104
|
}
|
|
117
105
|
exports.StartedKafkaContainer = StartedKafkaContainer;
|
package/dist/reaper.d.ts
CHANGED
package/dist/reaper.js
CHANGED
|
@@ -68,6 +68,9 @@ class ReaperInstance {
|
|
|
68
68
|
static isEnabled() {
|
|
69
69
|
return process.env.TESTCONTAINERS_RYUK_DISABLED !== "true";
|
|
70
70
|
}
|
|
71
|
+
static isPrivileged() {
|
|
72
|
+
return process.env.TESTCONTAINERS_RYUK_PRIVILEGED === "true";
|
|
73
|
+
}
|
|
71
74
|
static createDisabledInstance() {
|
|
72
75
|
logger_1.log.debug(`Not creating new Reaper for session: ${session_id_1.sessionId}`);
|
|
73
76
|
return Promise.resolve(new DisabledReaper());
|
|
@@ -77,33 +80,36 @@ class ReaperInstance {
|
|
|
77
80
|
return __awaiter(this, void 0, void 0, function* () {
|
|
78
81
|
const dockerSocket = (_a = process.env["TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"]) !== null && _a !== void 0 ? _a : "/var/run/docker.sock";
|
|
79
82
|
logger_1.log.debug(`Creating new Reaper for session: ${session_id_1.sessionId}`);
|
|
80
|
-
const container =
|
|
83
|
+
const container = new generic_container_1.GenericContainer(images_1.REAPER_IMAGE)
|
|
81
84
|
.withName(`testcontainers-ryuk-${session_id_1.sessionId}`)
|
|
82
85
|
.withExposedPorts(8080)
|
|
83
|
-
.withBindMount(dockerSocket, "/var/run/docker.sock")
|
|
84
|
-
|
|
85
|
-
.
|
|
86
|
+
.withBindMount(dockerSocket, "/var/run/docker.sock");
|
|
87
|
+
if (this.isPrivileged()) {
|
|
88
|
+
container.withPrivilegedMode();
|
|
89
|
+
}
|
|
90
|
+
const startedContainer = yield container.start();
|
|
91
|
+
const containerId = startedContainer.getId();
|
|
86
92
|
const host = yield docker_host_1.dockerHost;
|
|
87
|
-
const port =
|
|
88
|
-
logger_1.log.debug(`Connecting to Reaper ${
|
|
93
|
+
const port = startedContainer.getMappedPort(8080);
|
|
94
|
+
logger_1.log.debug(`Connecting to Reaper ${containerId} on ${host}:${port}`);
|
|
89
95
|
const socket = new net_1.Socket();
|
|
90
96
|
socket.unref();
|
|
91
97
|
socket
|
|
92
|
-
.on("timeout", () => logger_1.log.error(`Reaper ${
|
|
93
|
-
.on("error", (err) => logger_1.log.error(`Reaper ${
|
|
98
|
+
.on("timeout", () => logger_1.log.error(`Reaper ${containerId} socket timed out`))
|
|
99
|
+
.on("error", (err) => logger_1.log.error(`Reaper ${containerId} socket error: ${err}`))
|
|
94
100
|
.on("close", (hadError) => {
|
|
95
101
|
if (hadError) {
|
|
96
|
-
logger_1.log.error(`Connection to Reaper ${
|
|
102
|
+
logger_1.log.error(`Connection to Reaper ${containerId} closed with error`);
|
|
97
103
|
}
|
|
98
104
|
else {
|
|
99
|
-
logger_1.log.warn(`Connection to Reaper ${
|
|
105
|
+
logger_1.log.warn(`Connection to Reaper ${containerId} closed`);
|
|
100
106
|
}
|
|
101
107
|
});
|
|
102
108
|
return new Promise((resolve) => {
|
|
103
109
|
socket.connect(port, host, () => {
|
|
104
|
-
logger_1.log.debug(`Connected to Reaper ${
|
|
110
|
+
logger_1.log.debug(`Connected to Reaper ${containerId}`);
|
|
105
111
|
socket.write(`label=org.testcontainers.session-id=${session_id_1.sessionId}\r\n`);
|
|
106
|
-
const reaper = new RealReaper(
|
|
112
|
+
const reaper = new RealReaper(startedContainer, socket);
|
|
107
113
|
resolve(reaper);
|
|
108
114
|
});
|
|
109
115
|
});
|
package/dist/test-container.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Port } from "./port";
|
|
|
3
3
|
import { PullPolicy } from "./pull-policy";
|
|
4
4
|
import { WaitStrategy } from "./wait-strategy";
|
|
5
5
|
import { Readable } from "stream";
|
|
6
|
-
import { BindMode, Command, ContainerName, Dir, EnvKey, EnvValue, ExecResult, Host, Id, NetworkMode, TmpFs } from "./docker/types";
|
|
6
|
+
import { BindMode, Command, ContainerName, Dir, EnvKey, EnvValue, ExecResult, ExtraHost, Host, Id, NetworkMode, TmpFs } from "./docker/types";
|
|
7
7
|
export interface TestContainer {
|
|
8
8
|
start(): Promise<StartedTestContainer>;
|
|
9
9
|
withEnv(key: EnvKey, value: EnvValue): this;
|
|
@@ -14,6 +14,7 @@ export interface TestContainer {
|
|
|
14
14
|
withWaitStrategy(waitStrategy: WaitStrategy): this;
|
|
15
15
|
withStartupTimeout(startupTimeout: number): this;
|
|
16
16
|
withNetworkMode(networkMode: NetworkMode): this;
|
|
17
|
+
withExtraHosts(...extraHosts: ExtraHost[]): this;
|
|
17
18
|
withDefaultLogDriver(): this;
|
|
18
19
|
withPrivilegedMode(): this;
|
|
19
20
|
withUser(user: string): this;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const loadTestcontainersPropertiesFile: () => void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadTestcontainersPropertiesFile = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const logger_1 = require("./logger");
|
|
9
|
+
const os_1 = require("os");
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const properties_reader_1 = __importDefault(require("properties-reader"));
|
|
12
|
+
const loadTestcontainersPropertiesFile = () => {
|
|
13
|
+
const file = path_1.default.resolve(os_1.homedir(), ".testcontainers.properties");
|
|
14
|
+
if (fs_1.existsSync(file)) {
|
|
15
|
+
logger_1.log.debug("Found .testcontainers.properties file");
|
|
16
|
+
const string = fs_1.readFileSync(file, { encoding: "utf-8" });
|
|
17
|
+
const properties = properties_reader_1.default("").read(string);
|
|
18
|
+
const host = properties.get("docker.host");
|
|
19
|
+
if (host !== null) {
|
|
20
|
+
logger_1.log.debug(`Setting DOCKER_HOST to ${host}`);
|
|
21
|
+
process.env.DOCKER_HOST = host;
|
|
22
|
+
}
|
|
23
|
+
const tlsVerify = properties.get("docker.tls.verify");
|
|
24
|
+
if (tlsVerify !== null) {
|
|
25
|
+
logger_1.log.debug(`Setting DOCKER_TLS_VERIFY to ${tlsVerify}`);
|
|
26
|
+
process.env.DOCKER_TLS_VERIFY = `${tlsVerify}`;
|
|
27
|
+
}
|
|
28
|
+
const certPath = properties.get("docker.cert.path");
|
|
29
|
+
if (certPath !== null) {
|
|
30
|
+
logger_1.log.debug(`Setting DOCKER_CERT_PATH to ${certPath}`);
|
|
31
|
+
process.env.DOCKER_CERT_PATH = certPath;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
exports.loadTestcontainersPropertiesFile = loadTestcontainersPropertiesFile;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testcontainers",
|
|
3
3
|
"author": "Cristian Greco",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "8.0.0",
|
|
5
5
|
"main": "dist/index",
|
|
6
6
|
"types": "dist/index",
|
|
7
7
|
"keywords": [
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"pre-commit": "lint-staged",
|
|
27
27
|
"build": "npm run clean && tsc",
|
|
28
28
|
"clean": "rimraf dist",
|
|
29
|
-
"test": "cross-env DEBUG=testcontainers jest --
|
|
29
|
+
"test": "cross-env DEBUG=testcontainers jest --runInBand",
|
|
30
30
|
"format": "prettier --write package.json \"src/**/*.ts\"",
|
|
31
31
|
"lint": "eslint --fix package.json \"src/**/*.ts\"",
|
|
32
32
|
"updateDeps": "npx npm-check-updates -u"
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"dockerode": "^3.3.1",
|
|
42
42
|
"get-port": "^5.1.1",
|
|
43
43
|
"glob": "^7.2.0",
|
|
44
|
+
"properties-reader": "^2.2.0",
|
|
44
45
|
"slash": "^3.0.0",
|
|
45
46
|
"ssh-remote-port-forward": "^1.0.4",
|
|
46
47
|
"tar-fs": "^2.1.1"
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
"@types/node": "^16.9.6",
|
|
55
56
|
"@types/node-fetch": "^2.5.11",
|
|
56
57
|
"@types/pg": "^8.6.1",
|
|
58
|
+
"@types/properties-reader": "^2.1.1",
|
|
57
59
|
"@types/tar-fs": "^2.0.1",
|
|
58
60
|
"@typescript-eslint/eslint-plugin": "^4.31.2",
|
|
59
61
|
"@typescript-eslint/parser": "^4.31.2",
|