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.
Files changed (33) hide show
  1. package/README.md +38 -12
  2. package/dist/bound-ports.d.ts +3 -0
  3. package/dist/bound-ports.js +14 -0
  4. package/dist/docker/dockerode.js +2 -2
  5. package/dist/docker/functions/container/create-container.d.ts +2 -2
  6. package/dist/docker/functions/container/create-container.js +8 -8
  7. package/dist/docker/functions/container/exec-container.js +16 -12
  8. package/dist/docker/functions/container/inspect-container.d.ts +3 -4
  9. package/dist/docker/functions/container/inspect-container.js +10 -8
  10. package/dist/docker/functions/image/build-image.js +1 -1
  11. package/dist/docker/get-docker-host.js +9 -5
  12. package/dist/docker/types.d.ts +3 -0
  13. package/dist/docker-compose/default-docker-compose-options.d.ts +1 -1
  14. package/dist/docker-compose/docker-compose.d.ts +2 -0
  15. package/dist/docker-compose/docker-compose.js +11 -0
  16. package/dist/docker-compose/functions/docker-compose-down.js +1 -1
  17. package/dist/docker-compose/functions/docker-compose-stop.js +1 -1
  18. package/dist/docker-compose/functions/docker-compose-up.js +1 -1
  19. package/dist/docker-compose-environment/docker-compose-environment.d.ts +0 -1
  20. package/dist/docker-compose-environment/docker-compose-environment.js +3 -11
  21. package/dist/docker-ignore.d.ts +1 -1
  22. package/dist/docker-ignore.js +2 -1
  23. package/dist/generic-container/generic-container.d.ts +6 -2
  24. package/dist/generic-container/generic-container.js +23 -7
  25. package/dist/log-system-diagnostics.js +2 -21
  26. package/dist/modules/kafka/kafka-container.d.ts +6 -17
  27. package/dist/modules/kafka/kafka-container.js +56 -68
  28. package/dist/reaper.d.ts +1 -0
  29. package/dist/reaper.js +18 -12
  30. package/dist/test-container.d.ts +2 -1
  31. package/dist/testcontainers-properties-file.d.ts +1 -0
  32. package/dist/testcontainers-properties-file.js +35 -0
  33. package/package.json +4 -2
