testcontainers
Advanced tools
Comparing version 9.6.0 to 9.7.0
{ | ||
"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", |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
293352
6100
25