UNPKG

node-docker-api

Version:
979 lines (897 loc) 27.1 kB
'use strict' import Modem = require('docker-modem') import { Image } from './image' import fs = require('fs') /** * Class representing container execution */ export class Exec { modem: Modem container: Container id: string data: Object = {} /** * Create an execution * @param {Modem} modem Modem to connect to the remote service * @param {Container} container Container that owns the execution (optional) * @param {string} id Id of the execution */ constructor (modem: Modem, container: Container, id: string) { this.modem = modem this.container = container this.id = id } /** * Create an exec instance in a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/create-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise return the new exec instance */ create (opts?: Object): Promise<Exec> { const call = { path: `/containers/${this.container.id}/exec?`, method: 'POST', options: opts, statusCodes: { 200: true, 201: true, 404: 'no such container', 409: 'container is paused', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, conf) => { if (err) return reject(err) const exec = new Exec(this.modem, this.container, conf.Id) exec.data = conf resolve(exec) }) }) } /** * Start an exec instance * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/exec-start * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise return the stream to the execution */ start (opts: any = {}): Promise<Object> { const call = { path: `/exec/${this.id}/start?`, method: 'POST', options: opts, isStream: true, hijack: opts.hijack, openStdin: opts.stdin, statusCodes: { 200: true, 404: 'no such exec instance', 409: 'container is paused' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, stream: Object) => { if (err) return reject(err) resolve(stream) }) }) } /** * Resize an exec instance * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/exec-resize * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise return the result */ resize (opts?: Object): Promise<{}> { const call = { path: `/exec/${this.id}/resize?`, method: 'POST', options: opts, statusCodes: { 201: true, 404: 'no such exec instance' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve() }) }) } /** * Get status of an exec instance * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/exec-inspect * The reason why this module isn't called inspect is because that interferes with the inspect utility of node. * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise return the exec instance */ status (opts?: Object): Promise<Exec> { const call = { path: `/exec/${this.id}/json?`, method: 'GET', options: opts, statusCodes: { 200: true, 404: 'no such exec instance', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, conf) => { if (err) return reject(err) const exec = new Exec(this.modem, this.container, conf.Id) exec.data = conf resolve(exec) }) }) } } /** * Class representing container execution management */ export class ExecManager { modem: Modem container: Container /** * Create an execution * @param {Modem} modem Modem to connect to the remote service * @param {Container} container Container that owns the execution (optional) * @param {string} id Id of the execution */ constructor (modem: Modem, container: Container) { this.modem = modem this.container = container } /** * Get a Exec Object * @param {id} string ID of the exec * @return {Exec} */ get (id: string): Exec { return new Exec(this.modem, this.container, id) } /** * Create an exec instance in a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/create-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise return the new exec instance */ create (opts?: Object): Promise<Exec> { const call = { path: `/containers/${this.container.id}/exec?`, method: 'POST', options: opts, statusCodes: { 200: true, 201: true, 404: 'no such container', 409: 'container is paused', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, conf) => { if (err) return reject(err) const exec = new Exec(this.modem, this.container, conf.Id) exec.data = conf resolve(exec) }) }) } } /** * Class representing container filesystem */ export class ContainerFs { modem: Modem container: Container /** * Create an container filesystem Object * @param {Modem} modem Modem to connect to the remote service * @param {Container} container Container that owns the filesystem (optional) */ constructor (modem: Modem, container: Container) { this.modem = modem this.container = container } /** * Get the info about the filesystem of the container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/retrieving-information-about-files-and-folders-in-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the info about the filesystem */ info (opts?: Object): Promise<String> { const call = { path: `/containers/${this.container.id}/archive?`, method: 'HEAD', isStream: true, options: opts, statusCodes: { 200: true, 404: 'bad request', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, info: string) => { if (err) return reject(err) resolve(info) }) }) } /** * Get a tar archive of a resource in the filesystem of a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/get-an-archive-of-a-filesystem-resource-in-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the result as a stream to the tar file */ get (opts: any = {}): Promise<Object> { const call = { path: `/containers/${this.container.id}/archive?path=${opts.path}&`, method: 'GET', isStream: true, options: opts, statusCodes: { 200: true, 400: 'bad request', 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, stream: Object) => { if (err) return reject(err) resolve(stream) }) }) } /** * Put an extracted tar archive in the filesystem of a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/extract-an-archive-of-files-or-folders-to-a-directory-in-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the result */ put (file: fs.ReadStream, opts?: Object): Promise<Object> { const call = { path: `/containers/${this.container.id}/archive?`, method: 'PUT', options: opts, isStream: true, file: file, statusCodes: { 200: true, 400: 'bad request', 403: 'permission denied', 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, res: Object) => { if (err) return reject(err) resolve(res) }) }) } } /** * Class representing a container */ export class Container { modem: Modem id: string fs: ContainerFs exec: ExecManager Warnings: Array<String> = [] data: Object = {} /** * Create an container Object * @param {Modem} modem Modem to connect to the remote service * @param {string} id Container id */ constructor (modem: Modem, id: string) { this.modem = modem this.id = id this.fs = new ContainerFs(this.modem, this) this.exec = new ExecManager(this.modem, this) } /** * Get low-level information on a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/inspect-a-container * The reason why this module isn't called inspect is because that interferes with the inspect utility of node. * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise return the container */ status (opts?: Object): Promise<Container> { const call = { path: `/containers/${this.id}/json?`, method: 'GET', options: opts, statusCodes: { 200: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, conf) => { if (err) return reject(err) const container = new Container(this.modem, this.id) container.data = conf resolve(container) }) }) } /** * Get list of processes (ps) inside a container. Not supported in Windows. * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/list-processes-running-inside-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise return the list of processes */ top (opts?: Object): Promise<Array<Object>> { const call = { path: `/containers/${this.id}/top?`, method: 'GET', options: opts, statusCodes: { 200: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, processes: Array<Object>) => { if (err) return reject(err) resolve(processes) }) }) } /** * Get stdout and stderr logs from a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/get-container-logs * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the concatenated logs */ logs (opts?: Object): Promise<Object> { const call = { path: `/containers/${this.id}/logs?`, method: 'GET', options: opts, isStream: true, statusCodes: { 101: true, 200: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, logs: Object) => { if (err) return reject(err) resolve(logs) }) }) } /** * Get changes on a container's filesystem * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/inspect-changes-on-a-container-s-filesystem * @return {Promise} Promise returning the changes */ changes (): Promise<Array<Object>> { const call = { path: `/containers/${this.id}/changes?`, method: 'GET', options: {}, statusCodes: { 200: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, changes: Array<Object>) => { if (err) return reject(err) resolve(changes) }) }) } /** * Export the content of a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/export-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the content of the tar file as a stream or as a string */ export (opts: any = {}): Promise<Object> { const call = { path: `/containers/${this.id}/export?`, method: 'GET', options: opts, isStream: !!opts.stream, statusCodes: { 200: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, tarStream: any) => { if (err) return reject(err) if (!opts.stream) return resolve(tarStream) const res = [] tarStream.on('data', (chunk) => { res.push(chunk.toString()) }) tarStream.on('end', () => { resolve(res.join('')) }) }) }) } /** * Get the stats of a container, either by a live stream or the current state * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/export-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the stats, in a stream or string */ stats (opts?: Object): Promise<Object> { const call = { path: `/containers/${this.id}/stats?`, method: 'GET', options: opts, isStream: true, statusCodes: { 200: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, stats: Object) => { if (err) return reject(err) resolve(stats) }) }) } /** * Resize the TTY for a container. You must restart the container to make the resize take effect. * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/resize-a-container-tty * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the response */ resize (opts?: Object): Promise<Object> { const call = { path: `/containers/${this.id}/resize?`, method: 'GET', options: opts, statusCodes: { 200: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, res: Object) => { if (err) return reject(err) resolve(res) }) }) } /** * Start a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/start-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the container */ start (opts: Object = {}): Promise<Container> { const call = { path: `/containers/${this.id}/start?`, method: 'POST', options: opts, statusCodes: { 204: true, 304: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve(this) }) }) } /** * Stop a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/stop-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the container */ stop (opts?: Object): Promise<Container> { const call = { path: `/containers/${this.id}/stop?`, method: 'POST', options: opts, statusCodes: { 204: true, 304: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve(this) }) }) } /** * Restart a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/restart-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the container */ restart (opts?: Object): Promise<Container> { const call = { path: `/containers/${this.id}/restart?`, method: 'POST', options: opts, statusCodes: { 204: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve(this) }) }) } /** * Kill a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/kill-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the container */ kill (opts?: Object): Promise<Container> { const call = { path: `/containers/${this.id}/kill?`, method: 'POST', options: opts, statusCodes: { 204: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve(this) }) }) } /** * Update configuration a container. * Docs says you can do it for more than one, but doesn't exaplin how, so let's leave it in only one * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/update-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the container */ update (opts?: Object): Promise<Container> { const call = { path: `/containers/${this.id}/update?`, method: 'POST', options: opts, statusCodes: { 200: true, 400: 'bad request', 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, warnings: Array<String>) => { const container = new Container(this.modem, this.id) container.Warnings = warnings if (err) return reject(err) resolve(container) }) }) } /** * Rename a container. * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/rename-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the container */ rename (opts: Object): Promise<Container> { const call = { path: `/containers/${this.id}/rename?`, method: 'POST', options: opts, statusCodes: { 204: true, 404: 'no such container', 409: 'name already taken', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve(this) }) }) } /** * Pause a container. * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/pause-a-container * @return {Promise} Promise returning the container */ pause (): Promise<Container> { const call = { path: `/containers/${this.id}/pause?`, method: 'POST', options: {}, statusCodes: { 204: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve(this) }) }) } /** * Unpause a container. * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/unpause-a-container * @return {Promise} Promise returning the container */ unpause (): Promise<Container> { const call = { path: `/containers/${this.id}/unpause?`, method: 'POST', options: {}, statusCodes: { 204: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve(this) }) }) } /** * Attach to a container. * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/attach-to-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the container */ attach (opts: any = {}): Promise<Array<Object>> { const call = { path: `/containers/${this.id}/attach?`, method: 'POST', isStream: true, openStdin: opts.stdin, options: opts, statusCodes: { 101: true, 200: true, 400: 'bad request', 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, stream) => { if (err) return reject(err) resolve([ stream, this ]) }) }) } /** * Attach to a container using websocket. * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/attach-to-a-container-websocket * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the stream and the container */ wsattach (opts?: Object): Promise<Array<Object>> { const call = { path: `/containers/${this.id}/attach/ws?`, method: 'GET', options: opts, statusCodes: { 200: true, 400: 'bad request', 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, stream) => { if (err) return reject(err) resolve([ stream, this ]) }) }) } /** * Block until a container stops, returning exit code * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/wait-a-container * @return {Promise} Promise returning the exit code */ wait (): Promise<Number> { const call = { path: `/containers/${this.id}/wait?`, method: 'POST', options: {}, statusCodes: { 200: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, code: number) => { if (err) return reject(err) resolve(code) }) }) } /** * Remove a container. * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/remove-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning nothing */ delete (opts?: Object): Promise<{}> { const call = { path: `/containers/${this.id}?`, method: 'DELETE', options: opts, statusCodes: { 204: true, 400: 'bad request', 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err) => { if (err) return reject(err) resolve() }) }) } /** * Commit container into an image * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/create-a-new-image-from-a-container-s-changes * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the new image */ commit (opts: any = {}): Promise<Image> { opts.container = this.id const call = { path: `/commit?`, method: 'POST', options: opts, statusCodes: { 201: true, 404: 'no such container', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, res) => { if (err) return reject(err) resolve(new Image(this.modem, res.Id.replace('sha256:', ''))) }) }) } } export default class { modem: Modem constructor(modem: Modem) { this.modem = modem } /** * Get a Container Object * @param {id} string ID of the container * @return {Container} */ get (id: string): Container { return new Container(this.modem, id) } /** * Get the list of containers * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/list-containers * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the result as a list of containers */ list (opts?: Object): Promise<Array<Container>> { const call = { path: '/containers/json?', method: 'GET', options: opts, statusCodes: { 200: true, 400: 'bad request', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, containers) => { if (err) return reject(err) resolve(containers.map((conf) => { const container = new Container(this.modem, conf.Id) container.data = conf return container })) }) }) } /** * Create a container * https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/create-a-container * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise return the new container */ create (opts?: Object): Promise<Container> { const call = { path: '/containers/create?', method: 'POST', options: opts, statusCodes: { 200: true, 201: true, 400: 'bad request', 404: 'no such image', 406: 'impossible to attach', 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, conf) => { if (err) return reject(err) const container = new Container(this.modem, conf.Id) container.data = conf resolve(container) }) }) } /** * Prune a container * https://docs.docker.com/engine/api/v1.25/#operation/ContainerPrune * @param {Object} opts Query params in the request (optional) * @return {Promise} Promise returning the container */ prune (opts?: Object): Promise<Object> { const call = { path: `/containers/prune`, method: 'POST', options: opts, statusCodes: { 200: true, 500: 'server error' } } return new Promise((resolve, reject) => { this.modem.dial(call, (err, res: Object) => { if (err) return reject(err) resolve(res) }) }) } }