testcontainers
Advanced tools
Comparing version 9.5.0 to 9.6.0
{ | ||
"name": "testcontainers", | ||
"author": "Cristian Greco", | ||
"version": "9.5.0", | ||
"version": "9.6.0", | ||
"main": "dist/src/index", | ||
@@ -30,4 +30,4 @@ "types": "dist/src/index", | ||
"clean": "rimraf dist", | ||
"test": "cross-env DEBUG=testcontainers,testcontainers:exec,testcontainers:containers jest", | ||
"test:ci": "cross-env DEBUG=testcontainers,testcontainers:exec,testcontainers:containers jest --runInBand --coverage", | ||
"test": "cross-env DEBUG=testcontainers* jest", | ||
"test:ci": "npm run test -- --runInBand --coverage", | ||
"format": "prettier --write package.json \"src/**/*.ts\"", | ||
@@ -34,0 +34,0 @@ "lint": "eslint --fix package.json \"src/**/*.ts\"", |
@@ -17,3 +17,3 @@ import { WaitStrategy } from "../wait-strategy/wait-strategy"; | ||
private waitStrategy; | ||
private startupTimeout; | ||
private startupTimeout?; | ||
constructor(composeFilePath: string, composeFiles: string | string[], uuid?: Uuid); | ||
@@ -20,0 +20,0 @@ withBuild(): this; |
@@ -28,5 +28,5 @@ "use strict"; | ||
const wait_for_container_1 = require("../wait-for-container"); | ||
const default_wait_strategy_1 = require("../wait-strategy/default-wait-strategy"); | ||
const pull_policy_1 = require("../pull-policy"); | ||
const docker_compose_pull_1 = require("../docker-compose/functions/docker-compose-pull"); | ||
const wait_1 = require("../wait-strategy/wait"); | ||
class DockerComposeEnvironment { | ||
@@ -41,3 +41,2 @@ constructor(composeFilePath, composeFiles, uuid = new uuid_1.RandomUuid()) { | ||
this.waitStrategy = {}; | ||
this.startupTimeout = 60000; | ||
this.composeFilePath = composeFilePath; | ||
@@ -117,3 +116,8 @@ this.composeFiles = composeFiles; | ||
const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, inspectResult); | ||
const waitStrategy = (this.waitStrategy[containerName] ? this.waitStrategy[containerName] : (0, default_wait_strategy_1.defaultWaitStrategy)(host, container)).withStartupTimeout(this.startupTimeout); | ||
const waitStrategy = this.waitStrategy[containerName] | ||
? this.waitStrategy[containerName] | ||
: wait_1.Wait.forListeningPorts(); | ||
if (this.startupTimeout !== undefined) { | ||
waitStrategy.withStartupTimeout(this.startupTimeout); | ||
} | ||
if (logger_1.containerLog.enabled()) { | ||
@@ -126,3 +130,3 @@ (yield (0, container_logs_1.containerLogs)(container)) | ||
logger_1.log.info(`Waiting for container ${containerName} to be ready`); | ||
yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, host, boundPorts); | ||
yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, boundPorts); | ||
logger_1.log.info(`Container ${containerName} is ready`); | ||
@@ -129,0 +133,0 @@ } |
/// <reference types="node" /> | ||
import Dockerode from "dockerode"; | ||
import { HostIps } from "./lookup-host-ips"; | ||
export type Provider = "docker" | "podman"; | ||
type DockerClient = { | ||
uri: string; | ||
provider: Provider; | ||
host: string; | ||
@@ -7,0 +9,0 @@ hostIps: HostIps; |
@@ -46,3 +46,4 @@ "use strict"; | ||
const indexServerAddress = (yield (0, system_info_1.getSystemInfo)(dockerode)).dockerInfo.indexServerAddress; | ||
const host = yield (0, resolve_host_1.resolveHost)(dockerode, indexServerAddress, uri); | ||
const provider = uri.includes("podman.sock") ? "podman" : "docker"; | ||
const host = yield (0, resolve_host_1.resolveHost)(dockerode, provider, indexServerAddress, uri); | ||
const hostIps = yield (0, lookup_host_ips_1.lookupHostIps)(host); | ||
@@ -52,3 +53,3 @@ logger_1.log.info(`Using Docker client strategy: ${strategy.getName()}, Docker host: ${host} (${hostIps | ||
.join(", ")})`); | ||
return { uri, host, hostIps, dockerode, indexServerAddress, composeEnvironment }; | ||
return { uri, provider, host, hostIps, dockerode, indexServerAddress, composeEnvironment }; | ||
} | ||
@@ -55,0 +56,0 @@ else { |
import { ExecResult } from "../../types"; | ||
import Dockerode from "dockerode"; | ||
export declare const execContainer: (container: Dockerode.Container, command: string[], shouldLog?: boolean) => Promise<ExecResult>; | ||
import { Provider } from "../../docker-client"; | ||
export declare const execContainer: (dockerode: Dockerode, provider: Provider, container: Dockerode.Container, command: string[], shouldLog?: boolean) => Promise<ExecResult>; |
@@ -18,3 +18,4 @@ "use strict"; | ||
const byline_1 = __importDefault(require("byline")); | ||
const execContainer = (container, command, shouldLog = true) => __awaiter(void 0, void 0, void 0, function* () { | ||
const demux_stream_1 = require("../demux-stream"); | ||
const execContainer = (dockerode, provider, container, command, shouldLog = true) => __awaiter(void 0, void 0, void 0, function* () { | ||
const chunks = []; | ||
@@ -27,3 +28,3 @@ try { | ||
}); | ||
const stream = yield startExec(exec); | ||
const stream = yield startExec(dockerode, provider, exec); | ||
stream.on("data", (chunk) => chunks.push(chunk)); | ||
@@ -43,7 +44,11 @@ if (shouldLog && logger_1.execLog.enabled()) { | ||
exports.execContainer = execContainer; | ||
const startExec = (exec) => __awaiter(void 0, void 0, void 0, function* () { | ||
const startExec = (dockerode, provider, exec) => __awaiter(void 0, void 0, void 0, function* () { | ||
try { | ||
const stream = yield exec.start({ stdin: true, Detach: false, Tty: true }); | ||
stream.setEncoding("utf-8"); | ||
return stream; | ||
if (provider === "podman") { | ||
return (0, demux_stream_1.demuxStream)(dockerode, stream); | ||
} | ||
else { | ||
return stream; | ||
} | ||
} | ||
@@ -50,0 +55,0 @@ catch (err) { |
@@ -13,6 +13,9 @@ "use strict"; | ||
exports.putContainerArchive = void 0; | ||
const stream_1 = require("stream"); | ||
const logger_1 = require("../../../logger"); | ||
const stream_utils_1 = require("../../../stream-utils"); | ||
const putContainerArchive = (options) => __awaiter(void 0, void 0, void 0, function* () { | ||
try { | ||
yield options.container.putArchive(options.stream, { path: options.containerPath }); | ||
const stream = yield options.container.putArchive(options.stream, { path: options.containerPath }); | ||
yield (0, stream_utils_1.streamToString)(stream_1.Readable.from(stream)); | ||
} | ||
@@ -19,0 +22,0 @@ catch (err) { |
@@ -16,9 +16,6 @@ "use strict"; | ||
exports.imageExists = void 0; | ||
const logger_1 = require("../../../logger"); | ||
const async_lock_1 = __importDefault(require("async-lock")); | ||
const list_images_1 = require("./list-images"); | ||
const existingImages = new Set(); | ||
const imageCheckLock = new async_lock_1.default(); | ||
const imageExists = (dockerode, imageName) => __awaiter(void 0, void 0, void 0, function* () { | ||
logger_1.log.debug(`Checking if image exists: ${imageName}`); | ||
return imageCheckLock.acquire(imageName.toString(), () => __awaiter(void 0, void 0, void 0, function* () { | ||
@@ -28,9 +25,15 @@ if (existingImages.has(imageName.toString())) { | ||
} | ||
const images = yield (0, list_images_1.listImages)(dockerode); | ||
images.forEach((name) => { | ||
existingImages.add(name.toString()); | ||
}); | ||
return existingImages.has(imageName.toString()); | ||
try { | ||
yield dockerode.getImage(imageName.toString()).inspect(); | ||
existingImages.add(imageName.toString()); | ||
return true; | ||
} | ||
catch (err) { | ||
if (err instanceof Error && err.message.toLowerCase().includes("no such image")) { | ||
return false; | ||
} | ||
throw err; | ||
} | ||
})); | ||
}); | ||
exports.imageExists = imageExists; |
@@ -10,3 +10,2 @@ /// <reference types="node" /> | ||
consume(stream: Readable): Promise<void>; | ||
private getOrElse; | ||
} |
@@ -14,51 +14,8 @@ "use strict"; | ||
consume(stream) { | ||
const messagesById = new Map(); | ||
const statusesById = new Map(); | ||
return new Promise((resolve) => { | ||
(0, byline_1.default)(stream).on("data", (line) => { | ||
try { | ||
const json = JSON.parse(line); | ||
const { id, status } = json; | ||
const prefix = id | ||
? `Pulling ${this.dockerImageName.toString()} - ${id}` | ||
: `Pulling ${this.dockerImageName.toString()}`; | ||
if (status === "Downloading") { | ||
const { current, total } = json.progressDetail; | ||
const percentage = Math.round(100 * (current / total)); | ||
const message = `${prefix} - ${json.status} ${percentage}%`; | ||
const messages = this.getOrElse(messagesById, id, new Set()); | ||
if (!messages.has(message)) { | ||
messages.add(message); | ||
this.logger.trace(message); | ||
} | ||
} | ||
else { | ||
const statuses = this.getOrElse(statusesById, id, new Set()); | ||
if (!statuses.has(status)) { | ||
statuses.add(status); | ||
const message = `${prefix} - ${json.status}`; | ||
const messages = this.getOrElse(messagesById, id, new Set()); | ||
if (!messages.has(message)) { | ||
messages.add(message); | ||
this.logger.trace(message); | ||
} | ||
} | ||
} | ||
} | ||
catch (_a) { | ||
this.logger.warn(`Unexpected message format: ${line}`); | ||
} | ||
}); | ||
(0, byline_1.default)(stream).on("data", (line) => this.logger.trace(`Pulling ${this.dockerImageName.toString()}: ${line}`)); | ||
stream.on("end", resolve); | ||
}); | ||
} | ||
getOrElse(map, key, orElse) { | ||
const value = map.get(key); | ||
if (value === undefined) { | ||
map.set(key, orElse); | ||
return orElse; | ||
} | ||
return value; | ||
} | ||
} | ||
exports.PullStreamParser = PullStreamParser; |
@@ -6,2 +6,3 @@ /// <reference types="node" /> | ||
import Dockerode from "dockerode"; | ||
export declare const resolveHost: (dockerode: Dockerode, indexServerAddress: string, uri: string, env?: NodeJS.ProcessEnv) => Promise<string>; | ||
import { Provider } from "./docker-client"; | ||
export declare const resolveHost: (dockerode: Dockerode, provider: Provider, indexServerAddress: string, uri: string, env?: NodeJS.ProcessEnv) => Promise<string>; |
@@ -17,3 +17,3 @@ "use strict"; | ||
const fs_1 = require("fs"); | ||
const resolveHost = (dockerode, indexServerAddress, uri, env = process.env) => __awaiter(void 0, void 0, void 0, function* () { | ||
const resolveHost = (dockerode, provider, indexServerAddress, uri, env = process.env) => __awaiter(void 0, void 0, void 0, function* () { | ||
if (env.TESTCONTAINERS_HOST_OVERRIDE !== undefined) { | ||
@@ -31,3 +31,4 @@ return env.TESTCONTAINERS_HOST_OVERRIDE; | ||
if (isInContainer()) { | ||
const gateway = yield findGateway(dockerode); | ||
const networkName = provider === "podman" ? "podman" : "bridge"; | ||
const gateway = yield findGateway(dockerode, networkName); | ||
if (gateway !== undefined) { | ||
@@ -48,6 +49,6 @@ return gateway; | ||
exports.resolveHost = resolveHost; | ||
const findGateway = (dockerode) => __awaiter(void 0, void 0, void 0, function* () { | ||
const findGateway = (dockerode, networkName) => __awaiter(void 0, void 0, void 0, function* () { | ||
var _a, _b, _c; | ||
logger_1.log.debug(`Checking gateway for Docker host`); | ||
const inspectResult = yield dockerode.getNetwork("bridge").inspect(); | ||
const inspectResult = yield dockerode.getNetwork(networkName).inspect(); | ||
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; | ||
@@ -54,0 +55,0 @@ }); |
@@ -16,4 +16,4 @@ import archiver from "archiver"; | ||
protected opts: CreateContainerOptions; | ||
protected startupTimeout: number; | ||
protected waitStrategy?: WaitStrategy; | ||
protected startupTimeout?: number; | ||
protected waitStrategy: WaitStrategy; | ||
protected tarToCopy?: archiver.Archiver; | ||
@@ -20,0 +20,0 @@ protected networkMode?: string; |
@@ -39,3 +39,3 @@ "use strict"; | ||
const create_container_options_1 = require("./create-container-options"); | ||
const default_wait_strategy_1 = require("../wait-strategy/default-wait-strategy"); | ||
const wait_1 = require("../wait-strategy/wait"); | ||
const reusableContainerCreationLock = new async_lock_1.default(); | ||
@@ -48,3 +48,3 @@ class GenericContainer { | ||
this.image = image; | ||
this.startupTimeout = 60000; | ||
this.waitStrategy = wait_1.Wait.forListeningPorts(); | ||
this.networkAliases = []; | ||
@@ -106,3 +106,2 @@ this.pullPolicy = new pull_policy_1.DefaultPullPolicy(); | ||
reuseContainer(container) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -112,8 +111,10 @@ const { host, hostIps } = yield (0, docker_client_1.dockerClient)(); | ||
const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, inspectResult).filter(this.opts.exposedPorts); | ||
const waitStrategy = ((_a = this.waitStrategy) !== null && _a !== void 0 ? _a : (0, default_wait_strategy_1.defaultWaitStrategy)(host, container)).withStartupTimeout(this.startupTimeout); | ||
if (this.startupTimeout !== undefined) { | ||
this.waitStrategy.withStartupTimeout(this.startupTimeout); | ||
} | ||
if (this.containerStarting) { | ||
yield this.containerStarting(inspectResult, true); | ||
} | ||
yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, host, boundPorts); | ||
const startedContainer = new started_generic_container_1.StartedGenericContainer(container, host, inspectResult, boundPorts, inspectResult.name, waitStrategy); | ||
yield (0, wait_for_container_1.waitForContainer)(container, this.waitStrategy, boundPorts); | ||
const startedContainer = new started_generic_container_1.StartedGenericContainer(container, host, inspectResult, boundPorts, inspectResult.name, this.waitStrategy); | ||
if (this.containerStarted) { | ||
@@ -129,3 +130,2 @@ yield this.containerStarted(startedContainer, inspectResult, true); | ||
startContainer(createContainerOptions) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -164,3 +164,5 @@ const container = yield (0, create_container_1.createContainer)(createContainerOptions); | ||
const boundPorts = bound_ports_1.BoundPorts.fromInspectResult(hostIps, inspectResult).filter(this.opts.exposedPorts); | ||
const waitStrategy = ((_a = this.waitStrategy) !== null && _a !== void 0 ? _a : (0, default_wait_strategy_1.defaultWaitStrategy)(host, container)).withStartupTimeout(this.startupTimeout); | ||
if (this.startupTimeout !== undefined) { | ||
this.waitStrategy.withStartupTimeout(this.startupTimeout); | ||
} | ||
if (logger_1.containerLog.enabled()) { | ||
@@ -174,4 +176,4 @@ (yield (0, container_logs_1.containerLogs)(container)) | ||
} | ||
yield (0, wait_for_container_1.waitForContainer)(container, waitStrategy, host, boundPorts); | ||
const startedContainer = new started_generic_container_1.StartedGenericContainer(container, host, inspectResult, boundPorts, inspectResult.name, waitStrategy); | ||
yield (0, wait_for_container_1.waitForContainer)(container, this.waitStrategy, boundPorts); | ||
const startedContainer = new started_generic_container_1.StartedGenericContainer(container, host, inspectResult, boundPorts, inspectResult.name, this.waitStrategy); | ||
if (this.containerStarted) { | ||
@@ -178,0 +180,0 @@ yield this.containerStarted(startedContainer, inspectResult, false); |
@@ -16,2 +16,4 @@ /// <reference types="node" /> | ||
private readonly waitStrategy; | ||
private stoppedContainer?; | ||
private stopContainerLock; | ||
constructor(container: Dockerode.Container, host: string, inspectResult: InspectResult, boundPorts: BoundPorts, name: string, waitStrategy: WaitStrategy); | ||
@@ -18,0 +20,0 @@ protected containerIsStopping?(): Promise<void>; |
@@ -11,2 +11,5 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -25,2 +28,3 @@ exports.StartedGenericContainer = void 0; | ||
const docker_client_1 = require("../docker/docker-client"); | ||
const async_lock_1 = __importDefault(require("async-lock")); | ||
class StartedGenericContainer { | ||
@@ -34,6 +38,13 @@ constructor(container, host, inspectResult, boundPorts, name, waitStrategy) { | ||
this.waitStrategy = waitStrategy; | ||
this.stopContainerLock = new async_lock_1.default(); | ||
} | ||
stop(options = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return this.stopContainer(options); | ||
return this.stopContainerLock.acquire("stop", () => __awaiter(this, void 0, void 0, function* () { | ||
if (this.stoppedContainer) { | ||
return this.stoppedContainer; | ||
} | ||
this.stoppedContainer = yield this.stopContainer(options); | ||
return this.stoppedContainer; | ||
})); | ||
}); | ||
@@ -55,3 +66,3 @@ } | ||
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.host, this.boundPorts, startTime); | ||
yield (0, wait_for_container_1.waitForContainer)(this.container, this.waitStrategy, this.boundPorts, startTime); | ||
}); | ||
@@ -102,3 +113,6 @@ } | ||
exec(command) { | ||
return (0, exec_container_1.execContainer)(this.container, command); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const { dockerode, provider } = yield (0, docker_client_1.dockerClient)(); | ||
return (0, exec_container_1.execContainer)(dockerode, provider, this.container, command); | ||
}); | ||
} | ||
@@ -105,0 +119,0 @@ logs() { |
@@ -29,3 +29,3 @@ "use strict"; | ||
{ | ||
content: "-Xms2G\n-Xmx2G\n", | ||
content: "-Xmx2G\n", | ||
target: "/usr/share/elasticsearch/config/jvm.options.d/elasticsearch-default-memory-vm.options", | ||
@@ -32,0 +32,0 @@ }, |
@@ -5,2 +5,3 @@ import { Uuid } from "./uuid"; | ||
private readonly createNetworkOptions; | ||
private readonly uuid; | ||
constructor(createNetworkOptions?: Partial<CreateNetworkOptions>, uuid?: Uuid); | ||
@@ -7,0 +8,0 @@ start(): Promise<StartedNetwork>; |
@@ -20,10 +20,13 @@ "use strict"; | ||
constructor(createNetworkOptions = {}, uuid = new uuid_1.RandomUuid()) { | ||
this.createNetworkOptions = Object.assign({ name: uuid.nextUuid(), driver: "bridge", checkDuplicate: true, internal: false, attachable: false, ingress: false, enableIPv6: false }, createNetworkOptions); | ||
this.createNetworkOptions = createNetworkOptions; | ||
this.uuid = uuid; | ||
this.createNetworkOptions = createNetworkOptions; | ||
} | ||
start() { | ||
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); | ||
yield reaper_1.ReaperInstance.getInstance(); | ||
const id = yield (0, create_network_1.createNetwork)(this.createNetworkOptions); | ||
const id = yield (0, create_network_1.createNetwork)(options); | ||
logger_1.log.info(`Started network with ID: ${id}`); | ||
return new StartedNetwork(id, this.createNetworkOptions); | ||
return new StartedNetwork(id, options); | ||
}); | ||
@@ -30,0 +33,0 @@ } |
import Dockerode from "dockerode"; | ||
import { Provider } from "./docker/docker-client"; | ||
export interface PortCheck { | ||
@@ -11,8 +12,10 @@ isBound(port: number): Promise<boolean>; | ||
export declare class InternalPortCheck implements PortCheck { | ||
private readonly dockerode; | ||
private readonly provider; | ||
private readonly container; | ||
private isDistroless; | ||
private commandOutputs; | ||
constructor(container: Dockerode.Container); | ||
constructor(dockerode: Dockerode, provider: Provider, container: Dockerode.Container); | ||
isBound(port: number): Promise<boolean>; | ||
private commandOutputsKey; | ||
} |
@@ -42,3 +42,5 @@ "use strict"; | ||
class InternalPortCheck { | ||
constructor(container) { | ||
constructor(dockerode, provider, container) { | ||
this.dockerode = dockerode; | ||
this.provider = provider; | ||
this.container = container; | ||
@@ -56,3 +58,3 @@ this.isDistroless = false; | ||
]; | ||
const commandResults = yield Promise.all(commands.map((command) => (0, exec_container_1.execContainer)(this.container, command, false))); | ||
const commandResults = yield Promise.all(commands.map((command) => (0, exec_container_1.execContainer)(this.dockerode, this.provider, this.container, command, false))); | ||
const isBound = commandResults.some((result) => result.exitCode === 0); | ||
@@ -59,0 +61,0 @@ if (!isBound && logger_1.log.enabled()) { |
@@ -45,7 +45,2 @@ /// <reference types="node" /> | ||
} | ||
export interface ExecOptions { | ||
tty: boolean; | ||
detach: boolean; | ||
stdin: boolean; | ||
} | ||
export interface StartedTestContainer { | ||
@@ -63,3 +58,3 @@ stop(options?: Partial<StopOptions>): Promise<StoppedTestContainer>; | ||
getIpAddress(networkName: string): string; | ||
exec(command: string[], options?: Partial<ExecOptions>): Promise<ExecResult>; | ||
exec(command: string[]): Promise<ExecResult>; | ||
logs(): Promise<Readable>; | ||
@@ -66,0 +61,0 @@ } |
import Dockerode from "dockerode"; | ||
import { BoundPorts } from "./bound-ports"; | ||
import { WaitStrategy } from "./wait-strategy/wait-strategy"; | ||
export declare const waitForContainer: (container: Dockerode.Container, waitStrategy: WaitStrategy, host: string, boundPorts: BoundPorts, startTime?: Date) => Promise<void>; | ||
export declare const waitForContainer: (container: Dockerode.Container, waitStrategy: WaitStrategy, boundPorts: BoundPorts, startTime?: Date) => Promise<void>; |
@@ -16,6 +16,6 @@ "use strict"; | ||
const remove_container_1 = require("./docker/functions/container/remove-container"); | ||
const waitForContainer = (container, waitStrategy, host, boundPorts, startTime) => __awaiter(void 0, void 0, void 0, function* () { | ||
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}`); | ||
try { | ||
yield waitStrategy.waitUntilReady(container, host, boundPorts, startTime); | ||
yield waitStrategy.waitUntilReady(container, boundPorts, startTime); | ||
logger_1.log.info(`Container is ready: ${container.id}`); | ||
@@ -22,0 +22,0 @@ } |
@@ -19,2 +19,3 @@ "use strict"; | ||
waitUntilReady(container) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -25,3 +26,3 @@ logger_1.log.debug(`Waiting for health check for ${container.id}`); | ||
throw new Error(`Health check not healthy after ${timeout}ms for ${container.id}`); | ||
}, this.startupTimeout); | ||
}, (_a = this.startupTimeout) !== null && _a !== void 0 ? _a : wait_strategy_1.DEFAULT_STARTUP_TIMEOUT); | ||
if (status !== "healthy") { | ||
@@ -28,0 +29,0 @@ throw new Error(`Health check failed: ${status} for ${container.id}`); |
@@ -1,2 +0,1 @@ | ||
import { PortCheck } from "../port-check"; | ||
import Dockerode from "dockerode"; | ||
@@ -6,6 +5,3 @@ import { BoundPorts } from "../bound-ports"; | ||
export declare class HostPortWaitStrategy extends AbstractWaitStrategy { | ||
private readonly hostPortCheck; | ||
private readonly internalPortCheck; | ||
constructor(hostPortCheck: PortCheck, internalPortCheck: PortCheck); | ||
waitUntilReady(container: Dockerode.Container, host: string, boundPorts: BoundPorts): Promise<void>; | ||
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts): Promise<void>; | ||
private waitForHostPorts; | ||
@@ -12,0 +8,0 @@ private waitForInternalPorts; |
@@ -13,21 +13,24 @@ "use strict"; | ||
exports.HostPortWaitStrategy = void 0; | ||
const port_check_1 = require("../port-check"); | ||
const logger_1 = require("../logger"); | ||
const retry_strategy_1 = require("../retry-strategy"); | ||
const wait_strategy_1 = require("./wait-strategy"); | ||
const docker_client_1 = require("../docker/docker-client"); | ||
class HostPortWaitStrategy extends wait_strategy_1.AbstractWaitStrategy { | ||
constructor(hostPortCheck, internalPortCheck) { | ||
super(); | ||
this.hostPortCheck = hostPortCheck; | ||
this.internalPortCheck = internalPortCheck; | ||
} | ||
waitUntilReady(container, host, boundPorts) { | ||
waitUntilReady(container, boundPorts) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield Promise.all([this.waitForHostPorts(container, boundPorts), this.waitForInternalPorts(container, boundPorts)]); | ||
const { dockerode, provider, host } = yield (0, docker_client_1.dockerClient)(); | ||
const hostPortCheck = new port_check_1.HostPortCheck(host); | ||
const internalPortCheck = new port_check_1.InternalPortCheck(dockerode, provider, container); | ||
yield Promise.all([ | ||
this.waitForHostPorts(hostPortCheck, container, boundPorts), | ||
this.waitForInternalPorts(internalPortCheck, container, boundPorts), | ||
]); | ||
}); | ||
} | ||
waitForHostPorts(container, boundPorts) { | ||
waitForHostPorts(portCheck, container, boundPorts) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
for (const [, hostPort] of boundPorts.iterator()) { | ||
logger_1.log.debug(`Waiting for host port ${hostPort} for ${container.id}`); | ||
yield this.waitForPort(container, hostPort, this.hostPortCheck); | ||
yield this.waitForPort(container, hostPort, portCheck); | ||
logger_1.log.debug(`Host port ${hostPort} ready for ${container.id}`); | ||
@@ -37,7 +40,7 @@ } | ||
} | ||
waitForInternalPorts(container, boundPorts) { | ||
waitForInternalPorts(portCheck, container, boundPorts) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
for (const [internalPort] of boundPorts.iterator()) { | ||
logger_1.log.debug(`Waiting for internal port ${internalPort} for ${container.id}`); | ||
yield this.waitForPort(container, internalPort, this.internalPortCheck); | ||
yield this.waitForPort(container, internalPort, portCheck); | ||
logger_1.log.debug(`Internal port ${internalPort} ready for ${container.id}`); | ||
@@ -48,7 +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, () => { | ||
const timeout = this.startupTimeout; | ||
throw new Error(`Port ${port} not bound after ${timeout}ms for ${container.id}`); | ||
}, this.startupTimeout); | ||
throw new Error(`Port ${port} not bound after ${startupTimeout}ms for ${container.id}`); | ||
}, startupTimeout); | ||
}); | ||
@@ -55,0 +59,0 @@ } |
@@ -25,4 +25,4 @@ import Dockerode from "dockerode"; | ||
allowInsecure(): HttpWaitStrategy; | ||
waitUntilReady(container: Dockerode.Container, host: string, boundPorts: BoundPorts): Promise<void>; | ||
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts): Promise<void>; | ||
private getAgent; | ||
} |
@@ -20,2 +20,3 @@ "use strict"; | ||
const https_1 = __importDefault(require("https")); | ||
const docker_client_1 = require("../docker/docker-client"); | ||
class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy { | ||
@@ -70,4 +71,7 @@ constructor(path, port) { | ||
} | ||
waitUntilReady(container, host, boundPorts) { | ||
waitUntilReady(container, boundPorts) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
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* () { | ||
@@ -83,3 +87,3 @@ try { | ||
} | ||
catch (_a) { | ||
catch (_b) { | ||
return undefined; | ||
@@ -104,4 +108,4 @@ } | ||
}), () => { | ||
throw new Error(`URL ${this.path} not accessible after ${this.startupTimeout}ms for ${container.id}`); | ||
}, this.startupTimeout); | ||
throw new Error(`URL ${this.path} not accessible after ${startupTimeout}ms for ${container.id}`); | ||
}, startupTimeout); | ||
}); | ||
@@ -108,0 +112,0 @@ } |
@@ -9,3 +9,3 @@ import Dockerode from "dockerode"; | ||
constructor(message: Log | RegExp, times: number); | ||
waitUntilReady(container: Dockerode.Container, host: string, boundPorts: BoundPorts, startTime?: Date): Promise<void>; | ||
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void>; | ||
} |
@@ -26,8 +26,9 @@ "use strict"; | ||
} | ||
waitUntilReady(container, host, boundPorts, startTime) { | ||
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; | ||
const stream = yield (0, container_logs_1.containerLogs)(container, { since: startTime }); | ||
return new Promise((resolve, reject) => { | ||
const startupTimeout = this.startupTimeout; | ||
const timeout = setTimeout(() => { | ||
@@ -34,0 +35,0 @@ const message = `Log message "${this.message}" not received after ${startupTimeout}ms for ${container.id}`; |
@@ -17,2 +17,3 @@ "use strict"; | ||
const exec_container_1 = require("../docker/functions/container/exec-container"); | ||
const docker_client_1 = require("../docker/docker-client"); | ||
class ShellWaitStrategy extends wait_strategy_1.AbstractWaitStrategy { | ||
@@ -24,11 +25,13 @@ constructor(command) { | ||
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}`); | ||
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* () { | ||
const { exitCode } = yield (0, exec_container_1.execContainer)(container, ["/bin/sh", "-c", this.command], false); | ||
const { exitCode } = yield (0, exec_container_1.execContainer)(dockerode, provider, container, ["/bin/sh", "-c", this.command], false); | ||
return exitCode; | ||
}), (exitCode) => exitCode === 0, () => { | ||
const timeout = this.startupTimeout; | ||
throw new Error(`Shell command not successful after ${timeout}ms for ${container.id}`); | ||
}, this.startupTimeout); | ||
throw new Error(`Shell command "${this.command}" not successful after ${startupTimeout}ms for ${container.id}`); | ||
}, startupTimeout); | ||
}); | ||
@@ -35,0 +38,0 @@ } |
@@ -18,5 +18,7 @@ "use strict"; | ||
waitUntilReady(container) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const { dockerode } = yield (0, docker_client_1.dockerClient)(); | ||
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 ${this.startupTimeout}ms for ${container.id}`), this.startupTimeout); | ||
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); | ||
if (startupStatus instanceof Error) { | ||
@@ -23,0 +25,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, host: string, boundPorts: BoundPorts, startTime?: Date): Promise<void>; | ||
waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void>; | ||
withStartupTimeout(startupTimeout: number): WaitStrategy; | ||
getStartupTimeout(): number | undefined; | ||
} | ||
export declare abstract class AbstractWaitStrategy implements WaitStrategy { | ||
protected startupTimeout: number; | ||
abstract waitUntilReady(container: Dockerode.Container, host: string, boundPorts: BoundPorts, startTime?: Date): Promise<void>; | ||
withStartupTimeout(startupTimeout: number): WaitStrategy; | ||
protected startupTimeout?: number; | ||
abstract waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void>; | ||
withStartupTimeout(startupTimeout: number): this; | ||
getStartupTimeout(): number | undefined; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.AbstractWaitStrategy = void 0; | ||
exports.AbstractWaitStrategy = exports.DEFAULT_STARTUP_TIMEOUT = void 0; | ||
exports.DEFAULT_STARTUP_TIMEOUT = 60000; | ||
class AbstractWaitStrategy { | ||
constructor() { | ||
this.startupTimeout = 60000; | ||
} | ||
withStartupTimeout(startupTimeout) { | ||
@@ -12,3 +10,6 @@ this.startupTimeout = startupTimeout; | ||
} | ||
getStartupTimeout() { | ||
return this.startupTimeout; | ||
} | ||
} | ||
exports.AbstractWaitStrategy = AbstractWaitStrategy; |
@@ -5,3 +5,6 @@ import { WaitStrategy } from "./wait-strategy"; | ||
import { ShellWaitStrategy } from "./shell-wait-strategy"; | ||
import { CompositeWaitStrategy } from "./composite-wait-strategy"; | ||
export declare class Wait { | ||
static forAll(waitStrategies: WaitStrategy[]): CompositeWaitStrategy; | ||
static forListeningPorts(): WaitStrategy; | ||
static forLogMessage(message: Log | RegExp, times?: number): WaitStrategy; | ||
@@ -8,0 +11,0 @@ static forHealthCheck(): WaitStrategy; |
@@ -8,3 +8,11 @@ "use strict"; | ||
const shell_wait_strategy_1 = require("./shell-wait-strategy"); | ||
const host_port_wait_strategy_1 = require("./host-port-wait-strategy"); | ||
const composite_wait_strategy_1 = require("./composite-wait-strategy"); | ||
class Wait { | ||
static forAll(waitStrategies) { | ||
return new composite_wait_strategy_1.CompositeWaitStrategy(waitStrategies); | ||
} | ||
static forListeningPorts() { | ||
return new host_port_wait_strategy_1.HostPortWaitStrategy(); | ||
} | ||
static forLogMessage(message, times = 1) { | ||
@@ -11,0 +19,0 @@ return new log_wait_strategy_1.LogWaitStrategy(message, times); |
{ | ||
"name": "testcontainers", | ||
"author": "Cristian Greco", | ||
"version": "9.5.0", | ||
"version": "9.6.0", | ||
"main": "dist/src/index", | ||
@@ -30,4 +30,4 @@ "types": "dist/src/index", | ||
"clean": "rimraf dist", | ||
"test": "cross-env DEBUG=testcontainers,testcontainers:exec,testcontainers:containers jest", | ||
"test:ci": "cross-env DEBUG=testcontainers,testcontainers:exec,testcontainers:containers jest --runInBand --coverage", | ||
"test": "cross-env DEBUG=testcontainers* jest", | ||
"test:ci": "npm run test -- --runInBand --coverage", | ||
"format": "prettier --write package.json \"src/**/*.ts\"", | ||
@@ -34,0 +34,0 @@ "lint": "eslint --fix package.json \"src/**/*.ts\"", |
289583
6054
198