mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-28 01:28:52 +00:00
89 lines
3.0 KiB
CoffeeScript
89 lines
3.0 KiB
CoffeeScript
Promise = require 'bluebird'
|
|
YAML = require 'yamljs'
|
|
_ = require 'lodash'
|
|
dockerUtils = require './docker-utils'
|
|
{ docker } = dockerUtils
|
|
fs = Promise.promisifyAll(require('fs'))
|
|
{ spawn, execAsync } = Promise.promisifyAll(require('child_process'))
|
|
mkdirp = Promise.promisify(require('mkdirp'))
|
|
path = require 'path'
|
|
utils = require './utils'
|
|
config = require './config'
|
|
|
|
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', 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
|
|
resolve()
|
|
child.stdout.on 'data', (data) ->
|
|
reportStatus(status: '' + data)
|
|
child.stderr.on 'data', (data) ->
|
|
reportStatus(status: '' + data)
|
|
|
|
writeComposeFile = (composeSpec, dstPath) ->
|
|
mkdirp(path.dirname(dstPath))
|
|
.then ->
|
|
YAML.stringify(composeSpec)
|
|
.then (yml) ->
|
|
fs.writeFileAsync(dstPath, yml)
|
|
.then ->
|
|
execAsync('sync')
|
|
|
|
# Runs docker-compose up using the compose YAML at "path".
|
|
# Reports status and errors in JSON to the onStatus function.
|
|
# 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(composePathSrc(appId))
|
|
.then (data) ->
|
|
YAML.parse(data.toString())
|
|
.then (composeSpec) ->
|
|
if composeSpec.version? && composeSpec.version == '2'
|
|
services = composeSpec.services
|
|
else
|
|
services = composeSpec
|
|
throw new Error('No services found') if !_.isObject(services)
|
|
servicesArray = _.pairs(services)
|
|
Promise.each servicesArray, ([ serviceName, service ]) ->
|
|
throw new Error("Service #{serviceName} has no image specified.") if !service.image
|
|
docker.getImage(service.image).inspectAsync()
|
|
.catch ->
|
|
dockerUtils.pullAndProtectImage(service.image, reportStatus)
|
|
.then ->
|
|
utils.validateKeys(service, utils.validComposeOptions)
|
|
.then ->
|
|
services[serviceName].volumes = utils.defaultBinds(composeDataPath(appId, serviceName))
|
|
.then ->
|
|
writeComposeFile(composeSpec, composePathDst(appId))
|
|
.then ->
|
|
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 = (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
|