testcontainers 9.6.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 (57) hide show
  1. package/dist/package.json +12 -12
  2. package/dist/src/docker/docker-client-config.js +42 -36
  3. package/dist/src/docker/docker-client.js +3 -3
  4. package/dist/src/docker/functions/container/attach-container.js +1 -1
  5. package/dist/src/docker/functions/container/create-container.js +6 -4
  6. package/dist/src/docker/functions/container/exec-container.js +7 -5
  7. package/dist/src/docker/functions/container/get-container.js +1 -1
  8. package/dist/src/docker/functions/container/inspect-container.js +1 -1
  9. package/dist/src/docker/functions/container/put-container-archive.js +1 -1
  10. package/dist/src/docker/functions/container/remove-container.js +1 -1
  11. package/dist/src/docker/functions/container/restart-container.js +1 -1
  12. package/dist/src/docker/functions/container/start-container.js +1 -1
  13. package/dist/src/docker/functions/container/stop-container.js +2 -2
  14. package/dist/src/docker/functions/create-labels.js +0 -3
  15. package/dist/src/docker/functions/image/build-image.js +8 -3
  16. package/dist/src/docker/functions/image/pull-image.js +3 -3
  17. package/dist/src/docker/functions/network/connect-network.js +3 -2
  18. package/dist/src/docker/functions/network/create-network.js +2 -1
  19. package/dist/src/docker/functions/network/remove-network.js +3 -2
  20. package/dist/src/docker/functions/run-in-container.js +6 -6
  21. package/dist/src/docker/pull-stream-parser.js +5 -1
  22. package/dist/src/docker/resolve-host.js +2 -2
  23. package/dist/src/docker-compose/default-docker-compose-options.js +4 -2
  24. package/dist/src/docker-compose/docker-compose-options.d.ts +2 -0
  25. package/dist/src/docker-compose/functions/docker-compose-down.js +1 -1
  26. package/dist/src/docker-compose/functions/docker-compose-pull.js +4 -4
  27. package/dist/src/docker-compose/functions/docker-compose-stop.js +1 -1
  28. package/dist/src/docker-compose/functions/docker-compose-up.js +1 -1
  29. package/dist/src/docker-compose-environment/docker-compose-environment.js +3 -6
  30. package/dist/src/generic-container/generic-container-builder.js +1 -1
  31. package/dist/src/generic-container/generic-container.d.ts +4 -0
  32. package/dist/src/generic-container/generic-container.js +18 -7
  33. package/dist/src/generic-container/started-generic-container.js +10 -5
  34. package/dist/src/images.js +1 -1
  35. package/dist/src/logger.d.ts +20 -31
  36. package/dist/src/logger.js +32 -42
  37. package/dist/src/modules/nats/nats-container.js +1 -1
  38. package/dist/src/network.js +6 -3
  39. package/dist/src/port-check.js +6 -2
  40. package/dist/src/port-forwarder.js +5 -3
  41. package/dist/src/reaper.js +11 -7
  42. package/dist/src/registry-auth-locator/credential-provider.js +3 -5
  43. package/dist/src/registry-auth-locator/get-auth-config.js +3 -3
  44. package/dist/src/system-info.js +2 -5
  45. package/dist/src/test-container.d.ts +1 -0
  46. package/dist/src/uuid.js +1 -1
  47. package/dist/src/wait-for-container.js +4 -4
  48. package/dist/src/wait-strategy/composite-wait-strategy.js +4 -4
  49. package/dist/src/wait-strategy/health-check-wait-strategy.js +8 -5
  50. package/dist/src/wait-strategy/host-port-wait-strategy.js +8 -8
  51. package/dist/src/wait-strategy/http-wait-strategy.js +7 -5
  52. package/dist/src/wait-strategy/log-wait-strategy.js +7 -7
  53. package/dist/src/wait-strategy/shell-wait-strategy.js +5 -5
  54. package/dist/src/wait-strategy/startup-check-strategy.js +6 -3
  55. package/dist/src/wait-strategy/wait-strategy.d.ts +6 -4
  56. package/dist/src/wait-strategy/wait-strategy.js +9 -2
  57. package/package.json +12 -12
