Socket
Socket
Sign inDemoInstall

testcontainers

Package Overview
Dependencies
84
Maintainers
1
Versions
238
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 9.6.0 to 9.7.0

24

dist/package.json
{
"name": "testcontainers",
"author": "Cristian Greco",
"version": "9.6.0",
"version": "9.7.0",
"main": "dist/src/index",

@@ -59,4 +59,4 @@ "types": "dist/src/index",

"@types/debug": "4.1.7",
"@types/jest": "^29.5.0",
"@types/node": "^18.15.11",
"@types/jest": "^29.5.1",
"@types/node": "^18.16.2",
"@types/node-fetch": "^2.6.3",

@@ -66,7 +66,7 @@ "@types/pg": "^8.6.6",

"@types/tar-fs": "^2.0.1",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"arangojs": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"arangojs": "^8.2.1",
"cross-env": "^7.0.3",
"eslint": "^8.37.0",
"eslint": "^8.39.0",
"eslint-config-prettier": "^8.8.0",

@@ -77,11 +77,11 @@ "eslint-plugin-prettier": "^4.2.1",

"kafkajs": "^2.2.4",
"lint-staged": "^13.2.0",
"mongoose": "^6.10.4",
"lint-staged": "^13.2.2",
"mongoose": "^6.10.5",
"mysql2": "^2.3.3",
"nats": "^2.13.1",
"neo4j-driver": "^5.6.0",
"neo4j-driver": "^5.7.0",
"pg": "^8.10.0",
"prettier": "^2.8.7",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
"ts-jest": "^29.0.5",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",

@@ -88,0 +88,0 @@ "typescript": "^4.9.5",

@@ -79,3 +79,3 @@ "use strict";

return __awaiter(this, void 0, void 0, function* () {
logger_1.log.info(`Starting DockerCompose environment: ${this.projectName}`);
logger_1.log.info(`Starting DockerCompose environment "${this.projectName}"...`);
(yield reaper_1.ReaperInstance.getInstance()).addProject(this.projectName);

@@ -108,3 +108,3 @@ const options = {

], []);
logger_1.log.info(`Started the following containers: ${startedContainerNames.join(", ")}`);
logger_1.log.info(`Started containers "${startedContainerNames.join('", "')}"`);
const startedGenericContainers = (yield Promise.all(startedContainers.map((startedContainer) => __awaiter(this, void 0, void 0, function* () {

@@ -128,8 +128,5 @@ const container = yield (0, get_container_1.getContainerById)(startedContainer.Id);

try {
logger_1.log.info(`Waiting for container ${containerName} to be ready`);
yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, boundPorts);
logger_1.log.info(`Container ${containerName} is ready`);
}
catch (err) {
logger_1.log.error(`Container ${containerName} failed to be ready: ${err}`);
try {

@@ -148,3 +145,3 @@ yield (0, docker_compose_down_1.dockerComposeDown)(options, { removeVolumes: true, timeout: 0 });

}, {});
logger_1.log.info(`DockerCompose environment started: ${Object.keys(startedGenericContainers).join(", ")}`);
logger_1.log.info(`DockerCompose environment started`);
return new started_docker_compose_environment_1.StartedDockerComposeEnvironment(startedGenericContainers, Object.assign(Object.assign({}, options), { composeOptions, environment: this.environment }));

@@ -151,0 +148,0 @@ });

@@ -29,7 +29,9 @@ "use strict";

const defaultDockerComposeOptions = (_a) => __awaiter(void 0, void 0, void 0, function* () {
var _b;
var { environment = {} } = _a, options = __rest(_a, ["environment"]);
const { composeEnvironment } = yield (0, docker_client_1.dockerClient)();
const log = (_b = options.logger) !== null && _b !== void 0 ? _b : logger_1.composeLog;
return {
log: false,
callback: logger_1.log.enabled()
callback: log.enabled()
? (chunk) => {

@@ -40,3 +42,3 @@ chunk

.filter(type_guards_1.isNotEmptyString)
.forEach((line) => logger_1.log.trace(line.trim()));
.forEach((line) => log.trace(line.trim()));
}

@@ -43,0 +45,0 @@ : undefined,

import { Environment } from "../docker/types";
import { Logger } from "../logger";
export type DockerComposeOptions = {

@@ -9,2 +10,3 @@ filePath: string;

environment?: Environment;
logger?: Logger;
};

@@ -17,3 +17,3 @@ "use strict";

const dockerComposeDown = (options, downOptions) => __awaiter(void 0, void 0, void 0, function* () {
logger_1.log.info(`Downing DockerCompose environment`);
logger_1.log.info(`Downing DockerCompose environment...`);
try {

@@ -20,0 +20,0 @@ yield (0, docker_compose_1.down)(Object.assign(Object.assign({}, (yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options))), { commandOptions: commandOptions(downOptions) }));

@@ -19,8 +19,8 @@ "use strict";

if (services) {
logger_1.log.info(`Pulling DockerCompose environment images for ${services.join(", ")}`);
yield (0, docker_compose_1.pullMany)(services, yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options));
logger_1.log.info(`Pulling DockerCompose environment images "${services.join('", "')}"...`);
yield (0, docker_compose_1.pullMany)(services, yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(Object.assign(Object.assign({}, options), { logger: logger_1.pullLog })));
}
else {
logger_1.log.info(`Pulling DockerCompose environment images`);
yield (0, docker_compose_1.pullAll)(yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options));
logger_1.log.info(`Pulling DockerCompose environment images...`);
yield (0, docker_compose_1.pullAll)(yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(Object.assign(Object.assign({}, options), { logger: logger_1.pullLog })));
}

@@ -27,0 +27,0 @@ logger_1.log.info(`Pulled DockerCompose environment`);

@@ -17,3 +17,3 @@ "use strict";

const dockerComposeStop = (options) => __awaiter(void 0, void 0, void 0, function* () {
logger_1.log.info(`Stopping DockerCompose environment`);
logger_1.log.info(`Stopping DockerCompose environment...`);
try {

@@ -20,0 +20,0 @@ yield (0, docker_compose_1.stop)(yield (0, default_docker_compose_options_1.defaultDockerComposeOptions)(options));

@@ -18,3 +18,3 @@ "use strict";

const dockerComposeUp = (options, services) => __awaiter(void 0, void 0, void 0, function* () {
logger_1.log.info(`Upping DockerCompose environment`);
logger_1.log.info(`Upping DockerCompose environment...`);
try {

@@ -21,0 +21,0 @@ if (services) {

@@ -23,45 +23,51 @@ "use strict";

const getDockerClientConfig = (env = process.env) => __awaiter(void 0, void 0, void 0, function* () {
let dockerHost;
let dockerTlsVerify;
let dockerCertPath;
const file = path_1.default.resolve((0, os_1.homedir)(), ".testcontainers.properties");
if ((0, fs_1.existsSync)(file)) {
logger_1.log.debug("Found .testcontainers.properties file");
const string = yield (0, promises_1.readFile)(file, { encoding: "utf-8" });
const properties = (0, properties_reader_1.default)("").read(string);
const host = properties.get("docker.host");
if (host !== null) {
dockerHost = host;
const dockerClientConfig = Object.assign(Object.assign({}, (yield loadFromFile())), loadFromEnv(env));
logDockerClientConfig(dockerClientConfig);
return dockerClientConfig;
});
exports.getDockerClientConfig = getDockerClientConfig;
function loadFromFile() {
return __awaiter(this, void 0, void 0, function* () {
const file = path_1.default.resolve((0, os_1.homedir)(), ".testcontainers.properties");
const dockerClientConfig = {};
if ((0, fs_1.existsSync)(file)) {
logger_1.log.debug(`Found ".testcontainers.properties" file`);
const string = yield (0, promises_1.readFile)(file, { encoding: "utf-8" });
const properties = (0, properties_reader_1.default)("").read(string);
const host = properties.get("docker.host");
if (host !== null) {
dockerClientConfig.dockerHost = host;
}
const tlsVerify = properties.get("docker.tls.verify");
if (tlsVerify !== null) {
dockerClientConfig.dockerTlsVerify = `${tlsVerify}`;
}
const certPath = properties.get("docker.cert.path");
if (certPath !== null) {
dockerClientConfig.dockerCertPath = certPath;
}
}
const tlsVerify = properties.get("docker.tls.verify");
if (tlsVerify !== null) {
dockerTlsVerify = `${tlsVerify}`;
}
const certPath = properties.get("docker.cert.path");
if (certPath !== null) {
dockerCertPath = certPath;
}
}
return dockerClientConfig;
});
}
function loadFromEnv(env) {
const dockerClientConfig = {};
if (env["DOCKER_HOST"] !== undefined) {
dockerHost = env["DOCKER_HOST"];
dockerClientConfig.dockerHost = env["DOCKER_HOST"];
}
if (env["DOCKER_TLS_VERIFY"] !== undefined) {
dockerTlsVerify = env["DOCKER_TLS_VERIFY"];
dockerClientConfig.dockerTlsVerify = env["DOCKER_TLS_VERIFY"];
}
if (env["DOCKER_CERT_PATH"] !== undefined) {
dockerCertPath = env["DOCKER_CERT_PATH"];
dockerClientConfig.dockerCertPath = env["DOCKER_CERT_PATH"];
}
let logStr = "Loaded properties: ";
if (dockerHost !== undefined) {
logStr += `dockerHost=${dockerHost}, `;
return dockerClientConfig;
}
function logDockerClientConfig(config) {
const configurations = Object.entries(config)
.filter(([, value]) => value !== undefined)
.map(([key, value]) => `${key}: "${value}"`);
if (configurations.length > 0) {
logger_1.log.debug(`Loaded Docker client configuration, ${configurations.join(", ")}`);
}
if (dockerTlsVerify !== undefined) {
logStr += `dockerTlsVerify=${dockerTlsVerify}, `;
}
if (dockerCertPath !== undefined) {
logStr += `dockerCertPath=${dockerCertPath}`;
}
logger_1.log.debug(logStr);
return { dockerHost, dockerTlsVerify, dockerCertPath };
});
exports.getDockerClientConfig = getDockerClientConfig;
}

@@ -41,5 +41,5 @@ "use strict";

if (strategy.isApplicable()) {
logger_1.log.debug(`Found applicable Docker client strategy: ${strategy.getName()}`);
logger_1.log.debug(`Found Docker client strategy "${strategy.getName()}"`);
const { uri, dockerode, composeEnvironment } = yield strategy.getDockerClient();
logger_1.log.debug(`Testing Docker client strategy URI: ${uri}`);
logger_1.log.debug(`Testing Docker client strategy "${uri}"...`);
if (yield isDockerDaemonReachable(dockerode)) {

@@ -50,3 +50,3 @@ const indexServerAddress = (yield (0, system_info_1.getSystemInfo)(dockerode)).dockerInfo.indexServerAddress;

const hostIps = yield (0, lookup_host_ips_1.lookupHostIps)(host);
logger_1.log.info(`Using Docker client strategy: ${strategy.getName()}, Docker host: ${host} (${hostIps
logger_1.log.info(`Using Docker client strategy "${strategy.getName()}", Docker host "${host}" (${hostIps
.map((hostIp) => hostIp.address)

@@ -53,0 +53,0 @@ .join(", ")})`);

@@ -21,3 +21,3 @@ "use strict";

catch (err) {
logger_1.log.error(`Failed to attach to container ${container.id}: ${err}`);
logger_1.log.error(`Failed to attach to container: ${err}`, { containerId: container.id });
throw err;

@@ -24,0 +24,0 @@ }

@@ -20,5 +20,5 @@ "use strict";

try {
logger_1.log.info(`Creating container for image: ${options.imageName}`);
logger_1.log.info(`Creating container for image "${options.imageName}"...`);
const { dockerode } = yield (0, docker_client_1.dockerClient)();
return yield dockerode.createContainer({
const container = yield dockerode.createContainer({
name: options.name,

@@ -51,5 +51,7 @@ User: options.user,

});
logger_1.log.info(`Created container for image "${options.imageName}"`, { containerId: container.id });
return container;
}
catch (err) {
logger_1.log.error(`Failed to create container for image ${options.imageName}: ${err}`);
logger_1.log.error(`Failed to create container for image "${options.imageName}": ${err}`);
throw err;

@@ -77,3 +79,3 @@ }

else {
dockerodePortBindings[exposedPort] = [{ HostPort: "0" }];
dockerodePortBindings[exposedPort] = [{ HostPort: "" }];
}

@@ -80,0 +82,0 @@ }

@@ -27,6 +27,6 @@ "use strict";

});
const stream = yield startExec(dockerode, provider, exec);
const stream = yield startExec(dockerode, provider, exec, container);
stream.on("data", (chunk) => chunks.push(chunk));
if (shouldLog && logger_1.execLog.enabled()) {
(0, byline_1.default)(stream).on("data", (line) => logger_1.execLog.trace(`${container.id}: ${line}`));
(0, byline_1.default)(stream).on("data", (line) => logger_1.execLog.trace(line, { containerId: container.id }));
}

@@ -38,3 +38,5 @@ const exitCode = yield waitForExec(exec, stream);

catch (err) {
logger_1.log.error(`Failed to exec container ${container.id} with command "${command.join(" ")}": ${err}. Container output: ${chunks.join("")}`);
logger_1.log.error(`Failed to exec container with command "${command.join(" ")}": ${err}: ${chunks.join("")}`, {
containerId: container.id,
});
throw err;

@@ -44,3 +46,3 @@ }

exports.execContainer = execContainer;
const startExec = (dockerode, provider, exec) => __awaiter(void 0, void 0, void 0, function* () {
const startExec = (dockerode, provider, exec, container) => __awaiter(void 0, void 0, void 0, function* () {
try {

@@ -56,3 +58,3 @@ const stream = yield exec.start({ stdin: true, Detach: false, Tty: true });

catch (err) {
logger_1.log.error(`Failed to start exec: ${err}`);
logger_1.log.error(`Failed to start exec: ${err}`, { containerId: container.id });
throw err;

@@ -59,0 +61,0 @@ }

@@ -45,3 +45,3 @@ "use strict";

catch (err) {
logger_1.log.error(`Failed to get container by hash (${hash}): ${err}`);
logger_1.log.error(`Failed to get container by hash "${hash}": ${err}`);
throw err;

@@ -48,0 +48,0 @@ }

@@ -34,3 +34,3 @@ "use strict";

catch (err) {
logger_1.log.error(`Failed to inspect container ${container.id}: ${err}`);
logger_1.log.error(`Failed to inspect container: ${err}`, { containerId: container.id });
throw err;

@@ -37,0 +37,0 @@ }

@@ -22,3 +22,3 @@ "use strict";

catch (err) {
logger_1.log.error(`Failed to put archive to container ${options.container.id}: ${err}`);
logger_1.log.error(`Failed to put archive to container: ${err}`, { containerId: options.container.id });
throw err;

@@ -25,0 +25,0 @@ }

@@ -19,3 +19,3 @@ "use strict";

catch (err) {
logger_1.log.error(`Failed to remove container ${container.id}: ${err}`);
logger_1.log.error(`Failed to remove container: ${err}`, { containerId: container.id });
throw err;

@@ -22,0 +22,0 @@ }

@@ -19,3 +19,3 @@ "use strict";

catch (err) {
logger_1.log.error(`Failed to restart container ${container.id}: ${err}`);
logger_1.log.error(`Failed to restart container: ${err}`, { containerId: container.id });
throw err;

@@ -22,0 +22,0 @@ }

@@ -19,3 +19,3 @@ "use strict";

catch (err) {
logger_1.log.error(`Failed to start container ${container.id}: ${err}`);
logger_1.log.error(`Failed to start container: ${err}`, { containerId: container.id });
throw err;

@@ -22,0 +22,0 @@ }

@@ -21,6 +21,6 @@ "use strict";

if (err.statusCode === 304) {
logger_1.log.info(`Container has already been stopped: ${container.id}`);
logger_1.log.info(`Container has already been stopped`, { containerId: container.id });
}
else {
logger_1.log.error(`Failed to stop container ${container.id}: ${err}`);
logger_1.log.error(`Failed to stop container: ${err}`, { containerId: container.id });
throw err;

@@ -27,0 +27,0 @@ }

@@ -9,5 +9,2 @@ "use strict";

const labels = Object.assign(Object.assign({}, extraLabels), { [labels_1.LABEL_TESTCONTAINERS]: "true", [labels_1.LABEL_TESTCONTAINERS_LANG]: "node", [labels_1.LABEL_TESTCONTAINERS_VERSION]: package_json_1.version });
if (dockerImageName && dockerImageName.isReaper()) {
return labels;
}
if (!reusable) {

@@ -14,0 +11,0 @@ labels[labels_1.LABEL_TESTCONTAINERS_SESSION_ID] = session_id_1.sessionId;

@@ -26,3 +26,3 @@ "use strict";

try {
logger_1.log.info(`Building image '${options.imageName.toString()}' with context '${options.context}'`);
logger_1.log.info(`Building image "${options.imageName.toString()}" with context "${options.context}"...`);
const isDockerIgnored = yield createIsDockerIgnoredFunction(options.context);

@@ -50,3 +50,3 @@ const tarStream = tar_fs_1.default.pack(options.context, {

};
return new Promise((resolve) => {
yield new Promise((resolve) => {
dockerode

@@ -57,6 +57,11 @@ .buildImage(tarStream, buildImageOptions)

stream.setEncoding("utf-8");
stream.on("data", (line) => logger_1.log.trace(`${options.imageName.toString()}: ${line}`));
stream.on("data", (line) => {
if (logger_1.buildLog.enabled()) {
logger_1.buildLog.trace(line, { imageName: options.imageName.toString() });
}
});
stream.on("end", () => resolve());
});
});
logger_1.log.info(`Built image "${options.imageName.toString()}" with context "${options.context}"`);
}

@@ -63,0 +68,0 @@ catch (err) {

@@ -27,9 +27,9 @@ "use strict";

if (!options.force && (yield (0, image_exists_1.imageExists)(dockerode, options.imageName))) {
logger_1.log.debug(`Not pulling image as it already exists: ${options.imageName}`);
logger_1.log.debug(`Not pulling image "${options.imageName}" as it already exists`);
return;
}
logger_1.log.info(`Pulling image: ${options.imageName}`);
logger_1.log.info(`Pulling image "${options.imageName}"...`);
const authconfig = yield (0, get_auth_config_1.getAuthConfig)((_a = options.imageName.registry) !== null && _a !== void 0 ? _a : indexServerAddress);
const stream = yield dockerode.pull(options.imageName.toString(), { authconfig });
yield new pull_stream_parser_1.PullStreamParser(options.imageName, logger_1.log).consume(stream);
yield new pull_stream_parser_1.PullStreamParser(options.imageName, logger_1.pullLog).consume(stream);
}));

@@ -36,0 +36,0 @@ }

@@ -17,9 +17,10 @@ "use strict";

try {
logger_1.log.info(`Connecting container ${options.containerId} to network ${options.networkId}`);
logger_1.log.info(`Connecting to network "${options.networkId}"...`, { containerId: options.containerId });
const { dockerode } = yield (0, docker_client_1.dockerClient)();
const network = dockerode.getNetwork(options.networkId);
yield network.connect({ Container: options.containerId, EndpointConfig: { Aliases: options.networkAliases } });
logger_1.log.info(`Connected to network "${options.networkId}"...`, { containerId: options.containerId });
}
catch (err) {
logger_1.log.error(`Failed to connect container ${options.containerId} to network ${options.networkId}: ${err}`);
logger_1.log.error(`Failed to connect to network "${options.networkId}": ${err}`, { containerId: options.containerId });
throw err;

@@ -26,0 +27,0 @@ }

@@ -18,3 +18,3 @@ "use strict";

try {
logger_1.log.info(`Creating network ${options.name}`);
logger_1.log.info(`Creating network "${options.name}"...`);
const { dockerode } = yield (0, docker_client_1.dockerClient)();

@@ -32,2 +32,3 @@ const network = yield dockerode.createNetwork({

});
logger_1.log.info(`Created network "${options.name}"`);
return network.id;

@@ -34,0 +35,0 @@ }

@@ -17,3 +17,3 @@ "use strict";

try {
logger_1.log.info(`Removing network ${id}`);
logger_1.log.info(`Removing network "${id}"...`);
const { dockerode } = yield (0, docker_client_1.dockerClient)();

@@ -25,5 +25,6 @@ const network = dockerode.getNetwork(id);

}
logger_1.log.info(`Removed network "${id}"`);
}
catch (err) {
logger_1.log.error(`Failed to remove network ${id}: ${err}`);
logger_1.log.error(`Failed to remove network "${id}": ${err}`);
throw err;

@@ -30,0 +31,0 @@ }

@@ -22,11 +22,11 @@ "use strict";

yield (0, pull_image_1.pullImage)(dockerode, indexServerAddress, { imageName: docker_image_name_1.DockerImageName.fromString(image), force: false });
logger_1.log.debug(`Creating container: ${image} with command: ${command.join(" ")}`);
logger_1.log.debug(`Creating container: ${image} with command "${command.join(" ")}"...`);
const container = yield dockerode.createContainer({ Image: image, Cmd: command });
logger_1.log.debug(`Attaching to container: ${container.id}`);
logger_1.log.debug(`Attaching to container...`, { containerId: container.id });
const stream = yield (0, attach_container_1.attachContainer)(dockerode, container);
logger_1.log.debug(`Starting container: ${container.id}`);
logger_1.log.debug(`Starting container...`, { containerId: container.id });
yield (0, start_container_1.startContainer)(container);
logger_1.log.debug(`Waiting for container output: ${container.id}`);
logger_1.log.debug(`Waiting for container output...`, { containerId: container.id });
const output = yield (0, stream_utils_1.streamToString)(stream, { trim: true });
logger_1.log.debug(`Removing container: ${container.id}`);
logger_1.log.debug(`Removing container...`, { containerId: container.id });
yield container.remove({ force: true, v: true });

@@ -36,3 +36,3 @@ return output.length === 0 ? undefined : output;

catch (err) {
logger_1.log.error(`Failed to run command in container: "${command.join(" ")}", error: "${err}"`);
logger_1.log.error(`Failed to run command "${command.join(" ")}" in container: "${err}"`);
return undefined;

@@ -39,0 +39,0 @@ }

@@ -15,3 +15,7 @@ "use strict";

return new Promise((resolve) => {
(0, byline_1.default)(stream).on("data", (line) => this.logger.trace(`Pulling ${this.dockerImageName.toString()}: ${line}`));
(0, byline_1.default)(stream).on("data", (line) => {
if (this.logger.enabled()) {
this.logger.trace(line, { imageName: this.dockerImageName.toString() });
}
});
stream.on("end", resolve);

@@ -18,0 +22,0 @@ });

@@ -49,3 +49,3 @@ "use strict";

var _a, _b, _c;
logger_1.log.debug(`Checking gateway for Docker host`);
logger_1.log.debug(`Checking gateway for Docker host...`);
const inspectResult = yield dockerode.getNetwork(networkName).inspect();

@@ -55,3 +55,3 @@ 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;

const findDefaultGateway = (dockerode, indexServerAddress) => __awaiter(void 0, void 0, void 0, function* () {
logger_1.log.debug(`Checking default gateway for Docker host`);
logger_1.log.debug(`Checking default gateway for Docker host...`);
return (0, run_in_container_1.runInContainer)(dockerode, indexServerAddress, "alpine:3.14", [

@@ -58,0 +58,0 @@ "sh",

@@ -55,3 +55,3 @@ "use strict";

const dockerfile = path_1.default.resolve(this.context, this.dockerfileName);
logger_1.log.debug(`Preparing to build Dockerfile: ${dockerfile}`);
logger_1.log.debug(`Preparing to build Dockerfile "${dockerfile}" as image "${imageName}"...`);
const imageNames = yield (0, dockerfile_parser_1.getDockerfileImages)(dockerfile, this.buildArgs);

@@ -58,0 +58,0 @@ const { dockerode, indexServerAddress } = yield (0, docker_client_1.dockerClient)();

@@ -0,1 +1,2 @@

/// <reference types="node" />
import archiver from "archiver";

@@ -12,2 +13,3 @@ import { BoundPorts } from "../bound-ports";

import { StartedNetwork } from "../network";
import { Readable } from "stream";
export declare class GenericContainer implements TestContainer {

@@ -23,2 +25,3 @@ readonly image: string;

protected pullPolicy: PullPolicy;
protected logConsumer?: (stream: Readable) => unknown;
constructor(image: string);

@@ -70,2 +73,3 @@ /**

withResourcesQuota({ memory, cpu }: ResourcesQuota): this;
withLogConsumer(logConsumer: (stream: Readable) => unknown): this;
}

@@ -82,3 +82,3 @@ "use strict";

this.opts.labels = Object.assign(Object.assign({}, this.opts.labels), { [labels_1.LABEL_TESTCONTAINERS_CONTAINER_HASH]: containerHash });
logger_1.log.debug(`Container reuse has been enabled, hash: ${containerHash}`);
logger_1.log.debug(`Container reuse has been enabled with hash "${containerHash}"`);
// We might have several async processes try to create a reusable container

@@ -90,3 +90,3 @@ // at once, to avoid possibly creating too many of these, use a lock

if (container !== undefined) {
logger_1.log.debug(`Found container to reuse with hash: ${containerHash}`);
logger_1.log.debug(`Found container to reuse with hash "${containerHash}"`, { containerId: container.id });
return this.reuseContainer(container);

@@ -153,3 +153,3 @@ }

}
logger_1.log.info(`Starting container ${this.opts.imageName} with ID: ${container.id}`);
logger_1.log.info(`Starting container for image "${this.opts.imageName}"...`, { containerId: container.id });
if (this.containerCreated) {

@@ -159,2 +159,3 @@ yield this.containerCreated(container.id);

yield (0, start_container_1.startContainer)(container);
logger_1.log.info(`Started container for image "${this.opts.imageName}"`, { containerId: container.id });
const { host, hostIps } = yield (0, docker_client_1.dockerClient)();

@@ -166,6 +167,12 @@ const inspectResult = yield (0, inspect_container_1.inspectContainer)(container);

}
if (logger_1.containerLog.enabled()) {
(yield (0, container_logs_1.containerLogs)(container))
.on("data", (data) => logger_1.containerLog.trace(`${container.id}: ${data.trim()}`))
.on("err", (data) => logger_1.containerLog.error(`${container.id}: ${data.trim()}`));
if (logger_1.containerLog.enabled() || this.logConsumer !== undefined) {
const logStream = yield (0, container_logs_1.containerLogs)(container);
if (this.logConsumer !== undefined) {
this.logConsumer(logStream);
}
if (logger_1.containerLog.enabled()) {
logStream
.on("data", (data) => logger_1.containerLog.trace(data.trim(), { containerId: container.id }))
.on("err", (data) => logger_1.containerLog.error(data.trim(), { containerId: container.id }));
}
}

@@ -315,3 +322,7 @@ if (this.containerStarting) {

}
withLogConsumer(logConsumer) {
this.logConsumer = logConsumer;
return this;
}
}
exports.GenericContainer = GenericContainer;

@@ -52,3 +52,3 @@ "use strict";

const resolvedOptions = Object.assign({ timeout: 0 }, options);
logger_1.log.info(`Restarting container with ID: ${this.container.id}`);
logger_1.log.info(`Restarting container...`, { containerId: this.container.id });
yield (0, restart_container_1.restartContainer)(this.container, resolvedOptions);

@@ -60,7 +60,8 @@ const { hostIps } = yield (0, docker_client_1.dockerClient)();

(yield (0, container_logs_1.containerLogs)(this.container, { since: startTime }))
.on("data", (data) => logger_1.containerLog.trace(`${this.container.id}: ${data.trim()}`))
.on("err", (data) => logger_1.containerLog.error(`${this.container.id}: ${data.trim()}`));
.on("data", (data) => logger_1.containerLog.trace(data.trim(), { containerId: this.container.id }))
.on("err", (data) => logger_1.containerLog.error(data.trim(), { containerId: this.container.id }));
}
this.boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, this.inspectResult).filter(Array.from(this.boundPorts.iterator()).map((port) => port[0]));
yield (0, wait_for_container_1.waitForContainer)(this.container, this.waitStrategy, this.boundPorts, startTime);
logger_1.log.info(`Restarted container`, { containerId: this.container.id });
});

@@ -70,3 +71,3 @@ }

return __awaiter(this, void 0, void 0, function* () {
logger_1.log.info(`Stopping container with ID: ${this.container.id}`);
logger_1.log.info(`Stopping container...`, { containerId: this.container.id });
if (this.containerIsStopping) {

@@ -78,2 +79,3 @@ yield this.containerIsStopping();

yield (0, remove_container_1.removeContainer)(this.container, { removeVolumes: resolvedOptions.removeVolumes });
logger_1.log.info(`Stopped container`, { containerId: this.container.id });
if (this.containerIsStopped) {

@@ -115,3 +117,6 @@ yield this.containerIsStopped();

const { dockerode, provider } = yield (0, docker_client_1.dockerClient)();
return (0, exec_container_1.execContainer)(dockerode, provider, this.container, command);
logger_1.log.debug(`Executing command "${command.join(" ")}"...`, { containerId: this.container.id });
const output = yield (0, exec_container_1.execContainer)(dockerode, provider, this.container, command);
logger_1.log.debug(`Executed command "${command.join(" ")}"...`, { containerId: this.container.id });
return output;
});

@@ -118,0 +123,0 @@ }

@@ -5,3 +5,3 @@ "use strict";

exports.SSHD_IMAGE = exports.REAPER_IMAGE = void 0;
exports.REAPER_IMAGE = (_a = process.env["RYUK_CONTAINER_IMAGE"]) !== null && _a !== void 0 ? _a : "testcontainers/ryuk:0.3.4";
exports.REAPER_IMAGE = (_a = process.env["RYUK_CONTAINER_IMAGE"]) !== null && _a !== void 0 ? _a : "testcontainers/ryuk:0.4.0";
exports.SSHD_IMAGE = (_b = process.env["SSHD_CONTAINER_IMAGE"]) !== null && _b !== void 0 ? _b : "testcontainers/sshd:1.1.0";
type Message = string;
export interface Logger {
enabled(): boolean;
trace(message: Message): void;
debug(message: Message): void;
info(message: Message): void;
warn(message: Message): void;
error(message: Message): void;
}
declare class DebugLogger implements Logger {
type Options = {
containerId?: string;
imageName?: string;
};
export declare class Logger {
private readonly showLevel;
private readonly logger;
constructor(namespace: string);
constructor(namespace: string, showLevel?: boolean);
enabled(): boolean;
trace(message: Message): void;
debug(message: Message): void;
info(message: Message): void;
warn(message: Message): void;
error(message: Message): void;
trace(message: Message, options?: Options): void;
debug(message: Message, options?: Options): void;
info(message: Message, options?: Options): void;
warn(message: Message, options?: Options): void;
error(message: Message, options?: Options): void;
private formatMessage;
private renderOptions;
}
export declare class FakeLogger implements Logger {
readonly traceLogs: Message[];
readonly debugLogs: Message[];
readonly infoLogs: Message[];
readonly warnLogs: Message[];
readonly errorLogs: Message[];
enabled(): boolean;
trace(message: Message): void;
debug(message: Message): void;
info(message: Message): void;
warn(message: Message): void;
error(message: Message): void;
}
export declare const log: DebugLogger;
export declare const containerLog: DebugLogger;
export declare const execLog: DebugLogger;
export declare const log: Logger;
export declare const containerLog: Logger;
export declare const composeLog: Logger;
export declare const buildLog: Logger;
export declare const pullLog: Logger;
export declare const execLog: Logger;
export {};

@@ -6,6 +6,7 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.execLog = exports.containerLog = exports.log = exports.FakeLogger = void 0;
exports.execLog = exports.pullLog = exports.buildLog = exports.composeLog = exports.containerLog = exports.log = exports.Logger = void 0;
const debug_1 = __importDefault(require("debug"));
class DebugLogger {
constructor(namespace) {
class Logger {
constructor(namespace, showLevel = true) {
this.showLevel = showLevel;
this.logger = (0, debug_1.default)(namespace);

@@ -16,48 +17,37 @@ }

}
trace(message) {
this.logger(`TRACE ${message}`);
trace(message, options) {
this.logger(this.formatMessage(message, "TRACE", options));
}
debug(message) {
this.logger(`DEBUG ${message}`);
debug(message, options) {
this.logger(this.formatMessage(message, "DEBUG", options));
}
info(message) {
this.logger(`INFO ${message}`);
info(message, options) {
this.logger(this.formatMessage(message, "INFO", options));
}
warn(message) {
this.logger(`WARN ${message}`);
warn(message, options) {
this.logger(this.formatMessage(message, "WARN", options));
}
error(message) {
this.logger(`ERROR ${message}`);
error(message, options) {
this.logger(this.formatMessage(message, "ERROR", options));
}
}
class FakeLogger {
constructor() {
this.traceLogs = [];
this.debugLogs = [];
this.infoLogs = [];
this.warnLogs = [];
this.errorLogs = [];
formatMessage(message, level, options) {
return `${this.showLevel ? `[${level}] ` : ""}${this.renderOptions(options)}${message}`;
}
enabled() {
return true;
renderOptions(options) {
let str = "";
if (options === null || options === void 0 ? void 0 : options.containerId) {
str += `[${options.containerId.substring(0, 12)}] `;
}
if (options === null || options === void 0 ? void 0 : options.imageName) {
str += `[${options.imageName}] `;
}
return str;
}
trace(message) {
this.traceLogs.push(message);
}
debug(message) {
this.debugLogs.push(message);
}
info(message) {
this.infoLogs.push(message);
}
warn(message) {
this.warnLogs.push(message);
}
error(message) {
this.errorLogs.push(message);
}
}
exports.FakeLogger = FakeLogger;
exports.log = new DebugLogger("testcontainers");
exports.containerLog = new DebugLogger("testcontainers:containers");
exports.execLog = new DebugLogger("testcontainers:exec");
exports.Logger = Logger;
exports.log = new Logger("testcontainers");
exports.containerLog = new Logger("testcontainers:containers", false);
exports.composeLog = new Logger("testcontainers:compose", false);
exports.buildLog = new Logger("testcontainers:build", false);
exports.pullLog = new Logger("testcontainers:pull", false);
exports.execLog = new Logger("testcontainers:exec", false);

@@ -22,3 +22,3 @@ "use strict";

class NatsContainer extends generic_container_1.GenericContainer {
constructor(image = "nats:2.8-alpine") {
constructor(image = "nats:2.8.4-alpine") {
super(image);

@@ -25,0 +25,0 @@ this.args = {};

@@ -26,6 +26,8 @@ "use strict";

return __awaiter(this, void 0, void 0, function* () {
const options = Object.assign({ name: this.uuid.nextUuid(), driver: "bridge", checkDuplicate: true, internal: false, attachable: false, ingress: false, enableIPv6: false }, this.createNetworkOptions);
const name = this.uuid.nextUuid();
const options = Object.assign({ name, driver: "bridge", checkDuplicate: true, internal: false, attachable: false, ingress: false, enableIPv6: false }, this.createNetworkOptions);
yield reaper_1.ReaperInstance.getInstance();
logger_1.log.info(`Starting network "${name}"...`);
const id = yield (0, create_network_1.createNetwork)(options);
logger_1.log.info(`Started network with ID: ${id}`);
logger_1.log.info(`Started network "${name}" with ID "${id}"`);
return new StartedNetwork(id, options);

@@ -49,4 +51,5 @@ });

return __awaiter(this, void 0, void 0, function* () {
logger_1.log.info(`Stopping network with ID: ${this.id}`);
logger_1.log.info(`Stopping network with ID "${this.id}"...`);
yield (0, remove_network_1.removeNetwork)(this.id);
logger_1.log.info(`Stopped network with ID "${this.id}"`);
return new StoppedNetwork();

@@ -53,0 +56,0 @@ });

@@ -64,3 +64,5 @@ "use strict";

this.isDistroless = true;
logger_1.log.error(`The HostPortWaitStrategy will not work on a distroless image, use an alternate wait strategy for container ${this.container.id}`);
logger_1.log.error(`The HostPortWaitStrategy will not work on a distroless image, use an alternate wait strategy`, {
containerId: this.container.id,
});
}

@@ -74,3 +76,5 @@ }

if (!this.commandOutputs.has(this.commandOutputsKey(result.output))) {
logger_1.log.trace(`Port check result for container ${this.container.id} exit code ${result.exitCode}: ${result.output}`);
logger_1.log.trace(`Port check result exit code ${result.exitCode}: ${result.output}`, {
containerId: this.container.id,
});
this.commandOutputs.add(this.commandOutputsKey(result.output));

@@ -77,0 +81,0 @@ }

@@ -27,4 +27,5 @@ "use strict";

return __awaiter(this, void 0, void 0, function* () {
logger_1.log.info(`Exposing host port ${port}`);
logger_1.log.info(`Exposing host port ${port}...`);
yield this.sshConnection.remoteForward("localhost", port);
logger_1.log.info(`Exposed host port ${port}`);
});

@@ -57,3 +58,3 @@ }

return __awaiter(this, void 0, void 0, function* () {
logger_1.log.debug(`Creating new Port Forwarder`);
logger_1.log.debug(`Creating new Port Forwarder...`);
const username = "root";

@@ -76,4 +77,5 @@ const password = new uuid_1.RandomUuid().nextUuid();

const port = container.getMappedPort(22);
logger_1.log.debug(`Connecting to Port Forwarder on ${host}:${port}`);
logger_1.log.debug(`Connecting to Port Forwarder on "${host}:${port}"...`);
const connection = yield (0, ssh_remote_port_forward_1.createSshConnection)({ host, port, username, password });
logger_1.log.debug(`Connected to Port Forwarder on "${host}:${port}"`);
connection.unref();

@@ -80,0 +82,0 @@ return new PortForwarder(connection, container);

@@ -80,3 +80,3 @@ "use strict";

static createDisabledInstance() {
logger_1.log.debug(`Not creating new Reaper for session: ${session_id_1.sessionId}`);
logger_1.log.debug(`Not creating new Reaper for session "${session_id_1.sessionId}"`);
return Promise.resolve(new DisabledReaper());

@@ -89,3 +89,3 @@ }

: 8080;
logger_1.log.debug(`Creating new Reaper for session: ${session_id_1.sessionId}`);
logger_1.log.debug(`Creating new Reaper for session "${session_id_1.sessionId}"...`);
const container = new generic_container_1.GenericContainer(images_1.REAPER_IMAGE)

@@ -111,3 +111,3 @@ .withName(`testcontainers-ryuk-${session_id_1.sessionId}`)

return new Promise((resolve) => {
logger_1.log.debug(`Connecting to Reaper (attempt ${attempt + 1}) ${containerId} on ${host}:${port}`);
logger_1.log.debug(`Connecting to Reaper (attempt ${attempt + 1}) on "${host}:${port}"...`, { containerId });
const socket = new net_1.Socket();

@@ -120,6 +120,6 @@ socket

if (hadError) {
logger_1.log.error(`Connection to Reaper ${containerId} closed with error`);
logger_1.log.error(`Connection to Reaper closed with error`, { containerId });
}
else {
logger_1.log.warn(`Connection to Reaper ${containerId} closed`);
logger_1.log.warn(`Connection to Reaper closed`, { containerId });
}

@@ -129,3 +129,3 @@ resolve(undefined);

.connect((0, port_1.getContainerPort)(port), host, () => {
logger_1.log.debug(`Connected to Reaper ${containerId}`);
logger_1.log.debug(`Connected to Reaper`, { containerId });
socket.write(`label=${labels_1.LABEL_TESTCONTAINERS_SESSION_ID}=${session_id_1.sessionId}\r\n`);

@@ -136,3 +136,7 @@ const reaper = new RealReaper(startedContainer, socket);

});
}, (result) => result !== undefined, () => new Error(`Failed to connect to Reaper ${containerId}`), 4000);
}, (result) => result !== undefined, () => {
const message = `Failed to connect to Reaper`;
logger_1.log.error(message, { containerId });
return new Error(message);
}, 4000);
if (retryResult instanceof RealReaper) {

@@ -139,0 +143,0 @@ return retryResult;

@@ -24,10 +24,10 @@ "use strict";

const programName = `docker-credential-${credentialProviderName}`;
logger_1.log.debug(`Executing Docker credential provider: ${programName}`);
logger_1.log.debug(`Executing Docker credential provider "${programName}"`);
const credentials = yield this.listCredentials(programName);
if (!Object.keys(credentials).some((aRegistry) => (0, registry_matches_1.registryMatches)(aRegistry, registry))) {
logger_1.log.debug(`No credential found for registry: "${registry}"`);
logger_1.log.debug(`No credential found for registry "${registry}"`);
return undefined;
}
const response = yield this.runCredentialProvider(registry, programName);
const authConfig = {
return {
username: response.Username,

@@ -37,4 +37,2 @@ password: response.Secret,

};
logger_1.log.debug(`Docker credential provider found auth config for ${registry}`);
return authConfig;
});

@@ -41,0 +39,0 @@ }

@@ -50,3 +50,3 @@ "use strict";

if (authsCache.has(registry)) {
logger_1.log.debug(`Re-using cached auth for registry ${registry}`);
logger_1.log.debug(`Auth config cache hit for registry "${registry}"`);
return authsCache.get(registry);

@@ -57,3 +57,3 @@ }

if (authConfig) {
logger_1.log.debug(`Found applicable registry auth locator for registry "${registry}": ${registryAuthLocator.getName()}`);
logger_1.log.debug(`Auth config found for registry "${registry}": ${registryAuthLocator.getName()}`);
authsCache.set(registry, authConfig);

@@ -63,3 +63,3 @@ return authConfig;

}
logger_1.log.debug(`No registry auth locator found for registry: "${registry}"`);
logger_1.log.debug(`No registry auth locator found for registry "${registry}"`);
authsCache.set(registry, undefined);

@@ -66,0 +66,0 @@ return undefined;

@@ -22,3 +22,3 @@ "use strict";

}
logger_1.log.debug("Fetching system info");
logger_1.log.debug("Fetching system info...");
const nodeInfo = getNodeInfo();

@@ -28,6 +28,3 @@ const dockerInfo = yield (0, get_info_1.getDockerInfo)(dockerode);

systemInfo = { nodeInfo, dockerInfo, dockerComposeInfo };
logger_1.log.debug(`System info:
System: Node version: ${nodeInfo.version}, Platform: ${nodeInfo.platform}, Arch: ${nodeInfo.architecture}
Container runtime: OS: ${dockerInfo.operatingSystem}, Version: ${dockerInfo.serverVersion}, Arch: ${dockerInfo.architecture}, CPUs: ${dockerInfo.cpus}, Memory: ${dockerInfo.memory}
Compose: Installed: ${dockerComposeInfo !== undefined}, Version: ${(_a = dockerComposeInfo === null || dockerComposeInfo === void 0 ? void 0 : dockerComposeInfo.version) !== null && _a !== void 0 ? _a : "N/A"}`);
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"}`);
return systemInfo;

@@ -34,0 +31,0 @@ });

@@ -33,2 +33,3 @@ /// <reference types="node" />

withResourcesQuota(resourcesQuota: ResourcesQuota): this;
withLogConsumer(logConsumer: (stream: Readable) => unknown): this;
}

@@ -35,0 +36,0 @@ export interface RestartOptions {

@@ -10,5 +10,5 @@ "use strict";

nextUuid() {
return crypto_1.default.randomUUID();
return crypto_1.default.createHash("md5").update(crypto_1.default.randomUUID()).digest("hex").substring(0, 12);
}
}
exports.RandomUuid = RandomUuid;

@@ -17,9 +17,9 @@ "use strict";

const waitForContainer = (container, waitStrategy, boundPorts, startTime) => __awaiter(void 0, void 0, void 0, function* () {
logger_1.log.debug(`Waiting for container to be ready: ${container.id}`);
logger_1.log.debug(`Waiting for container to be ready...`, { containerId: container.id });
try {
yield waitStrategy.waitUntilReady(container, boundPorts, startTime);
logger_1.log.info(`Container is ready: ${container.id}`);
logger_1.log.info(`Container is ready`, { containerId: container.id });
}
catch (err) {
logger_1.log.error(`Container failed to be ready: ${container.id}: ${err}`);
logger_1.log.error(`Container failed to be ready: ${err}`, { containerId: container.id });
try {

@@ -30,3 +30,3 @@ yield (0, stop_container_1.stopContainer)(container, { timeout: 0 });

catch (stopErr) {
logger_1.log.error(`Failed to stop container after it failed to be ready: ${container.id}: ${stopErr}`);
logger_1.log.error(`Failed to stop container after it failed to be ready: ${stopErr}`, { containerId: container.id });
}

@@ -33,0 +33,0 @@ throw err;

@@ -22,3 +22,3 @@ "use strict";

return __awaiter(this, void 0, void 0, function* () {
logger_1.log.debug(`Starting composite wait strategy for ${container.id}`);
logger_1.log.debug(`Waiting for composite...`, { containerId: container.id });
return new Promise((resolve, reject) => {

@@ -28,4 +28,4 @@ let deadlineTimeout;

deadlineTimeout = setTimeout(() => {
const message = `Composite wait strategy not successful after ${this.deadline}ms for ${container.id}`;
logger_1.log.error(message);
const message = `Composite wait strategy not successful after ${this.deadline}ms`;
logger_1.log.error(message, { containerId: container.id });
reject(new Error(message));

@@ -47,3 +47,3 @@ }, this.deadline);

this.waitStrategies
.filter((waitStrategy) => waitStrategy.getStartupTimeout() === undefined)
.filter((waitStrategy) => !waitStrategy.isStartupTimeoutSet())
.forEach((waitStrategy) => waitStrategy.withStartupTimeout(startupTimeout));

@@ -50,0 +50,0 @@ return this;

@@ -19,11 +19,14 @@ "use strict";

waitUntilReady(container) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
logger_1.log.debug(`Waiting for health check for ${container.id}`);
logger_1.log.debug(`Waiting for health check...`, { containerId: container.id });
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", () => {
const timeout = this.startupTimeout;
throw new Error(`Health check not healthy after ${timeout}ms for ${container.id}`);
}, (_a = this.startupTimeout) !== null && _a !== void 0 ? _a : wait_strategy_1.DEFAULT_STARTUP_TIMEOUT);
const message = `Health check not healthy after ${timeout}ms`;
logger_1.log.error(message, { containerId: container.id });
throw new Error(message);
}, this.startupTimeout);
if (status !== "healthy") {
throw new Error(`Health check failed: ${status} for ${container.id}`);
const message = `Health check failed: ${status}`;
logger_1.log.error(message, { containerId: container.id });
throw new Error(message);
}

@@ -30,0 +33,0 @@ });

@@ -33,5 +33,5 @@ "use strict";

for (const [, hostPort] of boundPorts.iterator()) {
logger_1.log.debug(`Waiting for host port ${hostPort} for ${container.id}`);
logger_1.log.debug(`Waiting for host port ${hostPort}...`, { containerId: container.id });
yield this.waitForPort(container, hostPort, portCheck);
logger_1.log.debug(`Host port ${hostPort} ready for ${container.id}`);
logger_1.log.debug(`Host port ${hostPort} ready`, { containerId: container.id });
}

@@ -43,5 +43,5 @@ });

for (const [internalPort] of boundPorts.iterator()) {
logger_1.log.debug(`Waiting for internal port ${internalPort} for ${container.id}`);
logger_1.log.debug(`Waiting for internal port ${internalPort}...`, { containerId: container.id });
yield this.waitForPort(container, internalPort, portCheck);
logger_1.log.debug(`Internal port ${internalPort} ready for ${container.id}`);
logger_1.log.debug(`Internal port ${internalPort} ready`, { containerId: container.id });
}

@@ -51,8 +51,8 @@ });

waitForPort(container, port, portCheck) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const startupTimeout = (_a = this.startupTimeout) !== null && _a !== void 0 ? _a : wait_strategy_1.DEFAULT_STARTUP_TIMEOUT;
yield new retry_strategy_1.IntervalRetryStrategy(100).retryUntil(() => portCheck.isBound(port), (isBound) => isBound, () => {
throw new Error(`Port ${port} not bound after ${startupTimeout}ms for ${container.id}`);
}, startupTimeout);
const message = `Port ${port} not bound after ${this.startupTimeout}ms`;
logger_1.log.error(message, { containerId: container.id });
throw new Error(message);
}, this.startupTimeout);
});

@@ -59,0 +59,0 @@ }

@@ -21,2 +21,3 @@ "use strict";

const docker_client_1 = require("../docker/docker-client");
const logger_1 = require("../logger");
class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {

@@ -72,6 +73,5 @@ constructor(path, port) {

waitUntilReady(container, boundPorts) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
logger_1.log.debug(`Waiting for HTTP...`, { containerId: container.id });
const { host } = yield (0, docker_client_1.dockerClient)();
const startupTimeout = (_a = this.startupTimeout) !== null && _a !== void 0 ? _a : wait_strategy_1.DEFAULT_STARTUP_TIMEOUT;
yield new retry_strategy_1.IntervalRetryStrategy(this.readTimeout).retryUntil(() => __awaiter(this, void 0, void 0, function* () {

@@ -87,3 +87,3 @@ try {

}
catch (_b) {
catch (_a) {
return undefined;

@@ -108,4 +108,6 @@ }

}), () => {
throw new Error(`URL ${this.path} not accessible after ${startupTimeout}ms for ${container.id}`);
}, startupTimeout);
const message = `URL ${this.path} not accessible after ${this.startupTimeout}ms`;
logger_1.log.error(message, { containerId: container.id });
throw new Error(message);
}, this.startupTimeout);
});

@@ -112,0 +114,0 @@ }

@@ -27,13 +27,11 @@ "use strict";

waitUntilReady(container, boundPorts, startTime) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
logger_1.log.debug(`Waiting for log message "${this.message}" for ${container.id}`);
const startupTimeout = (_a = this.startupTimeout) !== null && _a !== void 0 ? _a : wait_strategy_1.DEFAULT_STARTUP_TIMEOUT;
logger_1.log.debug(`Waiting for log message "${this.message}"...`, { containerId: container.id });
const stream = yield (0, container_logs_1.containerLogs)(container, { since: startTime });
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const message = `Log message "${this.message}" not received after ${startupTimeout}ms for ${container.id}`;
logger_1.log.error(message);
const message = `Log message "${this.message}" not received after ${this.startupTimeout}ms`;
logger_1.log.error(message, { containerId: container.id });
reject(new Error(message));
}, startupTimeout);
}, this.startupTimeout);
const comparisonFn = (line) => {

@@ -63,3 +61,5 @@ if (this.message instanceof RegExp) {

clearTimeout(timeout);
reject(new Error(`Log stream ended and message "${this.message}" was not received for ${container.id}`));
const message = `Log stream ended and message "${this.message}" was not received`;
logger_1.log.error(message, { containerId: container.id });
reject(new Error(message));
});

