testcontainers 7.24.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 +22 -14
- 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/inspect-container.d.ts +3 -4
- package/dist/docker/functions/container/inspect-container.js +10 -8
- 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 +2 -10
- package/dist/generic-container/generic-container.d.ts +4 -1
- package/dist/generic-container/generic-container.js +14 -6
- 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/testcontainers-properties-file.d.ts +1 -0
- package/dist/testcontainers-properties-file.js +35 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -18,12 +18,18 @@ npm i -D testcontainers
|
|
|
18
18
|
|
|
19
19
|
The following environment variables are supported:
|
|
20
20
|
|
|
21
|
-
- `DEBUG=testcontainers` See
|
|
22
|
-
- `DEBUG=testcontainers:containers` See container
|
|
23
|
-
- `DEBUG=testcontainers*` See all
|
|
24
|
-
- `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
|
|
25
29
|
- `TESTCONTAINERS_HOST_OVERRIDE=docker.svc.local` Docker's host on which ports are exposed
|
|
26
|
-
- `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
|
|
27
33
|
- `TESTCONTAINERS_RYUK_DISABLED=true` Disable [ryuk](#ryuk)
|
|
28
34
|
- `RYUK_CONTAINER_IMAGE=registry.mycompany.com/mirror/ryuk:0.3.0` Custom image for [ryuk](#ryuk)
|
|
29
35
|
- `SSHD_CONTAINER_IMAGE=registry.mycompany.com/mirror/sshd:1.0.0` Custom image for [SSHd](#SSHd)
|
|
@@ -138,7 +144,7 @@ Creating a container with a command:
|
|
|
138
144
|
const { GenericContainer } = require("testcontainers");
|
|
139
145
|
|
|
140
146
|
const container = await new GenericContainer("alpine")
|
|
141
|
-
.withCmd(["
|
|
147
|
+
.withCmd(["sleep", "infinity"])
|
|
142
148
|
.start();
|
|
143
149
|
```
|
|
144
150
|
|
|
@@ -148,7 +154,7 @@ Execute commands inside a running container:
|
|
|
148
154
|
const { GenericContainer } = require("testcontainers");
|
|
149
155
|
|
|
150
156
|
const container = await new GenericContainer("alpine")
|
|
151
|
-
.withCmd(["
|
|
157
|
+
.withCmd(["sleep", "infinity"])
|
|
152
158
|
.start();
|
|
153
159
|
|
|
154
160
|
const { output, exitCode } = await container.exec(["echo", "hello", "world"]);
|
|
@@ -265,12 +271,12 @@ const network = await new Network()
|
|
|
265
271
|
.start();
|
|
266
272
|
|
|
267
273
|
const container = await new GenericContainer("alpine")
|
|
268
|
-
.withCmd(["
|
|
274
|
+
.withCmd(["sleep", "infinity"])
|
|
269
275
|
.withNetworkMode(network.getName())
|
|
270
276
|
.start();
|
|
271
277
|
|
|
272
278
|
const fooContainer = await new GenericContainer("alpine")
|
|
273
|
-
.withCmd(["
|
|
279
|
+
.withCmd(["sleep", "infinity"])
|
|
274
280
|
.withNetworkMode(network.getName())
|
|
275
281
|
.withNetworkAliases("foo", "bar")
|
|
276
282
|
.start();
|
|
@@ -284,10 +290,10 @@ Add hostname mappings:
|
|
|
284
290
|
const { GenericContainer } = require("testcontainers");
|
|
285
291
|
|
|
286
292
|
const container = await new GenericContainer("alpine")
|
|
287
|
-
.withExtraHosts(
|
|
293
|
+
.withExtraHosts(
|
|
288
294
|
{ host: "foo", ipAddress: "10.11.12.13" },
|
|
289
295
|
{ host: "bar", ipAddress: "11.12.13.14" }
|
|
290
|
-
|
|
296
|
+
)
|
|
291
297
|
.start();
|
|
292
298
|
|
|
293
299
|
expect((await container.exec(["nslookup", "foo"])).exitCode).toBe(0);
|
|
@@ -419,7 +425,7 @@ server.listen(8000);
|
|
|
419
425
|
await TestContainers.exposeHostPorts(8000);
|
|
420
426
|
|
|
421
427
|
const container = await new GenericContainer("alpine")
|
|
422
|
-
.withCmd(["
|
|
428
|
+
.withCmd(["sleep", "infinity"])
|
|
423
429
|
.start();
|
|
424
430
|
|
|
425
431
|
const { output } = await container.exec(["curl", `http://host.testcontainers.internal:8000`]);
|
|
@@ -652,8 +658,9 @@ Once started, this container keeps track of containers/images/networks/volumes c
|
|
|
652
658
|
automatically clean them up 10s after connectivity with testcontainers is lost. This is useful for example if a test
|
|
653
659
|
starts a container and then terminates unexpectedly, as it will be automatically removed.
|
|
654
660
|
|
|
655
|
-
ryuk
|
|
656
|
-
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`.
|
|
657
664
|
|
|
658
665
|
### SSHd
|
|
659
666
|
|
|
@@ -683,3 +690,4 @@ depending on file sizes and network constraints. It's unlikely that the default
|
|
|
683
690
|
timeouts set by test frameworks are sufficient.
|
|
684
691
|
|
|
685
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
|
};
|
|
@@ -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) {
|
|
@@ -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);
|
|
@@ -123,11 +120,6 @@ class DockerComposeEnvironment {
|
|
|
123
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);
|
|
@@ -7,6 +7,7 @@ import { StartedTestContainer, TestContainer } from "../test-container";
|
|
|
7
7
|
import { WaitStrategy } from "../wait-strategy";
|
|
8
8
|
import { Readable } from "stream";
|
|
9
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,7 +32,9 @@ 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;
|
|
@@ -40,6 +43,7 @@ export declare class GenericContainer implements TestContainer {
|
|
|
40
43
|
withNetworkAliases(...networkAliases: string[]): this;
|
|
41
44
|
withExtraHosts(...extraHosts: ExtraHost[]): this;
|
|
42
45
|
withExposedPorts(...ports: Port[]): this;
|
|
46
|
+
protected addExposedPorts(...ports: Port[]): this;
|
|
43
47
|
withBindMount(source: Dir, target: Dir, bindMode?: BindMode): this;
|
|
44
48
|
withHealthCheck(healthCheck: HealthCheck): this;
|
|
45
49
|
withStartupTimeout(startupTimeout: number): this;
|
|
@@ -52,7 +56,6 @@ export declare class GenericContainer implements TestContainer {
|
|
|
52
56
|
withCopyFileToContainer(sourcePath: string, containerPath: string): this;
|
|
53
57
|
withCopyContentToContainer(content: string | Buffer | Readable, containerPath: string): this;
|
|
54
58
|
protected getTarToCopy(): archiver.Archiver;
|
|
55
|
-
protected preCreate?(boundPorts: BoundPorts): Promise<void>;
|
|
56
59
|
private waitForContainer;
|
|
57
60
|
private getWaitStrategy;
|
|
58
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,
|
|
@@ -118,8 +117,13 @@ class GenericContainer {
|
|
|
118
117
|
.on("data", (data) => logger_1.containerLog.trace(`${container.id}: ${data.trim()}`))
|
|
119
118
|
.on("err", (data) => logger_1.containerLog.error(`${container.id}: ${data.trim()}`));
|
|
120
119
|
const inspectResult = yield inspect_container_1.inspectContainer(container);
|
|
120
|
+
const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(inspectResult).filter(this.ports);
|
|
121
121
|
yield this.waitForContainer(container, boundPorts);
|
|
122
|
-
|
|
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;
|
|
123
127
|
});
|
|
124
128
|
}
|
|
125
129
|
withCmd(cmd) {
|
|
@@ -154,6 +158,10 @@ class GenericContainer {
|
|
|
154
158
|
this.ports = ports;
|
|
155
159
|
return this;
|
|
156
160
|
}
|
|
161
|
+
addExposedPorts(...ports) {
|
|
162
|
+
this.ports.push(...ports);
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
157
165
|
withBindMount(source, target, bindMode = "rw") {
|
|
158
166
|
this.bindMounts.push({ source, target, bindMode });
|
|
159
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
|
});
|
|
@@ -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": [
|
|
@@ -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",
|