testcontainers 9.5.0 → 9.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/package.json +14 -14
  2. package/dist/src/docker/docker-client-config.js +42 -36
  3. package/dist/src/docker/docker-client.d.ts +2 -0
  4. package/dist/src/docker/docker-client.js +6 -5
  5. package/dist/src/docker/functions/container/attach-container.js +1 -1
  6. package/dist/src/docker/functions/container/create-container.js +6 -4
  7. package/dist/src/docker/functions/container/exec-container.d.ts +2 -1
  8. package/dist/src/docker/functions/container/exec-container.js +15 -8
  9. package/dist/src/docker/functions/container/get-container.js +1 -1
  10. package/dist/src/docker/functions/container/inspect-container.js +1 -1
  11. package/dist/src/docker/functions/container/put-container-archive.js +5 -2
  12. package/dist/src/docker/functions/container/remove-container.js +1 -1
  13. package/dist/src/docker/functions/container/restart-container.js +1 -1
  14. package/dist/src/docker/functions/container/start-container.js +1 -1
  15. package/dist/src/docker/functions/container/stop-container.js +2 -2
  16. package/dist/src/docker/functions/create-labels.js +0 -3
  17. package/dist/src/docker/functions/image/build-image.js +8 -3
  18. package/dist/src/docker/functions/image/image-exists.js +11 -8
  19. package/dist/src/docker/functions/image/pull-image.js +3 -3
  20. package/dist/src/docker/functions/network/connect-network.js +3 -2
  21. package/dist/src/docker/functions/network/create-network.js +2 -1
  22. package/dist/src/docker/functions/network/remove-network.js +3 -2
  23. package/dist/src/docker/functions/run-in-container.js +6 -6
  24. package/dist/src/docker/pull-stream-parser.d.ts +0 -1
  25. package/dist/src/docker/pull-stream-parser.js +2 -41
  26. package/dist/src/docker/resolve-host.d.ts +2 -1
  27. package/dist/src/docker/resolve-host.js +7 -6
  28. package/dist/src/docker-compose/default-docker-compose-options.js +4 -2
  29. package/dist/src/docker-compose/docker-compose-options.d.ts +2 -0
  30. package/dist/src/docker-compose/functions/docker-compose-down.js +1 -1
  31. package/dist/src/docker-compose/functions/docker-compose-pull.js +4 -4
  32. package/dist/src/docker-compose/functions/docker-compose-stop.js +1 -1
  33. package/dist/src/docker-compose/functions/docker-compose-up.js +1 -1
  34. package/dist/src/docker-compose-environment/docker-compose-environment.d.ts +1 -1
  35. package/dist/src/docker-compose-environment/docker-compose-environment.js +11 -10
  36. package/dist/src/generic-container/generic-container-builder.js +1 -1
  37. package/dist/src/generic-container/generic-container.d.ts +6 -2
  38. package/dist/src/generic-container/generic-container.js +30 -17
  39. package/dist/src/generic-container/started-generic-container.d.ts +2 -0
  40. package/dist/src/generic-container/started-generic-container.js +26 -7
  41. package/dist/src/images.js +1 -1
  42. package/dist/src/logger.d.ts +20 -31
  43. package/dist/src/logger.js +32 -42
  44. package/dist/src/modules/elasticsearch/elasticsearch-container.js +1 -1
  45. package/dist/src/modules/nats/nats-container.js +1 -1
  46. package/dist/src/network.d.ts +1 -0
  47. package/dist/src/network.js +11 -5
  48. package/dist/src/port-check.d.ts +4 -1
  49. package/dist/src/port-check.js +10 -4
  50. package/dist/src/port-forwarder.js +5 -3
  51. package/dist/src/reaper.js +11 -7
  52. package/dist/src/registry-auth-locator/credential-provider.js +3 -5
  53. package/dist/src/registry-auth-locator/get-auth-config.js +3 -3
  54. package/dist/src/system-info.js +2 -5
  55. package/dist/src/test-container.d.ts +2 -6
  56. package/dist/src/uuid.js +1 -1
  57. package/dist/src/wait-for-container.d.ts +1 -1
  58. package/dist/src/wait-for-container.js +6 -6
  59. package/dist/src/wait-strategy/composite-wait-strategy.d.ts +11 -0
  60. package/dist/src/wait-strategy/composite-wait-strategy.js +54 -0
  61. package/dist/src/wait-strategy/health-check-wait-strategy.js +7 -3
  62. package/dist/src/wait-strategy/host-port-wait-strategy.d.ts +1 -5
  63. package/dist/src/wait-strategy/host-port-wait-strategy.js +21 -17
  64. package/dist/src/wait-strategy/http-wait-strategy.d.ts +1 -1
  65. package/dist/src/wait-strategy/http-wait-strategy.js +8 -2
  66. package/dist/src/wait-strategy/log-wait-strategy.d.ts +1 -1
  67. package/dist/src/wait-strategy/log-wait-strategy.js +8 -7
  68. package/dist/src/wait-strategy/shell-wait-strategy.js +7 -4
  69. package/dist/src/wait-strategy/startup-check-strategy.js +6 -1
  70. package/dist/src/wait-strategy/wait-strategy.d.ts +8 -3
  71. package/dist/src/wait-strategy/wait-strategy.js +8 -0
  72. package/dist/src/wait-strategy/wait.d.ts +3 -0
  73. package/dist/src/wait-strategy/wait.js +8 -0
  74. package/package.json +14 -14
  75. package/dist/src/docker/functions/image/list-images.d.ts +0 -3
  76. package/dist/src/docker/functions/image/list-images.js +0 -34
  77. package/dist/src/wait-strategy/default-wait-strategy.d.ts +0 -3
  78. package/dist/src/wait-strategy/default-wait-strategy.js +0 -7
