UNPKG

@dgsh/docker-compose

Version:

Manage docker-compose from Node.js

319 lines (289 loc) 8.49 kB
'use strict' const childProcess = require('child_process') const os = require('os') const path = require('path') const cleanArray = require('clean-array') /** * Converts supplied yml files to cli arguments * https://docs.docker.com/compose/reference/overview/#use--f-to-specify-name-and-path-of-one-or-more-compose-files * @param {?(string|string[])} config */ const configToArgs = (config) => { if (typeof config === 'undefined') { return [] } else if (typeof config === 'string') { return ['-f', config] } else if (config instanceof Array) { return config.reduce((args, item) => args.concat(['-f', item]), []) } throw new Error(`Invalid argument supplied: ${config}`) } /** * Executes docker-compose command with common options * @param {string} command * @param {string[]} args * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const execCompose = function(command, args, options) { let o = Object.assign({}, options, this.options) return new Promise((resolve, reject) => { const cwd = o.cwd || process.cwd() const env = o.env || null const composeArgs = [ `-p ${o.project_name || path.basename(cwd)}`, command ].concat(args, configToArgs(o.config || [])) let a = cleanArray(composeArgs.concat(o.args)) const childProc = childProcess.spawn('docker-compose', a, { cwd, env }) childProc.on('error', (err) => { reject(err) }) const result = { err: '', out: '' } childProc.stdout.on('data', (chunk) => { result.out += chunk.toString() }) childProc.stderr.on('data', (chunk) => { result.err += chunk.toString() }) childProc.on('close', () => { resolve(result) }) if (o.log) { childProc.stdout.pipe(process.stdout) childProc.stderr.pipe(process.stderr) } }) } /** * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const pullAll = function(options) { return this.execCompose('pull', [], options) } /** * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const upAll = function(options) { return this.execCompose('up', ['-d'], options) } /** * @param {string[]} services * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const upMany = function(services, options) { return this.execCompose('up', ['-d'].concat(services), options) } /** * @param {string} service * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const upOne = function(service, options) { return this.execCompose('up', ['-d', service], options) } /** * Containers command - return ids of running containers * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] * * @return {array} container IDs */ const containers = function(options) { return new Promise(async (resolve, reject) => { let res = await this.execCompose('ps', ['--quiet'], options) if (res.err) { return reject(new Error(res.err.trim())) } return resolve(res.out.split(os.EOL)) }) } /** * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const down = function(options) { return this.execCompose('down', [], options) } /** * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const stop = function(options) { return this.execCompose('stop', [], options) } /** * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const kill = function(options) { return this.execCompose('kill', [], options) } /** * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] */ const rm = function(options) { return this.execCompose('rm', ['-f'], options) } /** * Execute command in a running container * @param {string} contaier container name * @param {string} command command to execute * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] * * @return {object} std.out / std.err */ const exec = function(container, command, options) { const args = command.split(/\s+/) return this.execCompose('exec', ['-T', container].concat(args), options) } /** * Run command * @param {string} contaier container name * @param {string} command command to execute * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] * * @return {object} std.out / std.err */ const run = function(container, command, options) { const args = command.split(/\s+/) return this.execCompose('run', ['-T', container].concat(args), options) } /** * Build command * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] * * @return {object} std.out / std.err */ const buildAll = function(options) { return this.execCompose('build', [], options) } /** * Build command * @param {string[]} services list of service names * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] * * @return {object} std.out / std.err */ const buildMany = function(services, options) { return this.execCompose('build', services, options) } /** * Build command * @param {string} service service name * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] * * @return {object} std.out / std.err */ const buildOne = function(service, options) { return this.execCompose('build', [service], options) } /** * Port command * @param {string} service service name * @param {string} port container port number name * @param {string} protocol protocol, tcp by default * @param {object} options * @param {string} options.cwd * @param {boolean} [options.log] * @param {?(string|string[])} [options.config] * @param {?object} [options.env] * * @return {object} std.out / std.err */ const port = function(service, port, protocol, options) { protocol = protocol || 'tcp' return new Promise(async (resolve, reject) => { let res = await this.execCompose( 'port', [`--protocol=${protocol}`, service, port], options ) if (!res.out && !res.err) { return reject('Invalid port or port/protocol has not been exposed') } else if (res.err) { return reject(new Error(res.err.trim())) } return resolve(res.out.trim()) }) } function DockerCompose(options) { this.options = options || {} } DockerCompose.prototype.execCompose = execCompose DockerCompose.prototype.pullAll = pullAll DockerCompose.prototype.upAll = upAll DockerCompose.prototype.upMany = upMany DockerCompose.prototype.upOne = upOne DockerCompose.prototype.containers = containers DockerCompose.prototype.kill = kill DockerCompose.prototype.down = down DockerCompose.prototype.stop = stop DockerCompose.prototype.rm = rm DockerCompose.prototype.exec = exec DockerCompose.prototype.run = run DockerCompose.prototype.buildAll = buildAll DockerCompose.prototype.buildMany = buildMany DockerCompose.prototype.buildOne = buildOne DockerCompose.prototype.port = port module.exports = DockerCompose