package/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
  [![Node.js CI](https://github.com/testcontainers/testcontainers-node/actions/workflows/node.js.yml/badge.svg?branch=master)](https://github.com/testcontainers/testcontainers-node/actions/workflows/node.js.yml)
6
6
  [![npm version](https://badge.fury.io/js/testcontainers.svg)](https://www.npmjs.com/package/testcontainers)
7
7
  [![npm version](https://img.shields.io/npm/dm/testcontainers.svg)](https://www.npmjs.com/package/testcontainers)
8
+ [![Thundra Foresight](https://thundra-assets-prod.s3.us-west-2.amazonaws.com/images/badges/thundra-foresight-badge-enabled.svg)](https://foresight.thundra.live/testRuns/57175ecb-d5ac-4129-957a-9dd617fd27d0)
9
+ [![Foresight Test Summary](https://foresight.service.thundra.us/public/api/v1/badge/test/57175ecb-d5ac-4129-957a-9dd617fd27d0)](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 output
20
- - `DEBUG=testcontainers:containers` See container output
21
- - `DEBUG=testcontainers*` See all output
22
- - `DOCKER_HOST=tcp://docker:2375` Daemon socket to connect to
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 other [auxiliary containers](#auxiliary-containers) that need to perform Docker actions
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(["top"])
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(["top"])
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(["top"])
274
+ .withCmd(["sleep", "infinity"])
267
275
  .withNetworkMode(network.getName())
268
276
  .start();
269
277
 
270
278
  const fooContainer = await new GenericContainer("alpine")
271
- .withCmd(["top"])
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(["top"])
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 must be run with privileged mode; in CI environments such as Bit Bucket where this isn't supported, ryuk can be
638
- disabled by setting the environment variable `TESTCONTAINERS_RYUK_DISABLED` to `true`.
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
+
@@ -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
  }
@@ -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;
@@ -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 logger_1 = require("../logger");
9
- logger_1.log.debug("Creating new Docker instance");
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
- boundPorts: BoundPorts;
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.boundPorts),
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.boundPorts),
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 = (boundPorts) => {
49
+ const getExposedPorts = (exposedPorts) => {
50
50
  const dockerodeExposedPorts = {};
51
- for (const [internalPort] of boundPorts.iterator()) {
52
- dockerodeExposedPorts[internalPort.toString()] = {};
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 = (boundPorts) => {
59
+ const getPortBindings = (exposedPorts) => {
60
60
  const dockerodePortBindings = {};
61
- for (const [internalPort, hostPort] of boundPorts.iterator()) {
62
- dockerodePortBindings[internalPort.toString()] = [{ HostPort: hostPort.toString() }];
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
- return yield new Promise((resolve) => {
23
- const chunks = [];
24
- stream.on("data", (chunk) => chunks.push(chunk));
25
- const interval = setInterval(() => __awaiter(void 0, void 0, void 0, function* () {
26
- const { running, exitCode } = yield inspectExec(exec);
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
- internalPorts: Port[];
7
- hostPorts: Port[];
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
- hostPorts: getHostPorts(inspectResult),
19
- internalPorts: getInternalPorts(inspectResult),
20
- name: getName(inspectResult),
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 getHostPorts = (inspectInfo) => Object.values(inspectInfo.NetworkSettings.Ports)
33
- .filter((portsArray) => portsArray !== null)
34
- .map((portsArray) => Number(portsArray[0].HostPort));
35
- const getInternalPorts = (inspectInfo) => Object.keys(inspectInfo.NetworkSettings.Ports).map((port) => Number(port.split("/")[0]));
36
- const getName = (inspectInfo) => inspectInfo.Name;
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
- logger_1.log.info(`Docker host strategy ${hostStrategyName}: ${result}`);
28
- return result;
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* () {
@@ -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,2 @@
1
+ export { upMany, upAll, down, stop, version } from "docker-compose";
2
+ export type { IDockerComposeOptions } from "docker-compose";
@@ -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 = this.getBoundPorts(startedContainer);
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);
@@ -1 +1 @@
1
- export declare const findDockerIgnoreFiles: (context: string) => Promise<Set<string>>;
1
+ export declare const findDockerIgnoreFiles: (context: string, dockerfileName: string) => Promise<Set<string>>;
@@ -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.preCreate) {
69
- yield this.preCreate(boundPorts);
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
- boundPorts,
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({ containerId: container.id, networkId: portForwarderNetworkId, networkAliases: [] });
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
- return new started_generic_container_1.StartedGenericContainer(container, yield docker_host_1.dockerHost, inspectResult, boundPorts, inspectResult.name);
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 dockerCompose = __importStar(require("docker-compose"));
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 dockerCompose.version()).data.version,
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, StoppedTestContainer } from "../..";
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
- private network?;
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 preCreate(boundPorts: BoundPorts): Promise<void>;
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
- private readonly network?;
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.ZK_IMAGE = exports.KAFKA_IMAGE = void 0;
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, host, zooKeeperImage = exports.ZK_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.host = host === undefined ? this.uuid.nextUuid() : host;
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
- preCreate(boundPorts) {
46
+ preStart() {
40
47
  return __awaiter(this, void 0, void 0, function* () {
41
- const kafkaInternalPort = boundPorts.getBinding(KAFKA_PORT);
42
- this.withEnv("KAFKA_ADVERTISED_LISTENERS", `PLAINTEXT://${this.host}:${KAFKA_BROKER_PORT},EXTERNAL_LISTENER://${yield docker_host_1.dockerHost}:${kafkaInternalPort}`);
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
- this.withName(this.host)
51
- .withStartupTimeout(180000)
52
- .withEnv("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP", "PLAINTEXT:PLAINTEXT,EXTERNAL_LISTENER:PLAINTEXT")
53
- .withEnv("KAFKA_INTER_BROKER_LISTENER_NAME", "PLAINTEXT")
54
- .withEnv("KAFKA_BROKER_ID", "1")
55
- .withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", "1")
56
- .withEnv("KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE", "false")
57
- .withEnv("KAFKA_LISTENERS", `PLAINTEXT://0.0.0.0:${KAFKA_BROKER_PORT},EXTERNAL_LISTENER://0.0.0.0:${KAFKA_PORT}`);
58
- let zooKeeperPort;
59
- if (this.isZooKeeperProvided) {
60
- this.withEnv("KAFKA_ZOOKEEPER_CONNECT", `${this.zooKeeperHost}:${this.zooKeeperPort}`);
61
- }
62
- else {
63
- const zooKeeperHost = this.uuid.nextUuid();
64
- zooKeeperPort = yield this.portGenerator.generatePort();
65
- this.withEnv("KAFKA_ZOOKEEPER_CONNECT", `${zooKeeperHost}:${zooKeeperPort}`);
66
- const zookeeperContainer = new generic_container_1.GenericContainer(this.zooKeeperImage)
67
- .withName(zooKeeperHost)
68
- .withEnv("ZOOKEEPER_CLIENT_PORT", zooKeeperPort.toString());
69
- if (this.networkMode !== undefined) {
70
- zookeeperContainer.withNetworkMode(this.networkMode);
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, network, zooKeeperContainer, zooKeeperPort) {
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
@@ -8,6 +8,7 @@ export declare class ReaperInstance {
8
8
  static getInstance(): Promise<Reaper>;
9
9
  static stopInstance(): Promise<void>;
10
10
  private static isEnabled;
11
+ private static isPrivileged;
11
12
  private static createDisabledInstance;
12
13
  private static createRealInstance;
13
14
  }
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 = yield new generic_container_1.GenericContainer(images_1.REAPER_IMAGE)
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
- .withPrivilegedMode()
85
- .start();
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 = container.getMappedPort(8080);
88
- logger_1.log.debug(`Connecting to Reaper ${container.getId()} on ${host}:${port}`);
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 ${container.getId()} socket timed out`))
93
- .on("error", (err) => logger_1.log.error(`Reaper ${container.getId()} socket error: ${err}`))
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 ${container.getId()} closed with error`);
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 ${container.getId()} closed`);
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 ${container.getId()}`);
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(container, socket);
112
+ const reaper = new RealReaper(startedContainer, socket);
107
113
  resolve(reaper);
108
114
  });
109
115
  });
@@ -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": "7.23.0",
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 --maxWorkers=2",
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",