@@ -66,0 +66,0 @@ });

@@ -24,7 +24,5 @@ "use strict";

waitUntilReady(container) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
logger_1.log.debug(`Waiting for successful shell command ${this.command} for ${container.id}`);
logger_1.log.debug(`Waiting for successful shell command "${this.command}"...`, { containerId: container.id });
const { dockerode, provider } = yield (0, docker_client_1.dockerClient)();
const startupTimeout = (_a = this.startupTimeout) !== null && _a !== void 0 ? _a : wait_strategy_1.DEFAULT_STARTUP_TIMEOUT;
yield new retry_strategy_1.IntervalRetryStrategy(100).retryUntil(() => __awaiter(this, void 0, void 0, function* () {

@@ -34,4 +32,6 @@ const { exitCode } = yield (0, exec_container_1.execContainer)(dockerode, provider, container, ["/bin/sh", "-c", this.command], false);

}), (exitCode) => exitCode === 0, () => {
throw new Error(`Shell command "${this.command}" not successful after ${startupTimeout}ms for ${container.id}`);
}, startupTimeout);
const message = `Shell command "${this.command}" not successful after ${this.startupTimeout}ms`;
logger_1.log.error(message, { containerId: container.id });
throw new Error(message);
}, this.startupTimeout);
});

@@ -38,0 +38,0 @@ }

