-
Notifications
You must be signed in to change notification settings - Fork 30
/
command.js
125 lines (122 loc) · 4.28 KB
/
command.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
'use strict'
const _ = require('halcyon')
const command = {
/**
* @property {object} available args parsing instructions, matches config name
* with command argument
*/
args: {
expose: '-p',
volumes: '-v',
env: '-e',
hosts: '--add-host'
},
/**
* Parses host environment variables specified with ${VAR}
* @param {String} str The string to parse
* @returns {String}
*/
parseHostEnvVars: (str) => str.toString().replace(/\$\{([^}]+)\}/g, (i, match) => {
const [envVar, defaultValue = ''] = match.split(':-')
return process.env.hasOwnProperty(envVar) ? process.env[envVar] : defaultValue
}),
/**
* Reduces args array into flagged arguments list
* @param {string} type Name of the argument
* @param {array} args Array of values
* @returns {array}
*/
parseArgs: (type, args) => _.chain((item) => ([command.args[type], command.parseHostEnvVars(item)]), args),
/**
* Parses config object and returns container name. Will have bc_ prefix and
* InstanceID suffix if ephemeral, unaltered name for persisted containers
* @param {object} cfg Config object
* @returns {string}
*/
getName: (name, cfg) => {
if (cfg.persist) return name
return `bc_${name}_${global.instanceId}`
},
/**
* Parses config object and returns array of command arguments
* @param {object} cfg Config object of instance or service
* @returns {array} Command arguments
*/
getArgs: (cfg) => _.pipe([
_.keys,
_.filter((key) => !!command.args[key]),
_.chain(_.cond([
[(key) => !_.isType('Array', cfg[key]), (key) => {
throw new Error(`Config error: '${key}' should be an array`)
}],
[_.T, (key) => command.parseArgs(key, cfg[key])]
]))
])(cfg),
/**
* Returns array of execution commands
* @param {object} cfg Config object for instance
* @returns {string} Execution script
*/
getExec: (cfg) => {
const sh = '#!/bin/sh\nset -e;\n'
const before = cfg.before ? `${cfg.before}\n` : ''
const after = cfg.after ? `\n${cfg.after}` : ''
// Custom exec, just run native task
if (cfg.exec) return sh + before + cfg.exec + after
// Ensure tasks exist
if (!cfg.tasks) throw new Error('No tasks are defined')
// Ensure a task is passed
if (!cfg.run) throw new Error('No task has been specified')
// Use predefined task(s)
const run = _.pipe([
tasks => _.pick(tasks, cfg.tasks),
_.toPairs,
_.map(([name, command]) => {
if (!command) throw new Error(`Task '${name}' does not exist.`)
if (_.isType('object', command)) {
if (!command.cmd) throw new Error(`Task '${name}' has no command defined.`)
return command.cmd
}
return command
}),
_.join('\n')
])(cfg.run)
return sh + before + run + after
},
/**
* Returns array of link arguments
* @param {object} cfg Config object for the container
* @returns {array} Link arguments
*/
getLinks: (cfg) => _.chain(_.pipe([_.toPairs, _.head, ([key, value]) => {
return ['--link', `${command.getName(key, value)}:${key}`]
}]))(cfg.services || []),
/**
* Returns full command arguments array
* @param {object} cfg Config object for instance
* @param {string} name Container name
* @param {string} tmpdir Path to temp execution file
* @param {boolean} primary If this is primary, i.e. not a service container
* @returns {object|array} Arguments for docker command
*/
get: (cfg, name, tmpdir, primary = false) => {
if (!cfg.from) throw new Error('Missing \'from\' property in config or argument')
const cwd = process.cwd()
const serviceArgs = cfg.rmOnShutdown ? ['run', '-d', '--privileged'] : ['run', '-d', '--rm', '--privileged']
const workDir = cfg.workDir || cwd
/* istanbul ignore next */
const itFlags = process.stdout.isTTY ? '-it' : ''
let args = primary
? ['run', '--rm', itFlags, '-v', `${cwd}:${workDir}`, '-v', `${tmpdir}:${tmpdir}`, '-w', workDir, '--privileged']
: serviceArgs
args = args.concat(_.flatten([
command.getArgs(cfg),
command.getLinks(cfg),
['--name', command.getName(name, cfg)],
cfg.from,
primary ? ['sh', `${tmpdir}/binci.sh`] : []
]))
return primary ? { args, cmd: command.getExec(cfg) } : args
}
}
module.exports = command