@apiclient.xyz/docker
Version:
Provides easy communication with Docker remote API from Node.js, with TypeScript support.
228 lines • 20.1 kB
JavaScript
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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