@nasriya/orchestriq
Version:
A package to generate Docker files
947 lines (946 loc) • 88.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ContainerTemplate_1 = __importDefault(require("./templates/ContainerTemplate"));
const DockerContainer_1 = __importDefault(require("./DockerContainer"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const helpers_1 = __importDefault(require("../../utils/helpers"));
const stream_1 = require("stream");
const Tarball_1 = __importDefault(require("../../utils/Tarball"));
class ContainersManager {
#_containers = [];
#_socket;
constructor(socket) {
this.#_socket = socket;
}
/**
* Creates a new container template and adds it to the list of containers.
* @returns {ContainerTemplate} The newly created container template.
*/
newTemplate() {
const container = new ContainerTemplate_1.default(this.#_socket);
this.#_containers.push(container);
return container;
}
/**
* Lists all containers managed by this instance.
*
* @returns {Promise<DockerContainer[]>} A promise that resolves to an array of DockerContainer objects, representing all containers managed by this instance.
* @throws {Error} If the request fails to list containers.
*/
async list() {
try {
const containers = await this.#_socket.fetch('containers/json?all=true');
return containers.map(container => new DockerContainer_1.default(container, this));
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to list containers: ${error.message}`;
}
throw error;
}
}
/**
* Creates a new container and returns its ID.
* @param container The container to create. This can be either a `ContainerTemplate` instance or a `CreateContainerOptions` object with the following properties:
* @param verbose Whether to log the creation of the container.
* @returns A promise that resolves to an object with two properties: id and name. The id is the ID of the created container, and the name is the name of the container.
* @throws {Error} If the container could not be created.
*/
async create(container, verbose = false) {
const params = new URLSearchParams();
const request = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
returnJSON: false,
};
try {
if (typeof verbose !== 'boolean') {
throw new Error('The "verbose" argument (when provided) must be a boolean.');
}
if (container instanceof ContainerTemplate_1.default) {
const services = Object.values(container.services.list);
if (services.length === 0) {
throw new Error('The container must have at least one service.');
}
const createdContainers = [];
for (const service of services) {
if (service.image === undefined) {
throw new Error(`The service ${service.name} must have an image.`);
}
if (verbose) {
console.log(`Creating the ${service.name} service${service.container_name ? ` (${service.container_name})` : ''}...`);
}
const requestBody = {
Image: service.image,
};
if (service.userns_mode && service.userns_mode !== 'default') {
requestBody.HostConfig = requestBody.HostConfig || {};
requestBody.HostConfig.UsernsMode = service.userns_mode;
}
if (service.user) {
requestBody.User = service.user;
}
if (service.command) {
requestBody.Cmd = service.command;
}
if (service.entrypoint) {
requestBody.Entrypoint = service.entrypoint;
}
// Preparing the environment variables
{
const env = new Map();
// #1: Check the container's environment files
for (const envFilePath of container.env_files) {
if (path_1.default.isAbsolute(envFilePath)) {
const contentStr = await fs_1.default.promises.readFile(envFilePath, 'utf8');
const pairs = contentStr.split('\n').map(line => line.trim()).filter(line => line.length > 0);
for (const pair of pairs) {
const [key, value] = pair.split('=');
env.set(key, value);
}
}
else {
if (verbose) {
console.warn(`Skipping the container's environment file path "${envFilePath}" since it is not an absolute path.`);
}
}
}
// #2: Check the container's environment variables
for (const [key, value] of Object.entries(container.environment.list)) {
env.set(key, value);
}
// #3: Check the service's environment files
for (const envFilePath of service.env_files) {
if (path_1.default.isAbsolute(envFilePath)) {
const contentStr = await fs_1.default.promises.readFile(envFilePath, 'utf8');
const pairs = contentStr.split('\n').map(line => line.trim()).filter(line => line.length > 0);
for (const pair of pairs) {
const [key, value] = pair.split('=');
env.set(key, value);
}
}
else {
if (verbose) {
console.warn(`Skipping the service's environment file path "${envFilePath}" since it is not an absolute path.`);
}
}
}
// #4: Check the service's environment variables
for (const [key, value] of Object.entries(service.environment.list)) {
env.set(key, value);
}
// Add the environment variables to the request body
if (env.size > 0) {
requestBody.Env = Array.from(env).map(([key, value]) => `${key}=${value}`);
}
}
// Preparing the healthcheck
{
const healthcheck = service.healthcheck.toJSON();
if (healthcheck.test.length > 0) {
const obj = {
test: healthcheck.test,
interval: healthcheck.interval,
timeout: healthcheck.timeout,
retries: healthcheck.retries,
start_period: healthcheck.start_period,
disable: healthcheck.disable,
};
if (Object.values(obj).some(value => value !== undefined)) {
requestBody.Healthcheck = obj;
}
}
}
// Preparing the volumes
{
const anonymousVolumes = service.volumes.filter(volume => volume.type === 'anonymous');
const namedVolumes = service.volumes.filter(volume => volume.type === 'named');
const bindMounts = service.volumes.filter(volume => volume.type === 'bind');
if (anonymousVolumes.length > 0) {
requestBody.Volumes = {};
for (const volume of anonymousVolumes) {
requestBody.Volumes[volume.containerPath] = {};
}
}
if (namedVolumes.length > 0 || bindMounts.length > 0) {
if (!helpers_1.default.hasOwnProperty(requestBody, 'HostConfig')) {
requestBody.HostConfig = {};
}
if (!helpers_1.default.hasOwnProperty(requestBody.HostConfig, 'Binds')) {
requestBody.HostConfig.Binds = [];
}
for (const volume of namedVolumes) {
requestBody.HostConfig.Binds.push(`${volume.name}:${volume.containerPath}`);
}
for (const volume of bindMounts) {
requestBody.HostConfig.Binds.push(`${volume.hostPath}:${volume.containerPath}`);
}
}
}
// Prepare the service ports
{
if (service.ports.length > 0) {
requestBody.ExposedPorts = {};
requestBody.HostConfig = requestBody.HostConfig || {};
requestBody.HostConfig.PortBindings = {};
for (const port of service.ports) {
const containerPort = `${port.internal}/tcp`;
requestBody.ExposedPorts[containerPort] = {};
requestBody.HostConfig.PortBindings[containerPort] = [{ HostPort: `${port.external || port.internal}` }];
}
}
}
// Prepare the network
{
if (service.networks && service.networks.length > 0) {
// Use the first network as the primary one
requestBody.HostConfig = requestBody.HostConfig || {};
requestBody.HostConfig.NetworkMode = service.networks[0];
requestBody.NetworkingConfig = {
EndpointsConfig: {}
};
for (const network of service.networks) {
requestBody.NetworkingConfig.EndpointsConfig[network] = {
Aliases: [service.name] // or other custom values
};
}
}
else {
requestBody.HostConfig = requestBody.HostConfig || {};
requestBody.HostConfig.NetworkMode = service.network_mode;
}
}
// Prepare the restart policy
{
requestBody.HostConfig = requestBody.HostConfig || {};
const restart = service.restart;
const restartPolicy = {
Name: 'unless-stopped' // default
};
if (typeof restart === 'string') {
restartPolicy.Name = restart;
}
else if (typeof restart === 'object' && restart !== null) {
restartPolicy.Name = restart.policy;
if (restart.policy === 'on-failure') {
restartPolicy.MaximumRetryCount = restart.times ?? 5;
}
}
requestBody.HostConfig.RestartPolicy = restartPolicy;
}
// Preparing the request
const containerName = service.container_name || service.name;
params.set('name', containerName);
request.body = JSON.stringify(requestBody);
// The request to create the container
const response = await this.#_socket.fetch(`containers/create${params.size > 0 ? `?${params.toString()}` : ''}`, request);
const data = await response.json();
if (response.ok) {
createdContainers.push({ id: data.Id, name: containerName });
}
else {
throw new Error(data.message);
}
}
if (createdContainers.length === 0) {
throw new Error('No containers were created.');
}
if (createdContainers.length === 1) {
return createdContainers[0];
}
else {
return createdContainers;
}
}
else {
// Check if the container is a an object and validate it
if (!helpers_1.default.isObject(container)) {
throw new Error('The container must be an object.');
}
if (helpers_1.default.hasOwnProperty(container, 'name')) {
if (typeof container.name !== 'string') {
throw new Error('The container name must be a string.');
}
if (container.name.length === 0) {
throw new Error('The container name must be defined.');
}
params.set('name', container.name);
}
const requestBody = { ...container };
delete requestBody.name;
request.body = JSON.stringify(requestBody);
const response = await this.#_socket.fetch(`containers/create${params.size > 0 ? `?${params.toString()}` : ''}`, request);
const data = await response.json();
if (response.ok) {
return { id: data.Id, name: container.name || '' };
}
else {
throw new Error(data.message);
}
}
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to create the ${container.name ? `(${container.name})` : ''} container: ${error.message}`;
}
throw error;
}
}
/**
* Retrieves the details of a Docker container from the Docker daemon.
*
* @param {string} id - The ID of the container to inspect.
* @returns {Promise<DockerContainer | undefined>} A promise that resolves to a DockerContainer object containing the details of the container, or undefined if the container does not exist.
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async get(id) {
try {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
const response = await this.#_socket.fetch(`containers/${id}/json`, { method: 'GET', returnJSON: false });
const data = await response.json();
if (response.ok) {
return new DockerContainer_1.default(data, this);
}
if (response.status === 404) {
return;
}
if (response.status === 500) {
throw new Error(data.message);
}
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to get the details of the container${typeof id === 'string' && id.length < 0 ? ` with ID "${id}"` : ''}: ${error.message}`;
}
throw error;
}
}
/**
* Retrieves the top processes running in a Docker container from the Docker daemon.
*
* @param {string} id - The ID of the container to inspect.
* @returns {Promise<ContainerTopResponse | undefined>} A promise that resolves to a ContainerTopResponse object containing the top processes of the container, or undefined if the container does not exist.
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async top(id) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
const response = await this.#_socket.fetch(`containers/${id}/top`, { method: 'GET', returnJSON: false });
const data = await response.json();
if (response.ok) {
return data;
}
if (response.status === 404) {
return;
}
if (response.status === 500) {
throw new Error(data.message);
}
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to get the top processes of the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Retrieves the logs of a Docker container.
*
* @param {string} id - The ID of the container whose logs are to be retrieved.
* @param {ContainerLogsOptions} options - The options for retrieving the logs.
* @param {boolean} [options.stdout] - If true, includes standard output logs.
* @param {boolean} [options.stderr] - If true, includes standard error logs.
* @param {Date} [options.since] - Filters logs to entries since this date.
* @param {Date} [options.until] - Filters logs to entries until this date.
* @param {boolean} [options.follow] - If true, continuously streams new logs.
* @param {boolean} [options.timestamps] - If true, includes timestamps in logs.
* @param {number | string} [options.tail] - Limits the number of log entries returned.
* @returns {Promise<string | undefined>} A promise that resolves to the container logs as a string, or undefined if the container does not exist.
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async logs(id, options) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
if (!helpers_1.default.isObject(options)) {
throw new Error('The options argument must be an object.');
}
if (!('stdout' in options || 'stderr' in options)) {
throw new Error('At least one of the "stdout" or "stderr" options must be provided.');
}
const params = new URLSearchParams();
if (helpers_1.default.hasOwnProperty(options, 'stdout')) {
if (typeof options.stdout !== 'boolean') {
throw new Error('The "stdout" option must be a boolean.');
}
if (options.stdout) {
params.set('stdout', 'true');
}
}
if (helpers_1.default.hasOwnProperty(options, 'stderr')) {
if (typeof options.stderr !== 'boolean') {
throw new Error('The "stderr" option must be a boolean.');
}
if (options.stderr) {
params.set('stderr', 'true');
}
}
if (helpers_1.default.hasOwnProperty(options, 'since')) {
if (!(options.since instanceof Date)) {
throw new Error('The "since" option must be a Date object.');
}
params.set('since', (Math.floor(options.since.getTime() / 1000)).toString());
}
if (helpers_1.default.hasOwnProperty(options, 'until')) {
if (!(options.until instanceof Date)) {
throw new Error('The "until" option must be a Date object.');
}
params.set('until', (Math.floor(options.until.getTime() / 1000)).toString());
}
if (helpers_1.default.hasOwnProperty(options, 'follow')) {
if (typeof options.follow !== 'boolean') {
throw new Error('The "follow" option must be a boolean.');
}
if (options.follow) {
params.set('follow', 'true');
}
}
if (helpers_1.default.hasOwnProperty(options, 'timestamps')) {
if (typeof options.timestamps !== 'boolean') {
throw new Error('The "timestamps" option must be a boolean.');
}
if (options.timestamps) {
params.set('timestamps', 'true');
}
}
if (helpers_1.default.hasOwnProperty(options, 'tail')) {
if (!['number', 'string'].includes(typeof options.tail)) {
throw new Error('The "tail" option must be a number or a string.');
}
if (typeof options.tail === 'number') {
options.tail = String(options.tail);
}
params.set('tail', options.tail);
}
const response = await this.#_socket.fetch(`/containers/${id}/logs${params.size > 0 ? `?${params.toString()}` : ''}`, { method: 'GET', returnJSON: false });
// console.log(response)
const data = await response.text();
if (response.ok) {
return data || '';
}
if (response.status === 404) {
return undefined;
}
throw new Error(`${data.message || 'Empty response'} - Status: ${response.status}`);
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to get the logs of the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Retrieves the changes of a Docker container from the Docker daemon.
*
* @param {string} id - The ID of the container whose changes are to be retrieved.
* @returns {Promise<ContainerChangesResponse[] | undefined>} A promise that resolves to an array of ContainerChangesResponse objects containing the changes of the container, or undefined if the container does not exist.
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async changes(id) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
const response = await this.#_socket.fetch(`containers/${id}/changes`, { method: 'GET', returnJSON: false });
const data = await response.json();
if (response.ok) {
return data;
}
if (response.status === 404) {
return undefined;
}
throw new Error(`${data.message || 'Empty response'} - Status: ${response.status}`);
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to get the changes of the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Exports the filesystem of a Docker container as a tar archive.
*
* @param {string} id - The ID of the container to export.
* @param {string} dest - The destination path where the tar archive will be saved. Must be an absolute path ending with a `.tar` extension.
* @returns {Promise<string | undefined>} A promise that resolves to the path where the tar archive is saved, or undefined if the container does not exist.
* @throws {Error} Throws an error if the container ID or destination path is invalid, or if the request fails.
*/
async export(id, dest) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
if (typeof dest !== 'string') {
throw new Error('The destination path must be a string.');
}
if (dest.length === 0) {
throw new Error('The destination path cannot be empty.');
}
if (!path_1.default.isAbsolute(dest)) {
dest = path_1.default.resolve(dest);
}
const folder = path_1.default.dirname(dest);
const basename = path_1.default.basename(dest);
if (!basename.toLowerCase().endsWith('.tar')) {
throw new Error('The destination file must have a `.tar` extension.');
}
if (!fs_1.default.existsSync(folder)) {
await fs_1.default.promises.mkdir(folder, { recursive: true });
}
const outputPath = basename ? dest : path_1.default.join(dest, `${id}.tar`);
const response = await this.#_socket.fetch(`containers/${id}/export`, { method: 'GET', returnJSON: false });
if (response.ok) {
const stream = fs_1.default.createWriteStream(outputPath);
for await (const chunk of response.body) {
stream.write(chunk);
}
stream.end();
return outputPath;
}
const data = await response.json();
if (response.status === 404) {
return undefined;
}
throw new Error(`${data.message || 'Empty response'} - Status: ${response.status}`);
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to export the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Retrieves the stats of a Docker container from the Docker daemon.
*
* @param {string} id - The ID of the container whose stats are to be retrieved.
* @param {ContainerStatsOptions} [options] - The options for retrieving the stats.
* @param {boolean} [options.stream] - If true, returns a ReadableStream. If false, returns the latest stats snapshot as an object.
* @param {boolean} [options.oneShot] - If true, retrieves only one stats entry (no continuous streaming).
* @returns {Promise<ReadableStream | object | undefined>} A promise resolving to a ReadableStream (if streaming) or an object (if not).
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async stats(id, options = { stream: true, oneShot: false }) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
const params = new URLSearchParams();
if (helpers_1.default.hasOwnProperty(options, 'stream')) {
if (typeof options.stream !== 'boolean') {
throw new Error('The "stream" option must be a boolean.');
}
params.set('stream', options.stream.toString());
}
if (helpers_1.default.hasOwnProperty(options, 'oneShot')) {
if (typeof options.oneShot !== 'boolean') {
throw new Error('The "oneShot" option must be a boolean.');
}
params.set('one-shot', options.oneShot.toString());
}
const response = await this.#_socket.fetch(`containers/${id}/stats${params.size > 0 ? `?${params.toString()}` : ''}`, { method: 'GET', returnJSON: false });
if (response.ok) {
return options.stream ? response.body : await response.json();
}
const error = await response.json();
if (response.status === 404) {
return undefined;
}
throw new Error(`${error.message || 'Empty response'} - Status: ${response.status}`);
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to get the stats of the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Starts a stopped or paused container.
*
* @param {string} id - The ID of the container to start.
* @param {number} [timeout] - The time to wait for the container to start before returning an error. Defaults to no timeout.
* @returns {Promise<void>} A promise that resolves when the container is started successfully.
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async start(id, timeout) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
if (timeout !== undefined && typeof timeout !== 'number') {
throw new Error('The timeout must be a number.');
}
const params = new URLSearchParams();
if (typeof timeout === 'number') {
params.set('timeout', timeout.toString());
}
const response = await this.#_socket.fetch(`containers/${id}/start${params.size > 0 ? `?${params.toString()}` : ''}`, { method: 'POST', returnJSON: false });
if (response.status === 204 || response.status === 304) {
return;
}
const error = await response.json();
throw new Error(`${error.message || 'Empty response'} - Status: ${response.status}`);
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to start the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Stops a running container.
*
* @param {string} id - The ID of the container to stop.
* @param {number} [timeout] - The time to wait for the container to stop before returning an error. Defaults to no timeout.
* @returns {Promise<void>} A promise that resolves when the container is stopped successfully.
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async stop(id, timeout) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
if (timeout !== undefined && typeof timeout !== 'number') {
throw new Error('The timeout must be a number.');
}
const params = new URLSearchParams();
if (typeof timeout === 'number') {
params.set('timeout', timeout.toString());
}
const response = await this.#_socket.fetch(`containers/${id}/stop${params.size > 0 ? `?${params.toString()}` : ''}`, { method: 'POST', returnJSON: false });
if (response.status === 204 || response.status === 304) {
return;
}
const error = await response.json();
throw new Error(`${error.message || 'Empty response'} - Status: ${response.status}`);
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to stop the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Restarts a running container.
*
* @param {string} id - The ID of the container to restart.
* @param {string} [signal] - The signal to send to the container to restart it. Defaults to SIGTERM.
* @returns {Promise<void>} A promise that resolves when the container is restarted successfully.
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async restart(id, signal) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
if (signal !== undefined && typeof signal !== 'string') {
throw new Error('The signal must be a string.');
}
const params = new URLSearchParams();
if (typeof signal === 'string') {
params.set('signal', signal);
}
const response = await this.#_socket.fetch(`containers/${id}/restart${params.size > 0 ? `?${params.toString()}` : ''}`, { method: 'POST', returnJSON: false });
if (response.ok) {
return;
}
const error = await response.json();
throw new Error(`${error.message || 'Empty response'} - Status: ${response.status}`);
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to restart the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Sends a signal to a container, killing it. Note that when using a signal other than the default (SIGKILL), the container is not removed from the list of running containers until it exits. This is a limitation of the Docker API.
*
* @param {string} id - The ID of the container to kill.
* @param {string} [signal] - The signal to send to the container. Defaults to SIGKILL.
* @returns {Promise<void>} A promise that resolves when the container is killed successfully.
* @throws {Error} Throws an error if the request fails or the response is not OK.
*/
async kill(id, signal) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
if (signal !== undefined && typeof signal !== 'string') {
throw new Error('The signal must be a string.');
}
const params = new URLSearchParams();
if (typeof signal === 'string') {
params.set('signal', signal);
}
const response = await this.#_socket.fetch(`containers/${id}/kill${params.size > 0 ? `?${params.toString()}` : ''}`, { method: 'POST', returnJSON: false });
if (response.ok) {
return;
}
const error = await response.json();
throw new Error(`${error.message || 'Empty response'} - Status: ${response.status}`);
}
catch (error) {
if (error instanceof Error) {
error.message = `Unable to kill the container with ID "${id}": ${error.message}`;
}
throw error;
}
}
/**
* Updates a container with the given options. Note that the container must be stopped (not paused) for this function to work.
*
* @param {string} id - The ID of the container to update.
* @param {ContainerUpdateOptions} options - The options to update the container with.
* @returns {Promise<void>} A promise that resolves when the container is updated successfully.
* @throws {Error} Throws an error if the request fails or the response is not OK.
* @see https://docs.docker.com/engine/api/v1.41/#operation/ContainerUpdate
*/
async update(id, options) {
if (typeof id !== 'string') {
throw new Error('The container ID must be a string.');
}
if (id.length === 0) {
throw new Error('The container ID cannot be empty.');
}
try {
if (!helpers_1.default.isObject(options)) {
throw new TypeError('The options argument must be an object.');
}
if (!helpers_1.default.isNotEmptyObject(options)) {
throw new SyntaxError('The options object cannot be empty.');
}
const requestBody = {};
// validate the request options
{
if (helpers_1.default.hasOwnProperty(options, 'CpuShares')) {
if (typeof options.CpuShares !== 'number') {
throw new TypeError(`The "CpuShares" (when provided) option must be a number, instead got ${typeof options.CpuShares}`);
}
requestBody.CpuShares = options.CpuShares;
}
if (helpers_1.default.hasOwnProperty(options, 'CpuPeriod')) {
if (typeof options.CpuPeriod !== 'number') {
throw new TypeError(`The "CpuPeriod" (when provided) option must be a number, instead got ${typeof options.CpuPeriod}`);
}
requestBody.CpuPeriod = options.CpuPeriod;
}
if (helpers_1.default.hasOwnProperty(options, 'CpuQuota')) {
if (typeof options.CpuQuota !== 'number') {
throw new TypeError(`The "CpuQuota" (when provided) option must be a number, instead got ${typeof options.CpuQuota}`);
}
requestBody.CpuQuota = options.CpuQuota;
}
if (helpers_1.default.hasOwnProperty(options, 'CpuRealtimePeriod')) {
if (typeof options.CpuRealtimePeriod !== 'number') {
throw new TypeError(`The "CpuRealtimePeriod" (when provided) option must be a number, instead got ${typeof options.CpuRealtimePeriod}`);
}
requestBody.CpuRealtimePeriod = options.CpuRealtimePeriod;
}
if (helpers_1.default.hasOwnProperty(options, 'CpuRealtimeRuntime')) {
if (typeof options.CpuRealtimeRuntime !== 'number') {
throw new TypeError(`The "CpuRealtimeRuntime" (when provided) option must be a number, instead got ${typeof options.CpuRealtimeRuntime}`);
}
requestBody.CpuRealtimeRuntime = options.CpuRealtimeRuntime;
}
if (helpers_1.default.hasOwnProperty(options, 'NanoCpus')) {
if (typeof options.NanoCpus !== 'number') {
throw new TypeError(`The "NanoCpus" (when provided) option must be a number, instead got ${typeof options.NanoCpus}`);
}
requestBody.NanoCpus = options.NanoCpus;
}
if (helpers_1.default.hasOwnProperty(options, 'Memory')) {
if (typeof options.Memory !== 'number') {
throw new TypeError(`The "Memory" (when provided) option must be a number, instead got ${typeof options.Memory}`);
}
requestBody.Memory = options.Memory;
}
if (helpers_1.default.hasOwnProperty(options, 'MemoryReservation')) {
if (typeof options.MemoryReservation !== 'number') {
throw new TypeError(`The "MemoryReservation" (when provided) option must be a number, instead got ${typeof options.MemoryReservation}`);
}
requestBody.MemoryReservation = options.MemoryReservation;
}
if (helpers_1.default.hasOwnProperty(options, 'MemorySwap')) {
if (typeof options.MemorySwap !== 'number') {
throw new TypeError(`The "MemorySwap" (when provided) option must be a number, instead got ${typeof options.MemorySwap}`);
}
requestBody.MemorySwap = options.MemorySwap;
}
if (helpers_1.default.hasOwnProperty(options, 'KernelMemory')) {
if (typeof options.KernelMemory !== 'number') {
throw new TypeError(`The "KernelMemory" (when provided) option must be a number, instead got ${typeof options.KernelMemory}`);
}
requestBody.KernelMemory = options.KernelMemory;
}
if (helpers_1.default.hasOwnProperty(options, 'PidsLimit')) {
if (typeof options.PidsLimit !== 'number') {
throw new TypeError(`The "PidsLimit" (when provided) option must be a number, instead got ${typeof options.PidsLimit}`);
}
requestBody.PidsLimit = options.PidsLimit;
}
if (helpers_1.default.hasOwnProperty(options, 'BlkioWeight')) {
if (typeof options.BlkioWeight !== 'number') {
throw new TypeError(`The "BlkioWeight" (when provided) option must be a number, instead got ${typeof options.BlkioWeight}`);
}
requestBody.BlkioWeight = options.BlkioWeight;
}
if (helpers_1.default.hasOwnProperty(options, 'BlkioWeightDevice')) {
if (!Array.isArray(options.BlkioWeightDevice)) {
throw new TypeError(`The "BlkioWeightDevice" (when provided) option must be an array, instead got ${typeof options.BlkioWeightDevice}`);
}
for (const item of options.BlkioWeightDevice) {
if (!helpers_1.default.isObject(item)) {
throw new TypeError(`The "BlkioWeightDevice" array must contain objects, instead one of them was ${typeof item}`);
}
if (!('Path' in item)) {
throw new SyntaxError(`The "BlkioWeightDevice" object must have a "Path" property.`);
}
if (!('Weight' in item)) {
throw new SyntaxError(`The "BlkioWeightDevice" object must have a "Weight" property.`);
}
if (typeof item.Path !== 'string') {
throw new TypeError(`The "Path" property of the "BlkioWeightDevice" object must be a string, instead got ${typeof item.Path}`);
}
if (typeof item.Weight !== 'number') {
throw new TypeError(`The "Weight" property of the "BlkioWeightDevice" object must be a number, instead got ${typeof item.Weight}`);
}
}
requestBody.BlkioWeightDevice = options.BlkioWeightDevice;
}
if (helpers_1.default.hasOwnProperty(options, 'BlkioDeviceReadBps')) {
if (!Array.isArray(options.BlkioDeviceReadBps)) {
throw new TypeError(`The "BlkioDeviceReadBps" (when provided) option must be an array, instead got ${typeof options.BlkioDeviceReadBps}`);
}
for (const item of options.BlkioDeviceReadBps) {
if (!helpers_1.default.isObject(item)) {
throw new TypeError(`The "BlkioDeviceReadBps" array must contain objects, instead one of them was ${typeof item}`);
}
if (!('Path' in item)) {
throw new SyntaxError(`The "BlkioDeviceReadBps" object must have a "Path" property.`);
}
if (!('Rate' in item)) {
throw new SyntaxError(`The "BlkioDeviceReadBps" object must have a "Rate" property.`);
}
if (typeof item.Path !== 'string') {
throw new TypeError(`The "Path" property of the "BlkioDeviceReadBps" object must be a string, instead got ${typeof item.Path}`);
}
if (typeof item.Rate !== 'number') {
throw new TypeError(`The "Rate" property of the "BlkioDeviceReadBps" object must be a number, instead got ${typeof item.Rate}`);
}
}
requestBody.BlkioDeviceReadBps = options.BlkioDeviceReadBps;
}
if (helpers_1.default.hasOwnProperty(options, 'BlkioDeviceWriteBps')) {
if (!Array.isArray(options.BlkioDeviceWriteBps)) {
throw new TypeError(`The "BlkioDeviceWriteBps" (when provided) option must be an array, instead got ${typeof options.BlkioDeviceWriteBps}`);
}
for (const item of options.BlkioDeviceWriteBps) {
if (!helpers_1.default.isObject(item)) {
throw new TypeError(`The "BlkioDeviceWriteBps" array must contain objects, instead one of them was ${typeof item}`);
}
if (!('Path' in item)) {
throw new SyntaxError(`The "BlkioDeviceWriteBps" object must have a "Path" property.`);
}
if (!('Rate' in item)) {
throw new SyntaxError(`The "BlkioDeviceWriteBps" object must have a "Rate" property.`);
}
if (typeof item.Path !== 'string') {
throw new TypeError(`The "Path" property of the "BlkioDeviceWriteBps" object must be a string, instead got ${typeof item.Path}`);
}
if (typeof item.Rate !== 'number') {
throw new TypeError(`The "Rate" property of the "BlkioDeviceWriteBps" object must be a number, instead got ${typeof item.Rate}`);
}
}
requestBody.BlkioDeviceWriteBps = options.BlkioDeviceWriteBps;
}
if (helpers_1.default.hasOwnProperty(options, 'BlkioDeviceReadIOps')) {
if (!Array.isArray(options.BlkioDeviceReadIOps)) {
throw new TypeError(`The "BlkioDeviceReadIOps" (when provided) option must be an array, instead got ${typeof options.BlkioDeviceReadIOps}`);
}
for (const item of options.BlkioDeviceReadIOps) {
if (!helpers_1.default.isObject(item)) {
throw new TypeError(`The "BlkioDeviceReadIOps" array must contain objects, instead one of them was ${typeof item}`);
}
if (!('Path' in item)) {
throw new SyntaxError(`The "BlkioDeviceReadIOps" object must have a "Path" property.`);
}
if (!('Rate' in item)) {
throw new SyntaxError(`The "BlkioDeviceReadIOps" object must have a "Rate" property.`);
}
if (typeof item.Path !== 'string') {
throw new TypeError(`The "Path" property of the "BlkioDeviceReadIOps" object must be a string, instead got ${typeof item.Path}`);
}
if (typeof item.Rate !== 'number') {
throw new TypeError(`The "Rate" property of the "BlkioDeviceReadIOps" object must be a number, instead got ${typeof item.Rate}`);
}
}
requestBody.BlkioDeviceReadIOps = options.BlkioDeviceReadIOps;
}
if (helpers_1.default.hasOwnProperty(options, 'BlkioDeviceWriteIOps')) {
if (!Array.isArray(options.BlkioDeviceWriteIOps)) {
throw new TypeError(`The "BlkioDeviceWriteIOps" (when provided) option must be an array, instead got ${typeof options.BlkioDeviceWriteIOps}`);
}
for (const item of options.BlkioDeviceWriteIOps) {
if (!helpers_1.default.isObject(item)) {
throw new TypeError(`The "BlkioDeviceWriteIOps" array must contain objects, instead one of them was ${typeof item}`);
}
if (!('Path' in item)) {
throw new SyntaxError(`The "BlkioDeviceWriteIOps" object must have a "Path" property.`);
}