UNPKG

testcontainers

Version:

Testcontainers is a NodeJS library that supports tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container

287 lines 11.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DockerContainerClient = void 0; const stream_1 = require("stream"); const byline_1 = __importDefault(require("byline")); const common_1 = require("../../../common"); class DockerContainerClient { dockerode; constructor(dockerode) { this.dockerode = dockerode; } getById(id) { try { common_1.log.debug(`Getting container by ID...`, { containerId: id }); const container = this.dockerode.getContainer(id); common_1.log.debug(`Got container by ID`, { containerId: id }); return container; } catch (err) { common_1.log.error(`Failed to get container by ID: ${err}`, { containerId: id }); throw err; } } async fetchByLabel(labelName, labelValue) { try { common_1.log.debug(`Fetching container by label "${labelName}=${labelValue}"...`); const containers = await this.dockerode.listContainers({ limit: 1, filters: { status: ["running"], label: [`${labelName}=${labelValue}`], }, }); if (containers.length === 0) { common_1.log.debug(`No container found with label "${labelName}=${labelValue}"`); return undefined; } else { common_1.log.debug(`Fetched container by label "${labelName}=${labelValue}"`); return this.getById(containers[0].Id); } } catch (err) { common_1.log.error(`Failed to fetch container by label "${labelName}=${labelValue}": ${err}`); throw err; } } async fetchArchive(container, path) { try { common_1.log.debug(`Fetching archive from container...`, { containerId: container.id }); const archive = await container.getArchive({ path }); common_1.log.debug(`Fetched archive from container`, { containerId: container.id }); return archive; } catch (err) { common_1.log.error(`Failed to fetch archive from container: ${err}`, { containerId: container.id }); throw err; } } async putArchive(container, stream, path) { try { common_1.log.debug(`Putting archive to container...`, { containerId: container.id }); await (0, common_1.streamToString)(stream_1.Readable.from(await container.putArchive(stream, { path }))); common_1.log.debug(`Put archive to container`, { containerId: container.id }); } catch (err) { common_1.log.error(`Failed to put archive to container: ${err}`, { containerId: container.id }); throw err; } } async list() { try { common_1.log.debug(`Listing containers...`); const containers = await this.dockerode.listContainers(); common_1.log.debug(`Listed containers`); return containers; } catch (err) { common_1.log.error(`Failed to list containers: ${err}`); throw err; } } async create(opts) { try { common_1.log.debug(`Creating container for image "${opts.Image}"...`); const container = await this.dockerode.createContainer(opts); common_1.log.debug(`Created container for image "${opts.Image}"`, { containerId: container.id }); return container; } catch (err) { common_1.log.error(`Failed to create container for image "${opts.Image}": ${err}`); throw err; } } async start(container) { try { common_1.log.debug(`Starting container...`, { containerId: container.id }); await container.start(); common_1.log.debug(`Started container`, { containerId: container.id }); } catch (err) { common_1.log.error(`Failed to start container: ${err}`, { containerId: container.id }); throw err; } } async inspect(container) { try { common_1.log.debug(`Inspecting container...`, { containerId: container.id }); const inspectInfo = await container.inspect(); common_1.log.debug(`Inspected container`, { containerId: container.id }); return inspectInfo; } catch (err) { common_1.log.error(`Failed to inspect container: ${err}`, { containerId: container.id }); throw err; } } async stop(container, opts) { try { common_1.log.debug(`Stopping container...`, { containerId: container.id }); await container.stop({ t: opts?.timeout }); common_1.log.debug(`Stopped container`, { containerId: container.id }); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err) { if (err.statusCode === 304) { common_1.log.debug(`Container already stopped`, { containerId: container.id }); } else { common_1.log.error(`Failed to stop container: ${err}`, { containerId: container.id }); throw err; } } } async attach(container) { try { common_1.log.debug(`Attaching to container...`, { containerId: container.id }); const stream = (await container.attach({ stream: true, stdout: true, stderr: true, })); const demuxedStream = this.demuxStream(container.id, stream); common_1.log.debug(`Attached to container`, { containerId: container.id }); return demuxedStream; } catch (err) { common_1.log.error(`Failed to attach to container: ${err}`, { containerId: container.id }); throw err; } } async logs(container, opts) { try { common_1.log.debug(`Fetching container logs...`, { containerId: container.id }); const stream = (await container.logs({ follow: true, stdout: true, stderr: true, tail: opts?.tail ?? -1, since: opts?.since ?? 0, })); stream.socket.unref(); const demuxedStream = this.demuxStream(container.id, stream); common_1.log.debug(`Fetched container logs`, { containerId: container.id }); return demuxedStream; } catch (err) { common_1.log.error(`Failed to fetch container logs: ${err}`, { containerId: container.id }); throw err; } } async exec(container, command, opts) { const execOptions = { Cmd: command, AttachStdout: true, AttachStderr: true, }; if (opts?.env !== undefined) { execOptions.Env = Object.entries(opts.env).map(([key, value]) => `${key}=${value}`); } if (opts?.workingDir !== undefined) { execOptions.WorkingDir = opts.workingDir; } if (opts?.user !== undefined) { execOptions.User = opts.user; } const chunks = []; try { if (opts?.log) { common_1.log.debug(`Execing container with command "${command.join(" ")}"...`, { containerId: container.id }); } const exec = await container.exec(execOptions); const stream = await exec.start({ stdin: true, Detach: false, Tty: true }); if (opts?.log && common_1.execLog.enabled()) { (0, byline_1.default)(stream).on("data", (line) => common_1.execLog.trace(line, { containerId: container.id })); } await new Promise((res, rej) => { stream.on("data", (chunk) => chunks.push(chunk)); stream.on("end", res); stream.on("error", rej); }); stream.destroy(); const inspectResult = await exec.inspect(); const exitCode = inspectResult.ExitCode ?? -1; const output = chunks.join(""); if (opts?.log) { common_1.log.debug(`Execed container with command "${command.join(" ")}"...`, { containerId: container.id }); } return { output, exitCode }; } catch (err) { common_1.log.error(`Failed to exec container with command "${command.join(" ")}": ${err}: ${chunks.join("")}`, { containerId: container.id, }); throw err; } } async restart(container, opts) { try { common_1.log.debug(`Restarting container...`, { containerId: container.id }); await container.restart({ t: opts?.timeout }); common_1.log.debug(`Restarted container`, { containerId: container.id }); } catch (err) { common_1.log.error(`Failed to restart container: ${err}`, { containerId: container.id }); throw err; } } async remove(container, opts) { try { common_1.log.debug(`Removing container...`, { containerId: container.id }); await container.remove({ v: opts?.removeVolumes }); common_1.log.debug(`Removed container`, { containerId: container.id }); } catch (err) { common_1.log.error(`Failed to remove container: ${err}`, { containerId: container.id }); throw err; } } async events(container, eventNames) { common_1.log.debug(`Fetching event stream...`, { containerId: container.id }); const stream = (await this.dockerode.getEvents({ filters: { type: ["container"], container: [container.id], event: eventNames, }, })); common_1.log.debug(`Fetched event stream...`, { containerId: container.id }); return stream; } async demuxStream(containerId, stream) { try { common_1.log.debug(`Demuxing stream...`, { containerId }); const demuxedStream = new stream_1.PassThrough({ autoDestroy: true, encoding: "utf-8" }); this.dockerode.modem.demuxStream(stream, demuxedStream, demuxedStream); stream.on("end", () => demuxedStream.end()); demuxedStream.on("close", () => { if (!stream.destroyed) { stream.destroy(); } }); common_1.log.debug(`Demuxed stream`, { containerId }); return demuxedStream; } catch (err) { common_1.log.error(`Failed to demux stream: ${err}`); throw err; } } async connectToNetwork(container, network, networkAliases) { try { common_1.log.debug(`Connecting to network "${network.id}"...`, { containerId: container.id }); await network.connect({ Container: container.id, EndpointConfig: { Aliases: networkAliases } }); common_1.log.debug(`Connected to network "${network.id}"...`, { containerId: container.id }); } catch (err) { common_1.log.error(`Failed to connect to network "${network.id}": ${err}`, { containerId: container.id }); throw err; } } } exports.DockerContainerClient = DockerContainerClient; //# sourceMappingURL=docker-container-client.js.map