@@ -16,9 +16,12 @@ "use strict";

const docker_client_1 = require("../docker/docker-client");
const logger_1 = require("../logger");
class StartupCheckStrategy extends wait_strategy_1.AbstractWaitStrategy {
waitUntilReady(container) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const { dockerode } = yield (0, docker_client_1.dockerClient)();
const startupTimeout = (_a = this.startupTimeout) !== null && _a !== void 0 ? _a : wait_strategy_1.DEFAULT_STARTUP_TIMEOUT;
const startupStatus = yield new retry_strategy_1.IntervalRetryStrategy(1000).retryUntil(() => __awaiter(this, void 0, void 0, function* () { return yield this.checkStartupState(dockerode, container.id); }), (startupStatus) => startupStatus === "SUCCESS" || startupStatus === "FAIL", () => new Error(`Container not accessible after ${startupTimeout}ms for ${container.id}`), startupTimeout);
const startupStatus = yield new retry_strategy_1.IntervalRetryStrategy(1000).retryUntil(() => __awaiter(this, void 0, void 0, function* () { return yield this.checkStartupState(dockerode, container.id); }), (startupStatus) => startupStatus === "SUCCESS" || startupStatus === "FAIL", () => {
const message = `Container not accessible after ${this.startupTimeout}ms`;
logger_1.log.error(message, { containerId: container.id });
return new Error(message);
}, this.startupTimeout);
if (startupStatus instanceof Error) {

@@ -25,0 +28,0 @@ throw startupStatus;

import { BoundPorts } from "../bound-ports";
import Dockerode from "dockerode";
export declare const DEFAULT_STARTUP_TIMEOUT = 60000;
export interface WaitStrategy {
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void>;
withStartupTimeout(startupTimeout: number): WaitStrategy;
getStartupTimeout(): number | undefined;
isStartupTimeoutSet(): boolean;
getStartupTimeout(): number;
}
export declare abstract class AbstractWaitStrategy implements WaitStrategy {
protected startupTimeout?: number;
protected startupTimeout: number;
private startupTimeoutSet;
abstract waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void>;
withStartupTimeout(startupTimeout: number): this;
getStartupTimeout(): number | undefined;
isStartupTimeoutSet(): boolean;
getStartupTimeout(): number;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractWaitStrategy = exports.DEFAULT_STARTUP_TIMEOUT = void 0;
exports.DEFAULT_STARTUP_TIMEOUT = 60000;
exports.AbstractWaitStrategy = void 0;
class AbstractWaitStrategy {
constructor() {
this.startupTimeout = 60000;
this.startupTimeoutSet = false;
}
withStartupTimeout(startupTimeout) {
this.startupTimeout = startupTimeout;
this.startupTimeoutSet = true;
return this;
}
isStartupTimeoutSet() {
return this.startupTimeoutSet;
}
getStartupTimeout() {

@@ -11,0 +18,0 @@ return this.startupTimeout;

{
"name": "testcontainers",
"author": "Cristian Greco",
"version": "9.6.0",
"version": "9.7.0",
"main": "dist/src/index",

@@ -59,4 +59,4 @@ "types": "dist/src/index",

"@types/debug": "4.1.7",
"@types/jest": "^29.5.0",
"@types/node": "^18.15.11",
"@types/jest": "^29.5.1",
"@types/node": "^18.16.2",
"@types/node-fetch": "^2.6.3",

@@ -66,7 +66,7 @@ "@types/pg": "^8.6.6",

"@types/tar-fs": "^2.0.1",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"arangojs": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"arangojs": "^8.2.1",
"cross-env": "^7.0.3",
"eslint": "^8.37.0",
"eslint": "^8.39.0",
"eslint-config-prettier": "^8.8.0",

@@ -77,11 +77,11 @@ "eslint-plugin-prettier": "^4.2.1",

"kafkajs": "^2.2.4",
"lint-staged": "^13.2.0",
"mongoose": "^6.10.4",
"lint-staged": "^13.2.2",
"mongoose": "^6.10.5",
"mysql2": "^2.3.3",
"nats": "^2.13.1",
"neo4j-driver": "^5.6.0",
"neo4j-driver": "^5.7.0",
"pg": "^8.10.0",
"prettier": "^2.8.7",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
"ts-jest": "^29.0.5",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",

@@ -88,0 +88,0 @@ "typescript": "^4.9.5",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc