@dgsh/docker-compose
Version:
Manage docker-compose from Node.js
319 lines (289 loc) • 8.49 kB
JavaScript
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