@@ -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,6 +10,7 @@ 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;
@@ -19,6 +21,7 @@ export declare class GenericContainer implements TestContainer {
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
  }
@@ -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 {
@@ -148,21 +148,28 @@ 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
160
  if (this.startupTimeout !== undefined) {
160
161
  this.waitStrategy.withStartupTimeout(this.startupTimeout);
161
162
  }
162
- if (logger_1.containerLog.enabled()) {
163
- (yield (0, container_logs_1.containerLogs)(container))
164
- .on("data", (data) => logger_1.containerLog.trace(`${container.id}: ${data.trim()}`))
165
- .on("err", (data) => logger_1.containerLog.error(`${container.id}: ${data.trim()}`));
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
+ }
166
173
  }
167
174
  if (this.containerStarting) {
168
175
  yield this.containerStarting(inspectResult, false);
@@ -307,5 +314,9 @@ class GenericContainer {
307
314
  this.opts.resourcesQuota = { memory: ram, cpu: cpuQuota };
308
315
  return this;
309
316
  }
317
+ withLogConsumer(logConsumer) {
318
+ this.logConsumer = logConsumer;
319
+ return this;
320
+ }
310
321
  }
311
322
  exports.GenericContainer = GenericContainer;
@@ -49,29 +49,31 @@ class StartedGenericContainer {
49
49
  restart(options = {}) {
50
50
  return __awaiter(this, void 0, void 0, function* () {
51
51
  const resolvedOptions = Object.assign({ timeout: 0 }, options);
52
- logger_1.log.info(`Restarting container with ID: ${this.container.id}`);
52
+ logger_1.log.info(`Restarting container...`, { containerId: this.container.id });
53
53
  yield (0, restart_container_1.restartContainer)(this.container, resolvedOptions);
54
54
  const { hostIps } = yield (0, docker_client_1.dockerClient)();
55
55
  this.inspectResult = yield (0, inspect_container_1.inspectContainer)(this.container);
56
56
  const startTime = this.inspectResult.state.startedAt;
57
57
  if (logger_1.containerLog.enabled()) {
58
58
  (yield (0, container_logs_1.containerLogs)(this.container, { since: startTime }))
59
- .on("data", (data) => logger_1.containerLog.trace(`${this.container.id}: ${data.trim()}`))
60
- .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 }));
61
61
  }
62
62
  this.boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, this.inspectResult).filter(Array.from(this.boundPorts.iterator()).map((port) => port[0]));
63
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 });
64
65
  });
65
66
  }