@@ -3,4 +3,5 @@
3
3
  /// <reference types="node" />
4
4
  /// <reference types="node" />
5
5
  import Dockerode from "dockerode";
6
- export declare const resolveHost: (dockerode: Dockerode, indexServerAddress: string, uri: string, env?: NodeJS.ProcessEnv) => Promise<string>;
6
+ import { Provider } from "./docker-client";
7
+ export declare const resolveHost: (dockerode: Dockerode, provider: Provider, indexServerAddress: string, uri: string, env?: NodeJS.ProcessEnv) => Promise<string>;
@@ -14,7 +14,7 @@ const url_1 = require("url");
14
14
  const logger_1 = require("../logger");
15
15
  const run_in_container_1 = require("./functions/run-in-container");
16
16
  const fs_1 = require("fs");
17
- const resolveHost = (dockerode, indexServerAddress, uri, env = process.env) => __awaiter(void 0, void 0, void 0, function* () {
17
+ const resolveHost = (dockerode, provider, indexServerAddress, uri, env = process.env) => __awaiter(void 0, void 0, void 0, function* () {
18
18
  if (env.TESTCONTAINERS_HOST_OVERRIDE !== undefined) {
19
19
  return env.TESTCONTAINERS_HOST_OVERRIDE;
20
20
  }
@@ -27,7 +27,8 @@ const resolveHost = (dockerode, indexServerAddress, uri, env = process.env) => _
27
27
  case "unix:":
28
28
  case "npipe:": {
29
29
  if (isInContainer()) {
30
- const gateway = yield findGateway(dockerode);
30
+ const networkName = provider === "podman" ? "podman" : "bridge";
31
+ const gateway = yield findGateway(dockerode, networkName);
31
32
  if (gateway !== undefined) {
32
33
  return gateway;
33
34
  }
@@ -43,14 +44,14 @@ const resolveHost = (dockerode, indexServerAddress, uri, env = process.env) => _
43
44
  }
44
45
  });
45
46
  exports.resolveHost = resolveHost;
46
- const findGateway = (dockerode) => __awaiter(void 0, void 0, void 0, function* () {
47
+ const findGateway = (dockerode, networkName) => __awaiter(void 0, void 0, void 0, function* () {
47
48
  var _a, _b, _c;
48
- logger_1.log.debug(`Checking gateway for Docker host`);
49
- const inspectResult = yield dockerode.getNetwork("bridge").inspect();
49
+ logger_1.log.debug(`Checking gateway for Docker host...`);
50
+ const inspectResult = yield dockerode.getNetwork(networkName).inspect();
50
51
  return (_c = (_b = (_a = inspectResult === null || inspectResult === void 0 ? void 0 : inspectResult.IPAM) === null || _a === void 0 ? void 0 : _a.Config) === null || _b === void 0 ? void 0 : _b.find((config) => config.Gateway !== undefined)) === null || _c === void 0 ? void 0 : _c.Gateway;
51
52
  });
52
53
  const findDefaultGateway = (dockerode, indexServerAddress) => __awaiter(void 0, void 0, void 0, function* () {
53
- logger_1.log.debug(`Checking default gateway for Docker host`);
54
+ logger_1.log.debug(`Checking default gateway for Docker host...`);
54
55
  return (0, run_in_container_1.runInContainer)(dockerode, indexServerAddress, "alpine:3.14", [
55
56
  "sh",
56
57
  "-c",
@@ -26,17 +26,19 @@ const logger_1 = require("../logger");
26
26
  const os_1 = require("os");
27
27
  const type_guards_1 = require("../type-guards");
28
28
  const defaultDockerComposeOptions = (_a) => __awaiter(void 0, void 0, void 0, function* () {
29
+ var _b;
29
30
  var { environment = {} } = _a, options = __rest(_a, ["environment"]);
30
31
  const { composeEnvironment } = yield (0, docker_client_1.dockerClient)();
32
+ const log = (_b = options.logger) !== null && _b !== void 0 ? _b : logger_1.composeLog;
31
33
  return {
32
34
  log: false,
33
- callback: logger_1.log.enabled()
35
+ callback: log.enabled()
34
36
  ? (chunk) => {
35
37
  chunk
36
38
  .toString()
37
39
  .split(os_1.EOL)
38
40
  .filter(type_guards_1.isNotEmptyString)
39
- .forEach((line) => logger_1.log.trace(line.trim()));
41
+ .forEach((line) => log.trace(line.trim()));
40
42
  }
41
43
  : undefined,
42
44
  cwd: options.filePath,
@@ -1,4 +1,5 @@
1
1
  import { Environment } from "../docker/types";
2
+ import { Logger } from "../logger";
2
3
  export type DockerComposeOptions = {
3
4
  filePath: string;
4
5
  files: string | string[];
@@ -6,4 +7,5 @@ export type DockerComposeOptions = {
6
7
  commandOptions?: string[];
7
8
  composeOptions?: string[];
8
9
  environment?: Environment;
10
+ logger?: Logger;
9
11
  };
@@ -14,7 +14,7 @@ const logger_1 = require("../../logger");
14
14
  const default_docker_compose_options_1 = require("../default-docker-compose-options");
15
15
  const docker_compose_1 = require("docker-compose");
16
16
  const dockerComposeDown = (options, downOptions) => __awaiter(void 0, void 0, void 0, function* () {
17
- logger_1.log.info(`Downing DockerCompose environment`);
17
+ logger_1.log.info(`Downing DockerCompose environment...`);
18
18
  try {
19
19
  yield (0, docker_compose_1.down)(Object.assign(Object.assign({}, (yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options))), { commandOptions: commandOptions(downOptions) }));
20
20
  logger_1.log.info(`Downed DockerCompose environment`);
@@ -16,12 +16,12 @@ const default_docker_compose_options_1 = require("../default-docker-compose-opti
16
16
  const dockerComposePull = (options, services) => __awaiter(void 0, void 0, void 0, function* () {
17
17
  try {
18
18
  if (services) {
19
- logger_1.log.info(`Pulling DockerCompose environment images for ${services.join(", ")}`);
20
- yield (0, docker_compose_1.pullMany)(services, yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options));
19
+ logger_1.log.info(`Pulling DockerCompose environment images "${services.join('", "')}"...`);
20
+ yield (0, docker_compose_1.pullMany)(services, yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(Object.assign(Object.assign({}, options), { logger: logger_1.pullLog })));
21
21
  }
22
22
  else {
23
- logger_1.log.info(`Pulling DockerCompose environment images`);
24
- yield (0, docker_compose_1.pullAll)(yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options));
23
+ logger_1.log.info(`Pulling DockerCompose environment images...`);
24
+ yield (0, docker_compose_1.pullAll)(yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(Object.assign(Object.assign({}, options), { logger: logger_1.pullLog })));
25
25
  }
26
26
  logger_1.log.info(`Pulled DockerCompose environment`);
27
27
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -14,7 +14,7 @@ const logger_1 = require("../../logger");
14
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
- logger_1.log.info(`Stopping DockerCompose environment`);
17
+ logger_1.log.info(`Stopping DockerCompose environment...`);
18
18
  try {
19
19
  yield (0, docker_compose_1.stop)(yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options));
20
20
  logger_1.log.info(`Stopped DockerCompose environment`);
@@ -15,7 +15,7 @@ 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* () {
18
- logger_1.log.info(`Upping DockerCompose environment`);
18
+ logger_1.log.info(`Upping DockerCompose environment...`);
19
19
  try {
20
20
  if (services) {
21
21
  yield (0, docker_compose_1.upMany)(services, yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options));
@@ -14,7 +14,7 @@ export declare class DockerComposeEnvironment {
14
14
  private environment;
15
15
  private pullPolicy;
16
16
  private waitStrategy;
17
- private startupTimeout;
17
+ private startupTimeout?;
18
18
  constructor(composeFilePath: string, composeFiles: string | string[], uuid?: Uuid);
19
19
  withBuild(): this;
20
20
  withEnvironment(environment: Environment): this;
@@ -25,9 +25,9 @@ const started_docker_compose_environment_1 = require("./started-docker-compose-e
25
25
  const docker_compose_down_1 = require("../docker-compose/functions/docker-compose-down");
26
26
  const docker_compose_up_1 = require("../docker-compose/functions/docker-compose-up");
27
27
  const wait_for_container_1 = require("../wait-for-container");
28
- const default_wait_strategy_1 = require("../wait-strategy/default-wait-strategy");
29
28
  const pull_policy_1 = require("../pull-policy");
30
29
  const docker_compose_pull_1 = require("../docker-compose/functions/docker-compose-pull");
30
+ const wait_1 = require("../wait-strategy/wait");
31
31
  class DockerComposeEnvironment {
32
32
  constructor(composeFilePath, composeFiles, uuid = new uuid_1.RandomUuid()) {
33
33
  this.build = false;
@@ -37,7 +37,6 @@ class DockerComposeEnvironment {
37
37
  this.environment = {};
38
38
  this.pullPolicy = new pull_policy_1.DefaultPullPolicy();
39
39
  this.waitStrategy = {};
40
- this.startupTimeout = 60000;
41
40
  this.composeFilePath = composeFilePath;
42
41
  this.composeFiles = composeFiles;
43
42
  this.projectName = `testcontainers-${uuid.nextUuid()}`;
@@ -77,7 +76,7 @@ class DockerComposeEnvironment {
77
76
  }
78
77
  up(services) {
79
78
  return __awaiter(this, void 0, void 0, function* () {
80
- logger_1.log.info(`Starting DockerCompose environment: ${this.projectName}`);
79
+ logger_1.log.info(`Starting DockerCompose environment "${this.projectName}"...`);
81
80
  (yield reaper_1.ReaperInstance.getInstance()).addProject(this.projectName);
82
81
  const options = {
83
82
  filePath: this.composeFilePath,
@@ -105,26 +104,28 @@ class DockerComposeEnvironment {
105
104
  ...containerNames,
106
105
  startedContainer.Names.join(", "),
107
106
  ], []);
108
- logger_1.log.info(`Started the following containers: ${startedContainerNames.join(", ")}`);
107
+ logger_1.log.info(`Started containers "${startedContainerNames.join('", "')}"`);
109
108
  const startedGenericContainers = (yield Promise.all(startedContainers.map((startedContainer) => __awaiter(this, void 0, void 0, function* () {
110
109
  const container = yield (0, get_container_1.getContainerById)(startedContainer.Id);
111
110
  const containerName = (0, container_name_resolver_1.resolveContainerName)(this.projectName, startedContainer.Names[0]);
112
111
  const { host, hostIps } = yield (0, docker_client_1.dockerClient)();
113
112
  const inspectResult = yield (0, inspect_container_1.inspectContainer)(container);
114
113
  const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, inspectResult);
115
- const waitStrategy = (this.waitStrategy[containerName] ? this.waitStrategy[containerName] : (0, default_wait_strategy_1.defaultWaitStrategy)(host, container)).withStartupTimeout(this.startupTimeout);
114
+ const waitStrategy = this.waitStrategy[containerName]
115
+ ? this.waitStrategy[containerName]
116
+ : wait_1.Wait.forListeningPorts();
117
+ if (this.startupTimeout !== undefined) {
118
+ waitStrategy.withStartupTimeout(this.startupTimeout);
119
+ }
116
120
  if (logger_1.containerLog.enabled()) {
117
121
  (yield (0, container_logs_1.containerLogs)(container))
118
122
  .on("data", (data) => logger_1.containerLog.trace(`${containerName}: ${data.trim()}`))
119
123
  .on("err", (data) => logger_1.containerLog.error(`${containerName}: ${data.trim()}`));
120
124
  }
121
125
  try {
122
- logger_1.log.info(`Waiting for container ${containerName} to be ready`);
123
- yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, host, boundPorts);
124
- logger_1.log.info(`Container ${containerName} is ready`);
126
+ yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, boundPorts);
125
127
  }
126
128
  catch (err) {
127
- logger_1.log.error(`Container ${containerName} failed to be ready: ${err}`);
128
129
  try {
129
130
  yield (0, docker_compose_down_1.dockerComposeDown)(options, { removeVolumes: true, timeout: 0 });
130
131
  }
@@ -138,7 +139,7 @@ class DockerComposeEnvironment {
138
139
  const containerName = startedGenericContainer.getName();
139
140
  return Object.assign(Object.assign({}, map), { [containerName]: startedGenericContainer });
140
141
  }, {});
141
- logger_1.log.info(`DockerCompose environment started: ${Object.keys(startedGenericContainers).join(", ")}`);
142
+ logger_1.log.info(`DockerCompose environment started`);
142
143
  return new started_docker_compose_environment_1.StartedDockerComposeEnvironment(startedGenericContainers, Object.assign(Object.assign({}, options), { composeOptions, environment: this.environment }));
143
144
  });
144
145
  }
@@ -52,7 +52,7 @@ class GenericContainerBuilder {
52
52
  const imageName = docker_image_name_1.DockerImageName.fromString(image);
53
53
  yield reaper_1.ReaperInstance.getInstance();
54
54
  const dockerfile = path_1.default.resolve(this.context, this.dockerfileName);
55
- logger_1.log.debug(`Preparing to build Dockerfile: ${dockerfile}`);
55
+ logger_1.log.debug(`Preparing to build Dockerfile "${dockerfile}" as image "${imageName}"...`);
56
56
  const imageNames = yield (0, dockerfile_parser_1.getDockerfileImages)(dockerfile, this.buildArgs);
57
57
  const { dockerode, indexServerAddress } = yield (0, docker_client_1.dockerClient)();
58
58
  const registryConfig = yield this.getRegistryConfig(indexServerAddress, imageNames);
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import archiver from "archiver";
2
3
  import { BoundPorts } from "../bound-ports";
3
4
  import { PortWithOptionalBinding } from "../port";
@@ -9,16 +10,18 @@ import { CreateContainerOptions } from "../docker/functions/container/create-con
9
10
  import { InspectResult } from "../docker/functions/container/inspect-container";
10
11
  import { GenericContainerBuilder } from "./generic-container-builder";
11
12
  import { StartedNetwork } from "../network";
13
+ import { Readable } from "stream";
12
14
  export declare class GenericContainer implements TestContainer {
13
15
  readonly image: string;
14
16
  static fromDockerfile(context: string, dockerfileName?: string): GenericContainerBuilder;
15
17
  protected opts: CreateContainerOptions;
16
- protected startupTimeout: number;
17
- protected waitStrategy?: WaitStrategy;
18
+ protected startupTimeout?: number;
19
+ protected waitStrategy: WaitStrategy;
18
20
  protected tarToCopy?: archiver.Archiver;
19
21
  protected networkMode?: string;
20
22
  protected networkAliases: string[];
21
23
  protected pullPolicy: PullPolicy;
24
+ protected logConsumer?: (stream: Readable) => unknown;
22
25
  constructor(image: string);
23
26
  /**
24
27
  * @deprecated Since version 9.4.0. Will be removed in version 10.0.0. Use `beforeContainerStarted` instead.
@@ -65,4 +68,5 @@ export declare class GenericContainer implements TestContainer {
65
68
  protected getTarToCopy(): archiver.Archiver;
66
69
  withWorkingDir(workingDir: string): this;
67
70
  withResourcesQuota({ memory, cpu }: ResourcesQuota): this;
71
+ withLogConsumer(logConsumer: (stream: Readable) => unknown): this;
68
72
  }
@@ -36,7 +36,7 @@ const get_container_1 = require("../docker/functions/container/get-container");
36
36
  const labels_1 = require("../labels");
37
37
  const wait_for_container_1 = require("../wait-for-container");
38
38
  const create_container_options_1 = require("./create-container-options");
39
- const default_wait_strategy_1 = require("../wait-strategy/default-wait-strategy");
39
+ const wait_1 = require("../wait-strategy/wait");
40
40
  const reusableContainerCreationLock = new async_lock_1.default();
41
41
  class GenericContainer {
42
42
  static fromDockerfile(context, dockerfileName = "Dockerfile") {
@@ -44,7 +44,7 @@ class GenericContainer {
44
44
  }
45
45
  constructor(image) {
46
46
  this.image = image;
47
- this.startupTimeout = 60000;
47
+ this.waitStrategy = wait_1.Wait.forListeningPorts();
48
48
  this.networkAliases = [];
49
49
  this.pullPolicy = new pull_policy_1.DefaultPullPolicy();
50
50
  const imageName = docker_image_name_1.DockerImageName.fromString(image);
@@ -79,14 +79,14 @@ class GenericContainer {
79
79
  if (this.opts.reusable) {
80
80
  const containerHash = (0, hash_1.hash)(JSON.stringify(this.opts));
81
81
  this.opts.labels = Object.assign(Object.assign({}, this.opts.labels), { [labels_1.LABEL_TESTCONTAINERS_CONTAINER_HASH]: containerHash });
82
- logger_1.log.debug(`Container reuse has been enabled, hash: ${containerHash}`);
82
+ logger_1.log.debug(`Container reuse has been enabled with hash "${containerHash}"`);
83
83
  // We might have several async processes try to create a reusable container
84
84
  // at once, to avoid possibly creating too many of these, use a lock
85
85
  // on the containerHash, this ensures that only single reusable instance is created
86
86
  return reusableContainerCreationLock.acquire(containerHash, () => __awaiter(this, void 0, void 0, function* () {
87
87
  const container = yield (0, get_container_1.getContainerByHash)(containerHash);
88
88
  if (container !== undefined) {
89
- logger_1.log.debug(`Found container to reuse with hash: ${containerHash}`);
89
+ logger_1.log.debug(`Found container to reuse with hash "${containerHash}"`, { containerId: container.id });
90
90
  return this.reuseContainer(container);
91
91
  }
92
92
  else {
@@ -101,17 +101,18 @@ class GenericContainer {
101
101
  });
102
102
  }
103
103
  reuseContainer(container) {
104
- var _a;
105
104
  return __awaiter(this, void 0, void 0, function* () {
106
105
  const { host, hostIps } = yield (0, docker_client_1.dockerClient)();
107
106
  const inspectResult = yield (0, inspect_container_1.inspectContainer)(container);
108
107
  const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, inspectResult).filter(this.opts.exposedPorts);
109
- const waitStrategy = ((_a = this.waitStrategy) !== null && _a !== void 0 ? _a : (0, default_wait_strategy_1.defaultWaitStrategy)(host, container)).withStartupTimeout(this.startupTimeout);
108
+ if (this.startupTimeout !== undefined) {
109
+ this.waitStrategy.withStartupTimeout(this.startupTimeout);
110
+ }
110
111
  if (this.containerStarting) {
111
112
  yield this.containerStarting(inspectResult, true);
112
113
  }
113
- yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, host, boundPorts);
114
- const startedContainer = new started_generic_container_1.StartedGenericContainer(container, host, inspectResult, boundPorts, inspectResult.name, waitStrategy);
114
+ yield (0, wait_for_container_1.waitForContainer)(container, this.waitStrategy, boundPorts);
115
+ const startedContainer = new started_generic_container_1.StartedGenericContainer(container, host, inspectResult, boundPorts, inspectResult.name, this.waitStrategy);
115
116
  if (this.containerStarted) {
116
117
  yield this.containerStarted(startedContainer, inspectResult, true);
117
118
  }
@@ -122,7 +123,6 @@ class GenericContainer {
122
123
  });
123
124
  }
124
125
  startContainer(createContainerOptions) {
125
- var _a;
126
126
  return __awaiter(this, void 0, void 0, function* () {
127
127
  const container = yield (0, create_container_1.createContainer)(createContainerOptions);
128
128
  if (!this.opts.imageName.isHelperContainer() && port_forwarder_1.PortForwarderInstance.isRunning()) {
@@ -148,25 +148,34 @@ class GenericContainer {
148
148
  this.tarToCopy.finalize();
149
149
  yield (0, put_container_archive_1.putContainerArchive)({ container, stream: this.tarToCopy, containerPath: "/" });
150
150
  }
151
- logger_1.log.info(`Starting container ${this.opts.imageName} with ID: ${container.id}`);
151
+ logger_1.log.info(`Starting container for image "${this.opts.imageName}"...`, { containerId: container.id });
152
152
  if (this.containerCreated) {
153
153
  yield this.containerCreated(container.id);
154
154
  }
155
155
  yield (0, start_container_1.startContainer)(container);
156
+ logger_1.log.info(`Started container for image "${this.opts.imageName}"`, { containerId: container.id });
156
157
  const { host, hostIps } = yield (0, docker_client_1.dockerClient)();
157
158
  const inspectResult = yield (0, inspect_container_1.inspectContainer)(container);
158
159
  const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, inspectResult).filter(this.opts.exposedPorts);
159
- const waitStrategy = ((_a = this.waitStrategy) !== null && _a !== void 0 ? _a : (0, default_wait_strategy_1.defaultWaitStrategy)(host, container)).withStartupTimeout(this.startupTimeout);
160
- if (logger_1.containerLog.enabled()) {
161
- (yield (0, container_logs_1.containerLogs)(container))
162
- .on("data", (data) => logger_1.containerLog.trace(`${container.id}: ${data.trim()}`))
163
- .on("err", (data) => logger_1.containerLog.error(`${container.id}: ${data.trim()}`));
160
+ if (this.startupTimeout !== undefined) {
161
+ this.waitStrategy.withStartupTimeout(this.startupTimeout);
162
+ }
163
+ if (logger_1.containerLog.enabled() || this.logConsumer !== undefined) {
164
+ const logStream = yield (0, container_logs_1.containerLogs)(container);
165
+ if (this.logConsumer !== undefined) {
166
+ this.logConsumer(logStream);
167
+ }
168
+ if (logger_1.containerLog.enabled()) {
169
+ logStream
170
+ .on("data", (data) => logger_1.containerLog.trace(data.trim(), { containerId: container.id }))
171
+ .on("err", (data) => logger_1.containerLog.error(data.trim(), { containerId: container.id }));
172
+ }
164
173
  }
165
174
  if (this.containerStarting) {
166
175
  yield this.containerStarting(inspectResult, false);
167
176
  }
168
- yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, host, boundPorts);
169
- const startedContainer = new started_generic_container_1.StartedGenericContainer(container, host, inspectResult, boundPorts, inspectResult.name, waitStrategy);
177
+ yield (0, wait_for_container_1.waitForContainer)(container, this.waitStrategy, boundPorts);
178
+ const startedContainer = new started_generic_container_1.StartedGenericContainer(container, host, inspectResult, boundPorts, inspectResult.name, this.waitStrategy);
170
179
  if (this.containerStarted) {
171
180
  yield this.containerStarted(startedContainer, inspectResult, false);
172
181
  }
@@ -305,5 +314,9 @@ class GenericContainer {
305
314
  this.opts.resourcesQuota = { memory: ram, cpu: cpuQuota };
306
315
  return this;
307
316
  }
317
+ withLogConsumer(logConsumer) {
318
+ this.logConsumer = logConsumer;
319
+ return this;
320
+ }
308
321
  }
309
322
  exports.GenericContainer = GenericContainer;
@@ -13,6 +13,8 @@ export declare class StartedGenericContainer implements StartedTestContainer {
13
13
  private boundPorts;
14
14
  private readonly name;
15
15
  private readonly waitStrategy;
16
+ private stoppedContainer?;
17
+ private stopContainerLock;
16
18
  constructor(container: Dockerode.Container, host: string, inspectResult: InspectResult, boundPorts: BoundPorts, name: string, waitStrategy: WaitStrategy);
17
19
  protected containerIsStopping?(): Promise<void>;
18
20
  stop(options?: Partial<StopOptions>): Promise<StoppedTestContainer>;
@@ -8,6 +8,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.StartedGenericContainer = void 0;
13
16
  const inspect_container_1 = require("../docker/functions/container/inspect-container");
@@ -21,6 +24,7 @@ const stop_container_1 = require("../docker/functions/container/stop-container")
21
24
  const restart_container_1 = require("../docker/functions/container/restart-container");
22
25
  const wait_for_container_1 = require("../wait-for-container");
23
26
  const docker_client_1 = require("../docker/docker-client");
27
+ const async_lock_1 = __importDefault(require("async-lock"));
24
28
  class StartedGenericContainer {
25
29
  constructor(container, host, inspectResult, boundPorts, name, waitStrategy) {
26
30
  this.container = container;
@@ -29,38 +33,47 @@ class StartedGenericContainer {
29
33
  this.boundPorts = boundPorts;
30
34
  this.name = name;
31
35
  this.waitStrategy = waitStrategy;
36
+ this.stopContainerLock = new async_lock_1.default();
32
37
  }
33
38
  stop(options = {}) {
34
39
  return __awaiter(this, void 0, void 0, function* () {
35
- return this.stopContainer(options);
40
+ return this.stopContainerLock.acquire("stop", () => __awaiter(this, void 0, void 0, function* () {
41
+ if (this.stoppedContainer) {
42
+ return this.stoppedContainer;
43
+ }
44
+ this.stoppedContainer = yield this.stopContainer(options);
45
+ return this.stoppedContainer;
46
+ }));
36
47
  });
37
48
  }
38
49
  restart(options = {}) {
39
50
  return __awaiter(this, void 0, void 0, function* () {
40
51
  const resolvedOptions = Object.assign({ timeout: 0 }, options);
41
- logger_1.log.info(`Restarting container with ID: ${this.container.id}`);
52
+ logger_1.log.info(`Restarting container...`, { containerId: this.container.id });
42
53
  yield (0, restart_container_1.restartContainer)(this.container, resolvedOptions);
43
54
  const { hostIps } = yield (0, docker_client_1.dockerClient)();
44
55
  this.inspectResult = yield (0, inspect_container_1.inspectContainer)(this.container);
45
56
  const startTime = this.inspectResult.state.startedAt;
46
57
  if (logger_1.containerLog.enabled()) {
47
58
  (yield (0, container_logs_1.containerLogs)(this.container, { since: startTime }))
48
- .on("data", (data) => logger_1.containerLog.trace(`${this.container.id}: ${data.trim()}`))
49
- .on("err", (data) => logger_1.containerLog.error(`${this.container.id}: ${data.trim()}`));
59
+ .on("data", (data) => logger_1.containerLog.trace(data.trim(), { containerId: this.container.id }))
60
+ .on("err", (data) => logger_1.containerLog.error(data.trim(), { containerId: this.container.id }));
50
61
  }
51
62
  this.boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, this.inspectResult).filter(Array.from(this.boundPorts.iterator()).map((port) => port[0]));
52
- yield (0, wait_for_container_1.waitForContainer)(this.container, this.waitStrategy, this.host, this.boundPorts, startTime);
63
+ yield (0, wait_for_container_1.waitForContainer)(this.container, this.waitStrategy, this.boundPorts, startTime);
64
+ logger_1.log.info(`Restarted container`, { containerId: this.container.id });
53
65
  });
54
66
  }
55
67
  stopContainer(options = {}) {
56
68
  return __awaiter(this, void 0, void 0, function* () {
57
- logger_1.log.info(`Stopping container with ID: ${this.container.id}`);
69
+ logger_1.log.info(`Stopping container...`, { containerId: this.container.id });
58
70
  if (this.containerIsStopping) {
59
71
  yield this.containerIsStopping();
60
72
  }
61
73
  const resolvedOptions = Object.assign({ timeout: 0, removeVolumes: true }, options);
62
74
  yield (0, stop_container_1.stopContainer)(this.container, { timeout: resolvedOptions.timeout });
63
75
  yield (0, remove_container_1.removeContainer)(this.container, { removeVolumes: resolvedOptions.removeVolumes });
76
+ logger_1.log.info(`Stopped container`, { containerId: this.container.id });
64
77
  if (this.containerIsStopped) {
65
78
  yield this.containerIsStopped();
66
79
  }
@@ -95,7 +108,13 @@ class StartedGenericContainer {
95
108
  return this.inspectResult.networkSettings[networkName].ipAddress;
96
109
  }
97
110
  exec(command) {
98
- return (0, exec_container_1.execContainer)(this.container, command);
111
+ return __awaiter(this, void 0, void 0, function* () {
112
+ const { dockerode, provider } = yield (0, docker_client_1.dockerClient)();
113
+ logger_1.log.debug(`Executing command "${command.join(" ")}"...`, { containerId: this.container.id });
114
+ const output = yield (0, exec_container_1.execContainer)(dockerode, provider, this.container, command);
115
+ logger_1.log.debug(`Executed command "${command.join(" ")}"...`, { containerId: this.container.id });
116
+ return output;
117
+ });
99
118
  }
100
119
  logs() {
101
120
  return (0, container_logs_1.containerLogs)(this.container);
@@ -2,5 +2,5 @@
2
2
  var _a, _b;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.SSHD_IMAGE = exports.REAPER_IMAGE = void 0;
5
- exports.REAPER_IMAGE = (_a = process.env["RYUK_CONTAINER_IMAGE"]) !== null && _a !== void 0 ? _a : "testcontainers/ryuk:0.3.4";
5
+ exports.REAPER_IMAGE = (_a = process.env["RYUK_CONTAINER_IMAGE"]) !== null && _a !== void 0 ? _a : "testcontainers/ryuk:0.4.0";
6
6
  exports.SSHD_IMAGE = (_b = process.env["SSHD_CONTAINER_IMAGE"]) !== null && _b !== void 0 ? _b : "testcontainers/sshd:1.1.0";
@@ -1,36 +1,25 @@
1
1
  type Message = string;
2
- export interface Logger {
3
- enabled(): boolean;
4
- trace(message: Message): void;
5
- debug(message: Message): void;
6
- info(message: Message): void;
7
- warn(message: Message): void;
8
- error(message: Message): void;
9
- }
10
- declare class DebugLogger implements Logger {
2
+ type Options = {
3
+ containerId?: string;
4
+ imageName?: string;
5
+ };
6
+ export declare class Logger {
7
+ private readonly showLevel;
11
8
  private readonly logger;
12
- constructor(namespace: string);
13
- enabled(): boolean;
14
- trace(message: Message): void;
15
- debug(message: Message): void;
16
- info(message: Message): void;
17
- warn(message: Message): void;
18
- error(message: Message): void;
19
- }
20
- export declare class FakeLogger implements Logger {
21
- readonly traceLogs: Message[];
22
- readonly debugLogs: Message[];
23
- readonly infoLogs: Message[];
24
- readonly warnLogs: Message[];
25
- readonly errorLogs: Message[];
9
+ constructor(namespace: string, showLevel?: boolean);
26
10
  enabled(): boolean;
27
- trace(message: Message): void;
28
- debug(message: Message): void;
29
- info(message: Message): void;
30
- warn(message: Message): void;
31
- error(message: Message): void;
11
+ trace(message: Message, options?: Options): void;
12
+ debug(message: Message, options?: Options): void;
13
+ info(message: Message, options?: Options): void;
14
+ warn(message: Message, options?: Options): void;
15
+ error(message: Message, options?: Options): void;
16
+ private formatMessage;
17
+ private renderOptions;
32
18
  }
33
- export declare const log: DebugLogger;
34
- export declare const containerLog: DebugLogger;
35
- export declare const execLog: DebugLogger;
19
+ export declare const log: Logger;
20
+ export declare const containerLog: Logger;
21
+ export declare const composeLog: Logger;
22
+ export declare const buildLog: Logger;
23
+ export declare const pullLog: Logger;
24
+ export declare const execLog: Logger;
36
25
  export {};
@@ -3,59 +3,49 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.execLog = exports.containerLog = exports.log = exports.FakeLogger = void 0;
6
+ exports.execLog = exports.pullLog = exports.buildLog = exports.composeLog = exports.containerLog = exports.log = exports.Logger = void 0;
7
7
  const debug_1 = __importDefault(require("debug"));
8
- class DebugLogger {
9
- constructor(namespace) {
8
+ class Logger {
9
+ constructor(namespace, showLevel = true) {
10
+ this.showLevel = showLevel;
10
11
  this.logger = (0, debug_1.default)(namespace);
11
12
  }
12
13
  enabled() {
13
14
  return this.logger.enabled;
14
15
  }
15
- trace(message) {
16
- this.logger(`TRACE ${message}`);
16
+ trace(message, options) {
17
+ this.logger(this.formatMessage(message, "TRACE", options));
17
18
  }
18
- debug(message) {
19
- this.logger(`DEBUG ${message}`);
19
+ debug(message, options) {
20
+ this.logger(this.formatMessage(message, "DEBUG", options));
20
21
  }
21
- info(message) {
22
- this.logger(`INFO ${message}`);
22
+ info(message, options) {
23
+ this.logger(this.formatMessage(message, "INFO", options));
23
24
  }
24
- warn(message) {
25
- this.logger(`WARN ${message}`);
25
+ warn(message, options) {
26
+ this.logger(this.formatMessage(message, "WARN", options));
26
27
  }
27
- error(message) {
28
- this.logger(`ERROR ${message}`);
28
+ error(message, options) {
29
+ this.logger(this.formatMessage(message, "ERROR", options));
29
30
  }
30
- }
31
- class FakeLogger {
32
- constructor() {
33
- this.traceLogs = [];
34
- this.debugLogs = [];
35
- this.infoLogs = [];
36
- this.warnLogs = [];
37
- this.errorLogs = [];
38
- }
39
- enabled() {
40
- return true;
41
- }
42
- trace(message) {
43
- this.traceLogs.push(message);
44
- }
45
- debug(message) {
46
- this.debugLogs.push(message);
47
- }
48
- info(message) {
49
- this.infoLogs.push(message);
50
- }
51
- warn(message) {
52
- this.warnLogs.push(message);
31
+ formatMessage(message, level, options) {
32
+ return `${this.showLevel ? `[${level}] ` : ""}${this.renderOptions(options)}${message}`;
53
33
  }
54
- error(message) {
55
- this.errorLogs.push(message);
34
+ renderOptions(options) {
35
+ let str = "";
36
+ if (options === null || options === void 0 ? void 0 : options.containerId) {
37
+ str += `[${options.containerId.substring(0, 12)}] `;
38
+ }
39
+ if (options === null || options === void 0 ? void 0 : options.imageName) {
40
+ str += `[${options.imageName}] `;
41
+ }
42
+ return str;
56
43
  }
57
44
  }
58
- exports.FakeLogger = FakeLogger;
59
- exports.log = new DebugLogger("testcontainers");
60
- exports.containerLog = new DebugLogger("testcontainers:containers");
61
- exports.execLog = new DebugLogger("testcontainers:exec");
45
+ exports.Logger = Logger;
46
+ exports.log = new Logger("testcontainers");
47
+ exports.containerLog = new Logger("testcontainers:containers", false);
48
+ exports.composeLog = new Logger("testcontainers:compose", false);
49
+ exports.buildLog = new Logger("testcontainers:build", false);
50
+ exports.pullLog = new Logger("testcontainers:pull", false);
51
+ exports.execLog = new Logger("testcontainers:exec", false);