mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-01-31 16:35:23 +00:00
Add default binds to containers created with API, add /v1/containers/update endpoint, and add a /data bind with an internal id
This commit is contained in:
parent
d652e13005
commit
b97fe634d5
@ -170,6 +170,7 @@ module.exports = (application) ->
|
||||
unparsedRouter.delete '/v1/images/*', dockerUtils.deleteImage
|
||||
unparsedRouter.get '/v1/images', dockerUtils.listImages
|
||||
parsedRouter.post '/v1/containers/create', dockerUtils.createContainer
|
||||
parsedRouter.post '/v1/containers/update', dockerUtils.updateContainer
|
||||
parsedRouter.post '/v1/containers/:id/start', dockerUtils.startContainer
|
||||
unparsedRouter.post '/v1/containers/:id/stop', dockerUtils.stopContainer
|
||||
unparsedRouter.delete '/v1/containers/:id', dockerUtils.deleteContainer
|
||||
|
@ -176,21 +176,8 @@ shouldMountKmod = (image) ->
|
||||
return false
|
||||
|
||||
application.start = start = (app) ->
|
||||
volumes =
|
||||
'/data': {}
|
||||
'/lib/modules': {}
|
||||
'/lib/firmware': {}
|
||||
'/host/var/lib/connman': {}
|
||||
'/host/run/dbus': {}
|
||||
binds = [
|
||||
config.dataPath + '/' + app.appId + ':/data'
|
||||
'/lib/modules:/lib/modules'
|
||||
'/lib/firmware:/lib/firmware'
|
||||
'/run/dbus:/host_run/dbus'
|
||||
'/run/dbus:/host/run/dbus'
|
||||
'/etc/resolv.conf:/etc/resolv.conf:rw'
|
||||
'/var/lib/connman:/host/var/lib/connman'
|
||||
]
|
||||
volumes = utils.defaultVolumes
|
||||
binds = utils.defaultBinds(app.appId)
|
||||
Promise.try ->
|
||||
# Parse the env vars before trying to access them, that's because they have to be stringified for knex..
|
||||
JSON.parse(app.env)
|
||||
|
@ -46,6 +46,12 @@ knex.init = Promise.all([
|
||||
knex.schema.createTable 'image', (t) ->
|
||||
t.increments('id').primary()
|
||||
t.string('repoTag')
|
||||
knex.schema.hasTable('container')
|
||||
.then (exists) ->
|
||||
if not exists
|
||||
knex.schema.createTable 'container', (t) ->
|
||||
t.increments('id').primary()
|
||||
t.string('containerId')
|
||||
|
||||
])
|
||||
|
||||
|
@ -244,57 +244,81 @@ do ->
|
||||
res.status(500).send(err?.message or err or 'Unknown error')
|
||||
|
||||
docker.modem.dialAsync = Promise.promisify(docker.modem.dial)
|
||||
exports.createContainer = (req, res) ->
|
||||
createContainer = (options, internalId) ->
|
||||
Promise.using writeLockImages(), ->
|
||||
knex('image').select().where('repoTag', req.body.Image)
|
||||
knex('image').select().where('repoTag', options.Image)
|
||||
.then (images) ->
|
||||
throw new Error('Only images created via the Supervisor can be used for creating containers.') if images.length == 0
|
||||
optsf =
|
||||
path: '/containers/create?'
|
||||
method: 'POST'
|
||||
options: req.body
|
||||
statusCodes:
|
||||
200: true
|
||||
201: true
|
||||
404: 'no such container'
|
||||
406: 'impossible to attach'
|
||||
500: 'server error'
|
||||
docker.modem.dialAsync(optsf)
|
||||
.then (data) ->
|
||||
res.json(data)
|
||||
.catch (err) ->
|
||||
res.status(500).send(err?.message or err or 'Unknown error')
|
||||
|
||||
exports.startContainer = (req, res) ->
|
||||
docker.getContainer(req.params.id).startAsync(req.body)
|
||||
knex.transaction (trx) ->
|
||||
Promise.try ->
|
||||
return internalId if internalId?
|
||||
trx.insert({}, 'id').into('container')
|
||||
.then ([ id ]) ->
|
||||
return id
|
||||
.then (id) ->
|
||||
options.HostConfig ?= {}
|
||||
options.Volumes ?= {}
|
||||
_.assign(options.Volumes, utils.defaultVolumes)
|
||||
options.HostConfig.Binds = utils.defaultBinds("containers/#{id}")
|
||||
optsf =
|
||||
path: '/containers/create?'
|
||||
method: 'POST'
|
||||
options: options
|
||||
statusCodes:
|
||||
200: true
|
||||
201: true
|
||||
404: 'no such container'
|
||||
406: 'impossible to attach'
|
||||
500: 'server error'
|
||||
docker.modem.dialAsync(optsf)
|
||||
.then (data) ->
|
||||
containerId = data.Id
|
||||
trx('container').update({ containerId }).where({ id })
|
||||
.then ->
|
||||
return data
|
||||
exports.createContainer = (req, res) ->
|
||||
createContainer(req.body)
|
||||
.then (data) ->
|
||||
res.json(data)
|
||||
.catch (err) ->
|
||||
res.status(500).send(err?.message or err or 'Unknown error')
|
||||
|
||||
exports.stopContainer = (req, res) ->
|
||||
container = docker.getContainer(req.params.id)
|
||||
startContainer = (containerId, options) ->
|
||||
docker.getContainer(containerId).startAsync(options)
|
||||
exports.startContainer = (req, res) ->
|
||||
startContainer(req.params.id, req.body)
|
||||
.then (data) ->
|
||||
res.json(data)
|
||||
.catch (err) ->
|
||||
res.status(500).send(err?.message or err or 'Unknown error')
|
||||
|
||||
stopContainer = (containerId, options) ->
|
||||
container = docker.getContainer(containerId)
|
||||
knex('app').select()
|
||||
.then (apps) ->
|
||||
throw new Error('Cannot stop an app container') if _.any(apps, containerId: req.params.id)
|
||||
throw new Error('Cannot stop an app container') if _.any(apps, { containerId })
|
||||
container.inspectAsync()
|
||||
.then (cont) ->
|
||||
throw new Error('Cannot stop supervisor container') if cont.Name == '/resin_supervisor' or _.any(cont.Names, (n) -> n == '/resin_supervisor')
|
||||
container.stopAsync(sanitizeQuery(req.query))
|
||||
container.stopAsync(options)
|
||||
exports.stopContainer = (req, res) ->
|
||||
stopContainer(req.params.id, sanitizeQuery(req.query))
|
||||
.then (data) ->
|
||||
res.json(data)
|
||||
.catch (err) ->
|
||||
res.status(500).send(err?.message or err or 'Unknown error')
|
||||
|
||||
exports.deleteContainer = (req, res) ->
|
||||
container = docker.getContainer(req.params.id)
|
||||
deleteContainer = (containerId, options) ->
|
||||
container = docker.getContainer(containerId)
|
||||
knex('app').select()
|
||||
.then (apps) ->
|
||||
throw new Error('Cannot remove an app container') if _.any(apps, containerId: req.params.id)
|
||||
throw new Error('Cannot remove an app container') if _.any(apps, { containerId })
|
||||
container.inspectAsync()
|
||||
.then (cont) ->
|
||||
throw new Error('Cannot remove supervisor container') if cont.Name == '/resin_supervisor' or _.any(cont.Names, (n) -> n == '/resin_supervisor')
|
||||
container.removeAsync(sanitizeQuery(req.query))
|
||||
container.removeAsync(options)
|
||||
exports.deleteContainer = (req, res) ->
|
||||
deleteContainer(req.params.id, sanitizeQuery(req.query))
|
||||
.then (data) ->
|
||||
res.json(data)
|
||||
.catch (err) ->
|
||||
@ -306,3 +330,21 @@ do ->
|
||||
res.json(containers)
|
||||
.catch (err) ->
|
||||
res.status(500).send(err?.message or err or 'Unknown error')
|
||||
|
||||
exports.updateContainer = (req, res) ->
|
||||
{ oldContainerId } = req.query
|
||||
return res.status(400).send('Missing oldContainerId') if !oldContainerId?
|
||||
knex('container').select().where({ containerId: oldContainerId })
|
||||
.then ([ oldContainer ]) ->
|
||||
return res.status(404).send('Old container not found') if !oldContainer?
|
||||
stopContainer(oldContainerId, t: 10)
|
||||
.then ->
|
||||
deleteContainer(oldContainerId, v: true)
|
||||
.then ->
|
||||
createContainer(req.body, oldContainer.id)
|
||||
.then ->
|
||||
startContainer(data.Id)
|
||||
.then (data) ->
|
||||
res.json(data)
|
||||
.catch (err) ->
|
||||
res.status(500).send(err?.message or err or 'Unknown error')
|
@ -204,3 +204,22 @@ exports.getOSVersion = (path) ->
|
||||
.catch (err) ->
|
||||
console.log('Could not get OS Version: ', err, err.stack)
|
||||
return undefined
|
||||
|
||||
exports.defaultVolumes = {
|
||||
'/data': {}
|
||||
'/lib/modules': {}
|
||||
'/lib/firmware': {}
|
||||
'/host/var/lib/connman': {}
|
||||
'/host/run/dbus': {}
|
||||
}
|
||||
|
||||
exports.defaultBinds = (dataPath) ->
|
||||
return [
|
||||
config.dataPath + '/' + dataPath + ':/data'
|
||||
'/lib/modules:/lib/modules'
|
||||
'/lib/firmware:/lib/firmware'
|
||||
'/run/dbus:/host_run/dbus'
|
||||
'/run/dbus:/host/run/dbus'
|
||||
'/etc/resolv.conf:/etc/resolv.conf:rw'
|
||||
'/var/lib/connman:/host/var/lib/connman'
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user