66
67
  stopContainer(options = {}) {
67
68
  return __awaiter(this, void 0, void 0, function* () {
68
- logger_1.log.info(`Stopping container with ID: ${this.container.id}`);
69
+ logger_1.log.info(`Stopping container...`, { containerId: this.container.id });
69
70
  if (this.containerIsStopping) {
70
71
  yield this.containerIsStopping();
71
72
  }
72
73
  const resolvedOptions = Object.assign({ timeout: 0, removeVolumes: true }, options);
73
74
  yield (0, stop_container_1.stopContainer)(this.container, { timeout: resolvedOptions.timeout });
74
75
  yield (0, remove_container_1.removeContainer)(this.container, { removeVolumes: resolvedOptions.removeVolumes });
76
+ logger_1.log.info(`Stopped container`, { containerId: this.container.id });
75
77
  if (this.containerIsStopped) {
76
78
  yield this.containerIsStopped();
77
79
  }
@@ -108,7 +110,10 @@ class StartedGenericContainer {
108
110
  exec(command) {
109
111
  return __awaiter(this, void 0, void 0, function* () {
110
112
  const { dockerode, provider } = yield (0, docker_client_1.dockerClient)();
111
- return (0, exec_container_1.execContainer)(dockerode, provider, this.container, command);
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;
112
117
  });
113
118
  }
114
119
  logs() {
@@ -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);
@@ -19,7 +19,7 @@ const HTTP_MANAGEMENT_PORT = 8222;
19
19
  const USER_ARGUMENT_KEY = "--user";
20
20
  const PASS_ARGUMENT_KEY = "--pass";
21
21
  class NatsContainer extends generic_container_1.GenericContainer {
22
- constructor(image = "nats:2.8-alpine") {
22
+ constructor(image = "nats:2.8.4-alpine") {
23
23
  super(image);
24
24
  this.args = {};
25
25
  this.args[USER_ARGUMENT_KEY] = "test";
@@ -23,10 +23,12 @@ class Network {
23
23
  }
24
24
  start() {
25
25
  return __awaiter(this, void 0, void 0, function* () {
26
- const options = Object.assign({ name: this.uuid.nextUuid(), driver: "bridge", checkDuplicate: true, internal: false, attachable: false, ingress: false, enableIPv6: false }, this.createNetworkOptions);
26
+ const name = this.uuid.nextUuid();
27
+ const options = Object.assign({ name, driver: "bridge", checkDuplicate: true, internal: false, attachable: false, ingress: false, enableIPv6: false }, this.createNetworkOptions);
27
28
  yield reaper_1.ReaperInstance.getInstance();
29
+ logger_1.log.info(`Starting network "${name}"...`);
28
30
  const id = yield (0, create_network_1.createNetwork)(options);
29
- logger_1.log.info(`Started network with ID: ${id}`);
31
+ logger_1.log.info(`Started network "${name}" with ID "${id}"`);
30
32
  return new StartedNetwork(id, options);
31
33
  });
32
34
  }
@@ -45,8 +47,9 @@ class StartedNetwork {
45
47
  }
46
48
  stop() {
47
49
  return __awaiter(this, void 0, void 0, function* () {
48
- logger_1.log.info(`Stopping network with ID: ${this.id}`);
50
+ logger_1.log.info(`Stopping network with ID "${this.id}"...`);
49
51
  yield (0, remove_network_1.removeNetwork)(this.id);
52
+ logger_1.log.info(`Stopped network with ID "${this.id}"`);
50
53
  return new StoppedNetwork();
51
54
  });
52
55
  }
@@ -61,7 +61,9 @@ class InternalPortCheck {
61
61
  if (!shellExists) {
62
62
  if (!this.isDistroless) {
63
63
  this.isDistroless = true;
64
- logger_1.log.error(`The HostPortWaitStrategy will not work on a distroless image, use an alternate wait strategy for container ${this.container.id}`);
64
+ logger_1.log.error(`The HostPortWaitStrategy will not work on a distroless image, use an alternate wait strategy`, {
65
+ containerId: this.container.id,
66
+ });
65
67
  }
66
68
  }
67
69
  else {
@@ -70,7 +72,9 @@ class InternalPortCheck {
70
72
  .filter((result) => result.exitCode !== 126 && result.output.length > 0)
71
73
  .forEach((result) => {
72
74
  if (!this.commandOutputs.has(this.commandOutputsKey(result.output))) {
73
- logger_1.log.trace(`Port check result for container ${this.container.id} exit code ${result.exitCode}: ${result.output}`);
75
+ logger_1.log.trace(`Port check result exit code ${result.exitCode}: ${result.output}`, {
76
+ containerId: this.container.id,
77
+ });
74
78
  this.commandOutputs.add(this.commandOutputsKey(result.output));
75
79
  }
76
80
  });
@@ -24,8 +24,9 @@ class PortForwarder {
24
24
  }
25
25
  exposeHostPort(port) {
26
26
  return __awaiter(this, void 0, void 0, function* () {
27
- logger_1.log.info(`Exposing host port ${port}`);
27
+ logger_1.log.info(`Exposing host port ${port}...`);
28
28
  yield this.sshConnection.remoteForward("localhost", port);
29
+ logger_1.log.info(`Exposed host port ${port}`);
29
30
  });
30
31
  }
31
32
  getNetworkId() {
@@ -53,7 +54,7 @@ class PortForwarderInstance {
53
54
  }
54
55
  static createInstance() {
55
56
  return __awaiter(this, void 0, void 0, function* () {
56
- logger_1.log.debug(`Creating new Port Forwarder`);
57
+ logger_1.log.debug(`Creating new Port Forwarder...`);
57
58
  const username = "root";
58
59
  const password = new uuid_1.RandomUuid().nextUuid();
59
60
  const containerPort = process.env["TESTCONTAINERS_SSHD_PORT"]
@@ -71,8 +72,9 @@ class PortForwarderInstance {
71
72
  .start();
72
73
  const host = (yield (0, docker_client_1.dockerClient)()).host;
73
74
  const port = container.getMappedPort(22);
74
- logger_1.log.debug(`Connecting to Port Forwarder on ${host}:${port}`);
75
+ logger_1.log.debug(`Connecting to Port Forwarder on "${host}:${port}"...`);
75
76
  const connection = yield (0, ssh_remote_port_forward_1.createSshConnection)({ host, port, username, password });
77
+ logger_1.log.debug(`Connected to Port Forwarder on "${host}:${port}"`);
76
78
  connection.unref();
77
79
  return new PortForwarder(connection, container);
78
80
  });
@@ -77,7 +77,7 @@ class ReaperInstance {
77
77
  return process.env.TESTCONTAINERS_RYUK_PRIVILEGED === "true";
78
78
  }
79
79
  static createDisabledInstance() {
80
- logger_1.log.debug(`Not creating new Reaper for session: ${session_id_1.sessionId}`);
80
+ logger_1.log.debug(`Not creating new Reaper for session "${session_id_1.sessionId}"`);
81
81
  return Promise.resolve(new DisabledReaper());
82
82
  }
83
83
  static createRealInstance() {
@@ -85,7 +85,7 @@ class ReaperInstance {
85
85
  const containerPort = process.env["TESTCONTAINERS_RYUK_PORT"]
86
86
  ? { container: 8080, host: Number(process.env["TESTCONTAINERS_RYUK_PORT"]) }
87
87
  : 8080;
88
- logger_1.log.debug(`Creating new Reaper for session: ${session_id_1.sessionId}`);
88
+ logger_1.log.debug(`Creating new Reaper for session "${session_id_1.sessionId}"...`);
89
89
  const container = new generic_container_1.GenericContainer(images_1.REAPER_IMAGE)
90
90
  .withName(`testcontainers-ryuk-${session_id_1.sessionId}`)
91
91
  .withExposedPorts(containerPort)
@@ -106,7 +106,7 @@ class ReaperInstance {
106
106
  const retryStrategy = new retry_strategy_1.IntervalRetryStrategy(1000);
107
107
  const retryResult = yield retryStrategy.retryUntil((attempt) => {
108
108
  return new Promise((resolve) => {
109
- logger_1.log.debug(`Connecting to Reaper (attempt ${attempt + 1}) ${containerId} on ${host}:${port}`);
109
+ logger_1.log.debug(`Connecting to Reaper (attempt ${attempt + 1}) on "${host}:${port}"...`, { containerId });
110
110
  const socket = new net_1.Socket();
111
111
  socket
112
112
  .unref()
@@ -114,21 +114,25 @@ class ReaperInstance {
114
114
  .on("error", (err) => logger_1.log.error(`Reaper ${containerId} socket error: ${err}`))
115
115
  .on("close", (hadError) => {
116
116
  if (hadError) {
117
- logger_1.log.error(`Connection to Reaper ${containerId} closed with error`);
117
+ logger_1.log.error(`Connection to Reaper closed with error`, { containerId });
118
118
  }
119
119
  else {
120
- logger_1.log.warn(`Connection to Reaper ${containerId} closed`);
120
+ logger_1.log.warn(`Connection to Reaper closed`, { containerId });
121
121
  }
122
122
  resolve(undefined);
123
123
  })
124
124
  .connect((0, port_1.getContainerPort)(port), host, () => {
125
- logger_1.log.debug(`Connected to Reaper ${containerId}`);
125
+ logger_1.log.debug(`Connected to Reaper`, { containerId });
126
126
  socket.write(`label=${labels_1.LABEL_TESTCONTAINERS_SESSION_ID}=${session_id_1.sessionId}\r\n`);
127
127
  const reaper = new RealReaper(startedContainer, socket);
128
128
  resolve(reaper);
129
129
  });
130
130
  });
131
- }, (result) => result !== undefined, () => new Error(`Failed to connect to Reaper ${containerId}`), 4000);
131
+ }, (result) => result !== undefined, () => {
132
+ const message = `Failed to connect to Reaper`;
133
+ logger_1.log.error(message, { containerId });
134
+ return new Error(message);
135
+ }, 4000);
132
136
  if (retryResult instanceof RealReaper) {
133
137
  return retryResult;
134
138
  }
@@ -21,20 +21,18 @@ class CredentialProvider {
21
21
  return undefined;
22
22
  }
23
23
  const programName = `docker-credential-${credentialProviderName}`;
24
- logger_1.log.debug(`Executing Docker credential provider: ${programName}`);
24
+ logger_1.log.debug(`Executing Docker credential provider "${programName}"`);
25
25
  const credentials = yield this.listCredentials(programName);
26
26
  if (!Object.keys(credentials).some((aRegistry) => (0, registry_matches_1.registryMatches)(aRegistry, registry))) {
27
- logger_1.log.debug(`No credential found for registry: "${registry}"`);
27
+ logger_1.log.debug(`No credential found for registry "${registry}"`);
28
28
  return undefined;
29
29
  }
30
30
  const response = yield this.runCredentialProvider(registry, programName);
31
- const authConfig = {
31
+ return {
32
32
  username: response.Username,
33
33
  password: response.Secret,
34
34
  registryAddress: response.ServerURL,
35
35
  };
36
- logger_1.log.debug(`Docker credential provider found auth config for ${registry}`);
37
- return authConfig;
38
36
  });
39
37
  }
40
38
  listCredentials(providerName) {
@@ -47,18 +47,18 @@ const registryAuthLocators = [new cred_helpers_1.CredHelpers(), new creds_store_
47
47
  const authsCache = new Map();
48
48
  const getAuthConfig = (registry) => __awaiter(void 0, void 0, void 0, function* () {
49
49
  if (authsCache.has(registry)) {
50
- logger_1.log.debug(`Re-using cached auth for registry ${registry}`);
50
+ logger_1.log.debug(`Auth config cache hit for registry "${registry}"`);
51
51
  return authsCache.get(registry);
52
52
  }
53
53
  for (const registryAuthLocator of registryAuthLocators) {
54
54
  const authConfig = yield registryAuthLocator.getAuthConfig(registry, yield dockerConfig);
55
55
  if (authConfig) {
56
- logger_1.log.debug(`Found applicable registry auth locator for registry "${registry}": ${registryAuthLocator.getName()}`);
56
+ logger_1.log.debug(`Auth config found for registry "${registry}": ${registryAuthLocator.getName()}`);
57
57
  authsCache.set(registry, authConfig);
58
58
  return authConfig;
59
59
  }
60
60
  }
61
- logger_1.log.debug(`No registry auth locator found for registry: "${registry}"`);
61
+ logger_1.log.debug(`No registry auth locator found for registry "${registry}"`);
62
62
  authsCache.set(registry, undefined);
63
63
  return undefined;
64
64
  });
@@ -19,15 +19,12 @@ const getSystemInfo = (dockerode) => __awaiter(void 0, void 0, void 0, function*
19
19
  if (systemInfo !== undefined) {
20
20
  return systemInfo;
21
21
  }
22
- logger_1.log.debug("Fetching system info");
22
+ logger_1.log.debug("Fetching system info...");
23
23
  const nodeInfo = getNodeInfo();
24
24
  const dockerInfo = yield (0, get_info_1.getDockerInfo)(dockerode);
25
25
  const dockerComposeInfo = yield getDockerComposeInfo();
26
26
  systemInfo = { nodeInfo, dockerInfo, dockerComposeInfo };
27
- logger_1.log.debug(`System info:
28
- System: Node version: ${nodeInfo.version}, Platform: ${nodeInfo.platform}, Arch: ${nodeInfo.architecture}
29
- Container runtime: OS: ${dockerInfo.operatingSystem}, Version: ${dockerInfo.serverVersion}, Arch: ${dockerInfo.architecture}, CPUs: ${dockerInfo.cpus}, Memory: ${dockerInfo.memory}
30
- Compose: Installed: ${dockerComposeInfo !== undefined}, Version: ${(_a = dockerComposeInfo === null || dockerComposeInfo === void 0 ? void 0 : dockerComposeInfo.version) !== null && _a !== void 0 ? _a : "N/A"}`);
27
+ logger_1.log.debug(`Node version: ${nodeInfo.version}, Platform: ${nodeInfo.platform}, Arch: ${nodeInfo.architecture}, OS: ${dockerInfo.operatingSystem}, Version: ${dockerInfo.serverVersion}, Arch: ${dockerInfo.architecture}, CPUs: ${dockerInfo.cpus}, Memory: ${dockerInfo.memory}, Compose installed: ${dockerComposeInfo !== undefined}, Compose version: ${(_a = dockerComposeInfo === null || dockerComposeInfo === void 0 ? void 0 : dockerComposeInfo.version) !== null && _a !== void 0 ? _a : "N/A"}`);
31
28
  return systemInfo;
32
29
  });
33
30
  exports.getSystemInfo = getSystemInfo;
@@ -30,6 +30,7 @@ export interface TestContainer {
30
30
  withCopyContentToContainer(contentsToCopy: ContentToCopy[]): this;
31
31
  withWorkingDir(workingDir: string): this;
32
32
  withResourcesQuota(resourcesQuota: ResourcesQuota): this;
33
+ withLogConsumer(logConsumer: (stream: Readable) => unknown): this;
33
34
  }
34
35
  export interface RestartOptions {
35
36
  timeout: number;
package/dist/src/uuid.js CHANGED
@@ -7,7 +7,7 @@ exports.RandomUuid = void 0;
7
7
  const crypto_1 = __importDefault(require("crypto"));
8
8
  class RandomUuid {
9
9
  nextUuid() {
10
- return crypto_1.default.randomUUID();
10
+ return crypto_1.default.createHash("md5").update(crypto_1.default.randomUUID()).digest("hex").substring(0, 12);
11
11
  }
12
12
  }
13
13
  exports.RandomUuid = RandomUuid;
@@ -14,19 +14,19 @@ const logger_1 = require("./logger");
14
14
  const stop_container_1 = require("./docker/functions/container/stop-container");
15
15
  const remove_container_1 = require("./docker/functions/container/remove-container");
16
16
  const waitForContainer = (container, waitStrategy, boundPorts, startTime) => __awaiter(void 0, void 0, void 0, function* () {
17
- logger_1.log.debug(`Waiting for container to be ready: ${container.id}`);
17
+ logger_1.log.debug(`Waiting for container to be ready...`, { containerId: container.id });
18
18
  try {
19
19
  yield waitStrategy.waitUntilReady(container, boundPorts, startTime);
20
- logger_1.log.info(`Container is ready: ${container.id}`);
20
+ logger_1.log.info(`Container is ready`, { containerId: container.id });
21
21
  }
22
22
  catch (err) {
23
- logger_1.log.error(`Container failed to be ready: ${container.id}: ${err}`);
23
+ logger_1.log.error(`Container failed to be ready: ${err}`, { containerId: container.id });
24
24
  try {
25
25
  yield (0, stop_container_1.stopContainer)(container, { timeout: 0 });
26
26
  yield (0, remove_container_1.removeContainer)(container, { removeVolumes: true });
27
27
  }
28
28
  catch (stopErr) {
29
- logger_1.log.error(`Failed to stop container after it failed to be ready: ${container.id}: ${stopErr}`);
29
+ logger_1.log.error(`Failed to stop container after it failed to be ready: ${stopErr}`, { containerId: container.id });
30
30
  }
31
31
  throw err;
32
32
  }
@@ -19,13 +19,13 @@ class CompositeWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
19
19
  }
20
20
  waitUntilReady(container, boundPorts, startTime) {
21
21
  return __awaiter(this, void 0, void 0, function* () {
22
- logger_1.log.debug(`Starting composite wait strategy for ${container.id}`);
22
+ logger_1.log.debug(`Waiting for composite...`, { containerId: container.id });
23
23
  return new Promise((resolve, reject) => {
24
24
  let deadlineTimeout;
25
25
  if (this.deadline !== undefined) {
26
26
  deadlineTimeout = setTimeout(() => {
27
- const message = `Composite wait strategy not successful after ${this.deadline}ms for ${container.id}`;
28
- logger_1.log.error(message);
27
+ const message = `Composite wait strategy not successful after ${this.deadline}ms`;
28
+ logger_1.log.error(message, { containerId: container.id });
29
29
  reject(new Error(message));
30
30
  }, this.deadline);
31
31
  }
@@ -42,7 +42,7 @@ class CompositeWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
42
42
  }
43
43
  withStartupTimeout(startupTimeout) {
44
44
  this.waitStrategies
45
- .filter((waitStrategy) => waitStrategy.getStartupTimeout() === undefined)
45
+ .filter((waitStrategy) => !waitStrategy.isStartupTimeoutSet())
46
46
  .forEach((waitStrategy) => waitStrategy.withStartupTimeout(startupTimeout));
47
47
  return this;
48
48
  }
@@ -16,15 +16,18 @@ const inspect_container_1 = require("../docker/functions/container/inspect-conta
16
16
  const wait_strategy_1 = require("./wait-strategy");
17
17
  class HealthCheckWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
18
18
  waitUntilReady(container) {
19
- var _a;
20
19
  return __awaiter(this, void 0, void 0, function* () {
21
- logger_1.log.debug(`Waiting for health check for ${container.id}`);
20
+ logger_1.log.debug(`Waiting for health check...`, { containerId: container.id });
22
21
  const status = yield new retry_strategy_1.IntervalRetryStrategy(100).retryUntil(() => __awaiter(this, void 0, void 0, function* () { return (yield (0, inspect_container_1.inspectContainer)(container)).healthCheckStatus; }), (healthCheckStatus) => healthCheckStatus === "healthy" || healthCheckStatus === "unhealthy", () => {
23
22
  const timeout = this.startupTimeout;
24
- throw new Error(`Health check not healthy after ${timeout}ms for ${container.id}`);
25
- }, (_a = this.startupTimeout) !== null && _a !== void 0 ? _a : wait_strategy_1.DEFAULT_STARTUP_TIMEOUT);
23
+ const message = `Health check not healthy after ${timeout}ms`;
24
+ logger_1.log.error(message, { containerId: container.id });
25
+ throw new Error(message);
26
+ }, this.startupTimeout);
26
27
  if (status !== "healthy") {
27
- throw new Error(`Health check failed: ${status} for ${container.id}`);
28
+ const message = `Health check failed: ${status}`;
29
+ logger_1.log.error(message, { containerId: container.id });
30
+ throw new Error(message);
28
31
  }
29
32
  });
30
33
  }