mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-03-22 12:05:53 +00:00
Add endpoints for docker-compose up and down
This commit is contained in:
parent
548c9ff12b
commit
4278b6baf1
@ -1,3 +1,5 @@
|
||||
* Add endpoints for docker-compose up and down [Pablo]
|
||||
|
||||
# v1.11.6
|
||||
|
||||
* Fixed deltas for older docker daemon versions [petrosagg]
|
||||
|
@ -7,12 +7,17 @@ RUN apt-get -q update \
|
||||
&& apt-get install -qqy \
|
||||
btrfs-tools \
|
||||
ca-certificates \
|
||||
curl \
|
||||
rsync \
|
||||
supervisor \
|
||||
--no-install-recommends \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/
|
||||
|
||||
ENV DOCKER_COMPOSE_VERSION 1.8.0dev
|
||||
ENV DOCKER_COMPOSE_SHA256 9db33b03d9d02ea03aef7806f0272205a7c59b1a66599d559922db0eafaeb271
|
||||
|
||||
|
||||
# Copy supervisord configuration files
|
||||
COPY config/supervisor/ /etc/supervisor/
|
||||
|
||||
|
@ -2,17 +2,29 @@ FROM resin/armv7hf-node:0.10.40-slim
|
||||
|
||||
COPY 01_nodoc /etc/dpkg/dpkg.cfg.d/
|
||||
|
||||
ENV DOCKER_COMPOSE_VERSION 1.8.0dev
|
||||
ENV DOCKER_COMPOSE_SHA256 0f9e0bdedddc9188415e828985289d07e22e42244c758bd0e4579e7a5bfb881d
|
||||
|
||||
# Supervisor apt dependencies
|
||||
RUN apt-get -q update \
|
||||
&& apt-get install -qqy \
|
||||
btrfs-tools \
|
||||
ca-certificates \
|
||||
curl \
|
||||
rsync \
|
||||
supervisor \
|
||||
--no-install-recommends \
|
||||
&& curl -sLO http://resin-packages.s3.amazonaws.com/docker-compose/${DOCKER_COMPOSE_VERSION}/docker-compose-linux-armhf-${DOCKER_COMPOSE_VERSION}.tar.gz \
|
||||
&& echo $DOCKER_COMPOSE_SHA256 docker-compose-linux-armhf-${DOCKER_COMPOSE_VERSION}.tar.gz > docker-compose-linux-armhf-${DOCKER_COMPOSE_VERSION}.tar.gz.sha256 \
|
||||
&& sha256sum -c docker-compose-linux-armhf-${DOCKER_COMPOSE_VERSION}.tar.gz.sha256 \
|
||||
&& tar xzf docker-compose-linux-armhf-${DOCKER_COMPOSE_VERSION}.tar.gz \
|
||||
&& mv docker-compose-linux-armhf-${DOCKER_COMPOSE_VERSION}/docker-compose-linux-armhf /usr/bin/docker-compose \
|
||||
&& rm -rf docker-compose-linux-armhf-${DOCKER_COMPOSE_VERSION}* \
|
||||
&& apt-get purge -qqy curl \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/
|
||||
|
||||
|
||||
# Copy supervisord configuration files
|
||||
COPY config/supervisor/ /etc/supervisor/
|
||||
|
||||
|
@ -30,7 +30,8 @@
|
||||
"resin-register-device": "^2.0.0",
|
||||
"rwlock": "^5.0.0",
|
||||
"sqlite3": "3.0.9",
|
||||
"typed-error": "~0.1.0"
|
||||
"typed-error": "~0.1.0",
|
||||
"yamljs": "^0.2.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "0.10.22"
|
||||
|
@ -9,6 +9,7 @@ config = require './config'
|
||||
device = require './device'
|
||||
dockerUtils = require './docker-utils'
|
||||
_ = require 'lodash'
|
||||
compose = require './compose'
|
||||
|
||||
module.exports = (application) ->
|
||||
api = express()
|
||||
@ -175,6 +176,40 @@ module.exports = (application) ->
|
||||
unparsedRouter.delete '/v1/containers/:id', dockerUtils.deleteContainer
|
||||
unparsedRouter.get '/v1/containers', dockerUtils.listContainers
|
||||
|
||||
unparsedRouter.post '/v1/apps/:appId/compose/up', (req, res) ->
|
||||
appId = req.params.appId
|
||||
onStatus = (status) ->
|
||||
status = JSON.stringify(status) if _.isObject(status)
|
||||
res.write(status)
|
||||
knex('app').select().where({ appId })
|
||||
.then ([ app ]) ->
|
||||
return res.status(400).send('App not found') if !app?
|
||||
res.status(200)
|
||||
compose.up(application.composePath(appId), onStatus)
|
||||
.catch (err) ->
|
||||
console.log('Error on compose up:', err, err.stack)
|
||||
.finally ->
|
||||
res.end()
|
||||
.catch (err) ->
|
||||
res.status(503).send(err?.message or err or 'Unknown error')
|
||||
|
||||
unparsedRouter.post '/v1/apps/:appId/compose/down', (req, res) ->
|
||||
appId = req.params.appId
|
||||
onStatus = (status) ->
|
||||
status = JSON.stringify(status) if _.isObject(status)
|
||||
res.write(status)
|
||||
knex('app').select().where({ appId })
|
||||
.then ([ app ]) ->
|
||||
return res.status(400).send('App not found') if !app?
|
||||
res.status(200)
|
||||
compose.down(application.composePath(appId), onStatus)
|
||||
.catch (err) ->
|
||||
console.log('Error on compose down:', err, err.stack)
|
||||
.finally ->
|
||||
res.end()
|
||||
.catch (err) ->
|
||||
res.status(503).send(err?.message or err or 'Unknown error')
|
||||
|
||||
api.use(unparsedRouter)
|
||||
api.use(parsedRouter)
|
||||
|
||||
|
@ -311,6 +311,9 @@ 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))
|
||||
|
65
src/compose.coffee
Normal file
65
src/compose.coffee
Normal file
@ -0,0 +1,65 @@
|
||||
Promise = require 'bluebird'
|
||||
YAML = require 'yamljs'
|
||||
_ = require 'lodash'
|
||||
dockerUtils = require './docker-utils'
|
||||
{ docker } = dockerUtils
|
||||
fs = Promise.promisifyAll(require('fs'))
|
||||
spawn = require('child_process').spawn
|
||||
|
||||
# Runs docker-compose up using the compose YAML at "path".
|
||||
# Reports status and errors in JSON to the onStatus function.
|
||||
exports.up = (path, onStatus) ->
|
||||
onStatus = console.log.bind(console) if !onStatus?
|
||||
reportStatus = (status) ->
|
||||
try onStatus(status)
|
||||
fs.readFileAsync(path)
|
||||
.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.pullImage(service.image, reportStatus)
|
||||
.then ->
|
||||
new Promise (resolve, reject) ->
|
||||
child = spawn('docker-compose', ['-f', path, 'up', '-d'], 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)
|
||||
.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 = (path, onStatus) ->
|
||||
onStatus = console.log.bind(console) if !onStatus?
|
||||
reportStatus = (status) ->
|
||||
try onStatus(status)
|
||||
new Promise (resolve, reject) ->
|
||||
child = spawn('docker-compose', ['-f', path, 'down'], 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)
|
||||
.catch (err) ->
|
||||
msg = err?.message or err
|
||||
reportStatus(error: msg)
|
||||
throw err
|
@ -200,6 +200,13 @@ do ->
|
||||
.catch (err) ->
|
||||
res.status(500).send(err?.message or err or 'Unknown error')
|
||||
|
||||
exports.pullImage = (image, onProgress) ->
|
||||
repoTag = buildRepoTag(image)
|
||||
Promise.using writeLockImages(), ->
|
||||
knex('image').insert({ repoTag })
|
||||
.then ->
|
||||
dockerProgress.pull(repoTag, onProgress)
|
||||
|
||||
exports.loadImage = (req, res) ->
|
||||
Promise.using writeLockImages(), ->
|
||||
docker.listImagesAsync()
|
||||
|
Loading…
x
Reference in New Issue
Block a user