UNPKG

@apiclient.xyz/docker

Version:

Provides easy communication with Docker remote API from Node.js, with TypeScript support.

228 lines 20.1 kB
import * as plugins from './plugins.js'; import * as interfaces from './interfaces/index.js'; import { DockerHost } from './classes.host.js'; import { DockerResource } from './classes.base.js'; import { logger } from './logger.js'; export class DockerContainer extends DockerResource { // STATIC (Internal - prefixed with _ to indicate internal use) /** * Internal: Get all containers * Public API: Use dockerHost.listContainers() instead */ static async _list(dockerHostArg) { const result = []; const response = await dockerHostArg.request('GET', '/containers/json'); // TODO: Think about getting the config by inspecting the container for (const containerResult of response.body) { result.push(new DockerContainer(dockerHostArg, containerResult)); } return result; } /** * Internal: Get a container by ID * Public API: Use dockerHost.getContainerById(id) instead * Returns undefined if container does not exist */ static async _fromId(dockerHostArg, containerId) { const containers = await this._list(dockerHostArg); return containers.find((container) => container.Id === containerId); } /** * Internal: Create a container * Public API: Use dockerHost.createContainer(descriptor) instead */ static async _create(dockerHost, containerCreationDescriptor) { // Check for unique hostname const existingContainers = await DockerContainer._list(dockerHost); const sameHostNameContainer = existingContainers.find((container) => { // TODO implement HostName Detection; return false; }); const response = await dockerHost.request('POST', '/containers/create', { Hostname: containerCreationDescriptor.Hostname, Domainname: containerCreationDescriptor.Domainname, User: 'root', }); if (response.statusCode < 300) { logger.log('info', 'Container created successfully'); // Return the created container instance const container = await DockerContainer._fromId(dockerHost, response.body.Id); if (!container) { throw new Error('Container was created but could not be retrieved'); } return container; } else { logger.log('error', 'There has been a problem when creating the container'); throw new Error(`Failed to create container: ${response.statusCode}`); } } constructor(dockerHostArg, dockerContainerObjectArg) { super(dockerHostArg); Object.keys(dockerContainerObjectArg).forEach((keyArg) => { this[keyArg] = dockerContainerObjectArg[keyArg]; }); } // INSTANCE METHODS /** * Refreshes this container's state from the Docker daemon */ async refresh() { const updated = await DockerContainer._fromId(this.dockerHost, this.Id); Object.assign(this, updated); } /** * Inspects the container and returns detailed information */ async inspect() { const response = await this.dockerHost.request('GET', `/containers/${this.Id}/json`); // Update instance with fresh data Object.assign(this, response.body); return response.body; } /** * Starts the container */ async start() { const response = await this.dockerHost.request('POST', `/containers/${this.Id}/start`); if (response.statusCode >= 300) { throw new Error(`Failed to start container: ${response.statusCode}`); } await this.refresh(); } /** * Stops the container * @param options Options for stopping (e.g., timeout in seconds) */ async stop(options) { const queryParams = options?.t ? `?t=${options.t}` : ''; const response = await this.dockerHost.request('POST', `/containers/${this.Id}/stop${queryParams}`); if (response.statusCode >= 300) { throw new Error(`Failed to stop container: ${response.statusCode}`); } await this.refresh(); } /** * Removes the container * @param options Options for removal (force, remove volumes, remove link) */ async remove(options) { const queryParams = new URLSearchParams(); if (options?.force) queryParams.append('force', '1'); if (options?.v) queryParams.append('v', '1'); if (options?.link) queryParams.append('link', '1'); const queryString = queryParams.toString(); const response = await this.dockerHost.request('DELETE', `/containers/${this.Id}${queryString ? '?' + queryString : ''}`); if (response.statusCode >= 300) { throw new Error(`Failed to remove container: ${response.statusCode}`); } } /** * Gets container logs * @param options Log options (stdout, stderr, timestamps, tail, since, follow) */ async logs(options) { const queryParams = new URLSearchParams(); queryParams.append('stdout', options?.stdout !== false ? '1' : '0'); queryParams.append('stderr', options?.stderr !== false ? '1' : '0'); if (options?.timestamps) queryParams.append('timestamps', '1'); if (options?.tail) queryParams.append('tail', options.tail.toString()); if (options?.since) queryParams.append('since', options.since.toString()); if (options?.follow) queryParams.append('follow', '1'); const response = await this.dockerHost.request('GET', `/containers/${this.Id}/logs?${queryParams.toString()}`); // Docker returns logs with a special format (8 bytes header + payload) // For simplicity, we'll return the raw body as string return response.body.toString(); } /** * Gets container stats * @param options Stats options (stream for continuous stats) */ async stats(options) { const queryParams = new URLSearchParams(); queryParams.append('stream', options?.stream ? '1' : '0'); const response = await this.dockerHost.request('GET', `/containers/${this.Id}/stats?${queryParams.toString()}`); return response.body; } /** * Streams container logs continuously (follow mode) * Returns a readable stream that emits log data as it's produced * @param options Log streaming options */ async streamLogs(options) { const queryParams = new URLSearchParams(); queryParams.append('stdout', options?.stdout !== false ? '1' : '0'); queryParams.append('stderr', options?.stderr !== false ? '1' : '0'); queryParams.append('follow', '1'); // Always follow for streaming if (options?.timestamps) queryParams.append('timestamps', '1'); if (options?.tail) queryParams.append('tail', options.tail.toString()); if (options?.since) queryParams.append('since', options.since.toString()); const response = await this.dockerHost.requestStreaming('GET', `/containers/${this.Id}/logs?${queryParams.toString()}`); // requestStreaming returns Node.js stream return response; } /** * Attaches to the container's main process (PID 1) * Returns a duplex stream for bidirectional communication * @param options Attach options */ async attach(options) { const queryParams = new URLSearchParams(); queryParams.append('stream', options?.stream !== false ? '1' : '0'); queryParams.append('stdin', options?.stdin ? '1' : '0'); queryParams.append('stdout', options?.stdout !== false ? '1' : '0'); queryParams.append('stderr', options?.stderr !== false ? '1' : '0'); if (options?.logs) queryParams.append('logs', '1'); const response = await this.dockerHost.requestHijackedStreaming('POST', `/containers/${this.Id}/attach?${queryParams.toString()}`, {}); return { stream: response.stream, close: response.close, }; } /** * Executes a command in the container * Returns a duplex stream for command interaction * @param command Command to execute (string or array of strings) * @param options Exec options */ async exec(command, options) { // Step 1: Create exec instance const createResponse = await this.dockerHost.request('POST', `/containers/${this.Id}/exec`, { Cmd: typeof command === 'string' ? ['/bin/sh', '-c', command] : command, AttachStdin: options?.attachStdin !== false, AttachStdout: options?.attachStdout !== false, AttachStderr: options?.attachStderr !== false, Tty: options?.tty || false, Env: options?.env || [], WorkingDir: options?.workingDir, User: options?.user, }); const execId = createResponse.body.Id; // Step 2: Start exec instance with streaming response const startResponse = await this.dockerHost.requestHijackedStreaming('POST', `/exec/${execId}/start`, { Detach: false, Tty: options?.tty || false, }); const inspect = async () => { const inspectResponse = await this.dockerHost.request('GET', `/exec/${execId}/json`); return inspectResponse.body; }; return { stream: startResponse.stream, close: startResponse.close, inspect, }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5jb250YWluZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLmNvbnRhaW5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssVUFBVSxNQUFNLHVCQUF1QixDQUFDO0FBRXBELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDbkQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVyQyxNQUFNLE9BQU8sZUFBZ0IsU0FBUSxjQUFjO0lBQ2pELCtEQUErRDtJQUUvRDs7O09BR0c7SUFDSSxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FDdkIsYUFBeUI7UUFFekIsTUFBTSxNQUFNLEdBQXNCLEVBQUUsQ0FBQztRQUNyQyxNQUFNLFFBQVEsR0FBRyxNQUFNLGFBQWEsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFFeEUsbUVBQW1FO1FBQ25FLEtBQUssTUFBTSxlQUFlLElBQUksUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzVDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxlQUFlLENBQUMsYUFBYSxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUM7UUFDbkUsQ0FBQztRQUNELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQ3pCLGFBQXlCLEVBQ3pCLFdBQW1CO1FBRW5CLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUNuRCxPQUFPLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxF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