mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-19 13:47:54 +00:00
Improve the docker compose API
- Validate the options in the YAML file - Define bind mounts for each service as in Resin apps - Keep the modified compose file inside the supervisor's /data folder - Fix error reporting in the first stage of "up"
This commit is contained in:
parent
b97fe634d5
commit
54288f036a
@ -24,6 +24,7 @@
|
||||
"lodash": "^3.0.0",
|
||||
"log-timestamp": "^0.1.2",
|
||||
"mixpanel": "0.0.20",
|
||||
"mkdirp": "^0.5.1",
|
||||
"network-checker": "~0.0.5",
|
||||
"pinejs-client": "^1.7.1",
|
||||
"pubnub": "^3.7.13",
|
||||
|
@ -184,7 +184,7 @@ module.exports = (application) ->
|
||||
utils.getKnexApp(appId)
|
||||
.then (app) ->
|
||||
res.status(200)
|
||||
compose.up(application.composePath(appId), onStatus)
|
||||
compose.up(appId, onStatus)
|
||||
.catch (err) ->
|
||||
console.log('Error on compose up:', err, err.stack)
|
||||
.finally ->
|
||||
@ -202,7 +202,7 @@ module.exports = (application) ->
|
||||
utils.getKnexApp(appId)
|
||||
.then (app) ->
|
||||
res.status(200)
|
||||
compose.down(application.composePath(appId), onStatus)
|
||||
compose.down(appId, onStatus)
|
||||
.catch (err) ->
|
||||
console.log('Error on compose down:', err, err.stack)
|
||||
.finally ->
|
||||
|
@ -309,9 +309,6 @@ killmePath = (app) ->
|
||||
appId = app.appId ? app
|
||||
return "/mnt/root#{config.dataPath}/#{appId}/resin-kill-me"
|
||||
|
||||
application.composePath = (appId) ->
|
||||
return "/mnt/root#{config.dataPath}/#{appId}/docker-compose.yml"
|
||||
|
||||
# At boot, all apps should be unlocked *before* start to prevent a deadlock
|
||||
application.unlockAndStart = unlockAndStart = (app) ->
|
||||
lockFile.unlockAsync(lockPath(app))
|
||||
|
@ -4,14 +4,23 @@ _ = require 'lodash'
|
||||
dockerUtils = require './docker-utils'
|
||||
{ docker } = dockerUtils
|
||||
fs = Promise.promisifyAll(require('fs'))
|
||||
spawn = require('child_process').spawn
|
||||
{ spawn, execAsync } = Promise.promisifyAll(require('child_process'))
|
||||
mkdirp = Promise.promisify(require('mkdirp'))
|
||||
path = require 'path'
|
||||
utils = require './utils'
|
||||
|
||||
runComposeCommand = (composeArgs, path, onStatus) ->
|
||||
onStatus ?= console.log.bind(console)
|
||||
reportStatus = (status) ->
|
||||
try onStatus(status)
|
||||
composePathSrc = (appId) ->
|
||||
return "/mnt/root#{config.dataPath}/#{appId}/docker-compose.yml"
|
||||
|
||||
composePathDst = (appId) ->
|
||||
return "/mnt/root#{config.dataPath}/resin-supervisor/compose/#{appId}/docker-compose.yml"
|
||||
|
||||
composeDataPath = (appId, serviceName) ->
|
||||
return "compose/#{appId}/#{serviceName}"
|
||||
|
||||
runComposeCommand = (composeArgs, appId, reportStatus) ->
|
||||
new Promise (resolve, reject) ->
|
||||
child = spawn('docker-compose', ['-f', path].concat(composeArgs), stdio: 'pipe')
|
||||
child = spawn('docker-compose', ['-f', composePathDst(appId)].concat(composeArgs), stdio: 'pipe')
|
||||
.on 'error', reject
|
||||
.on 'exit', (code) ->
|
||||
return reject(new Error("docker-compose exited with code #{code}")) if code isnt 0
|
||||
@ -20,18 +29,30 @@ runComposeCommand = (composeArgs, path, onStatus) ->
|
||||
reportStatus(status: '' + data)
|
||||
child.stderr.on 'data', (data) ->
|
||||
reportStatus(status: '' + data)
|
||||
.catch (err) ->
|
||||
msg = err?.message or err
|
||||
reportStatus(error: msg)
|
||||
throw err
|
||||
|
||||
writeComposeFile = (composeSpec, dstPath) ->
|
||||
mkdirp(path.dirname(dstPath))
|
||||
.then ->
|
||||
YAML.stringify(composeSpec)
|
||||
.then (yml) ->
|
||||
fs.writeFileAsync(dstPath, yml)
|
||||
.then ->
|
||||
execAsync('sync')
|
||||
|
||||
validateServiceOptions = (service) ->
|
||||
Promise.try ->
|
||||
options = _.keys(service)
|
||||
_.each options, (option) ->
|
||||
throw new Error("Using #{option} is not allowed.") if !_.includes(utils.validComposeOptions, option)
|
||||
|
||||
# Runs docker-compose up using the compose YAML at "path".
|
||||
# Reports status and errors in JSON to the onStatus function.
|
||||
exports.up = (path, onStatus) ->
|
||||
# Copies the compose file from srcPath to dstPath adding default volumes
|
||||
exports.up = (appId, onStatus) ->
|
||||
onStatus ?= console.log.bind(console)
|
||||
reportStatus = (status) ->
|
||||
try onStatus(status)
|
||||
fs.readFileAsync(path)
|
||||
fs.readFileAsync(composePathSrc(appId))
|
||||
.then (data) ->
|
||||
YAML.parse(data.toString())
|
||||
.then (composeSpec) ->
|
||||
@ -46,9 +67,27 @@ exports.up = (path, onStatus) ->
|
||||
docker.getImage(service.image).inspectAsync()
|
||||
.catch ->
|
||||
dockerUtils.pullAndProtectImage(service.image, reportStatus)
|
||||
.then ->
|
||||
validateServiceOptions(service)
|
||||
.then ->
|
||||
services[serviceName].volumes = utils.defaultBinds(composeDataPath(appId, serviceName))
|
||||
.then ->
|
||||
writeComposeFile(composeSpec, dstPath)
|
||||
.then ->
|
||||
runComposeCommand(['up', '-d'], path, onStatus)
|
||||
runComposeCommand(['up', '-d'], appId, reportStatus)
|
||||
.catch (err) ->
|
||||
msg = err?.message or err
|
||||
reportStatus(error: msg)
|
||||
throw err
|
||||
|
||||
# Runs docker-compose down using the compose YAML at "path".
|
||||
# Reports status and errors in JSON to the onStatus function.
|
||||
exports.down = _.partial(runComposeCommand, 'down')
|
||||
exports.down = (appId, onStatus)
|
||||
onStatus ?= console.log.bind(console)
|
||||
reportStatus = (status) ->
|
||||
try onStatus(status)
|
||||
runComposeCommand([ 'down' ], appId, reportStatus)
|
||||
.catch (err) ->
|
||||
msg = err?.message or err
|
||||
reportStatus(error: msg)
|
||||
throw err
|
||||
|
@ -223,3 +223,47 @@ exports.defaultBinds = (dataPath) ->
|
||||
'/etc/resolv.conf:/etc/resolv.conf:rw'
|
||||
'/var/lib/connman:/host/var/lib/connman'
|
||||
]
|
||||
|
||||
exports.validComposeOptions = [
|
||||
'command'
|
||||
'entrypoint'
|
||||
'env_file'
|
||||
'environment'
|
||||
'expose'
|
||||
'image'
|
||||
'labels'
|
||||
'ports'
|
||||
'stop_signal'
|
||||
'volumes'
|
||||
'user'
|
||||
'working_dir'
|
||||
'cap_add'
|
||||
'cap_drop'
|
||||
'devices'
|
||||
'dns'
|
||||
'dns_search'
|
||||
'tmpfs'
|
||||
'extra_hosts'
|
||||
'links'
|
||||
'net'
|
||||
'network_mode'
|
||||
'ulimits'
|
||||
'volumes_from'
|
||||
'cpu_shares'
|
||||
'cpu_quota'
|
||||
'cpuset'
|
||||
'domainname'
|
||||
'hostname'
|
||||
'mac_address'
|
||||
'mem_limit'
|
||||
'memswap_limit'
|
||||
'privileged'
|
||||
'tty'
|
||||
'read_only'
|
||||
'shm_size'
|
||||
'ipc'
|
||||
'restart'
|
||||
'security_opt'
|
||||
'networks'
|
||||
'pid'
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user