Add endpoints for docker-compose up and down

This commit is contained in:
Pablo Carranza Velez 2016-06-17 06:06:42 +00:00
parent 548c9ff12b
commit 4278b6baf1
8 changed files with 131 additions and 1 deletions

View File

@ -1,3 +1,5 @@
* Add endpoints for docker-compose up and down [Pablo]
# v1.11.6
* Fixed deltas for older docker daemon versions [petrosagg]

View File

@ -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/

View File

@ -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/

View File

@ -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"

View File

@ -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)

View File

@ -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
View 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

View File

@ -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()