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
JavaScript
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
;