Store and make use of container ids.

This stores the container id for an app when creating that app, using it when it is necessary to stop/remove the app and when attempting to start it again (rather than creating a new container each time, eg restarting the pi does not create a new container any more)
This commit is contained in:
Pagan Gazzard 2014-08-08 15:25:19 +01:00 committed by Pablo Carranza Vélez
parent 550aebb163
commit d07c6abe99
2 changed files with 77 additions and 74 deletions

View File

@ -42,7 +42,7 @@ knex('config').select('value').where(key: 'uuid').then ([uuid]) ->
console.log('Starting Apps..') console.log('Starting Apps..')
knex('app').select() knex('app').select()
.then (apps) -> .then (apps) ->
Promise.all(apps.map(application.restart)) Promise.all(apps.map(application.start))
.catch (error) -> .catch (error) ->
console.error("Error starting apps:", error) console.error("Error starting apps:", error)
.then -> .then ->

View File

@ -42,13 +42,7 @@ publish = do ->
exports.kill = kill = (app) -> exports.kill = kill = (app) ->
utils.mixpanelTrack('Application kill', app) utils.mixpanelTrack('Application kill', app)
updateDeviceInfo(status: 'Stopping') updateDeviceInfo(status: 'Stopping')
docker.listContainersAsync(all: 1) container = docker.getContainer(app.containerId)
.then (containers) ->
Promise.all(
containers
.filter (container) -> container.Image is "#{app.imageId}:latest"
.map (container) ->
container = docker.getContainer(container.Id)
console.log('Stopping and deleting container:', container) console.log('Stopping and deleting container:', container)
container.stopAsync() container.stopAsync()
.then -> .then ->
@ -64,9 +58,9 @@ exports.kill = kill = (app) ->
if statusCode is '404' if statusCode is '404'
return return
throw err throw err
)
.tap -> .tap ->
utils.mixpanelTrack('Application stop', app.imageId) utils.mixpanelTrack('Application stop', app.imageId)
knex('app').where('id', app.id).delete()
.finally -> .finally ->
updateDeviceInfo(status: 'Idle') updateDeviceInfo(status: 'Idle')
@ -75,6 +69,25 @@ isValidPort = (port) ->
return parseFloat(port) is maybePort and maybePort > 0 and maybePort < 65535 return parseFloat(port) is maybePort and maybePort > 0 and maybePort < 65535
exports.start = start = (app) -> exports.start = start = (app) ->
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)
.then (env) ->
if env.PORT?
portList = env.PORT
.split(',')
.map((port) -> port.trim())
.filter(isValidPort)
if app.containerId?
# If we have a container id then check it exists and if so use it.
container = docker.getContainer(app.containerId)
containerPromise = container.inspectAsync().return(container)
else
containerPromise = Promise.rejected()
# If there is no existing container then create one instead.
containerPromise.catch ->
docker.getImage(app.imageId).inspectAsync() docker.getImage(app.imageId).inspectAsync()
.catch (error) -> .catch (error) ->
utils.mixpanelTrack('Application install', app) utils.mixpanelTrack('Application install', app)
@ -95,14 +108,7 @@ exports.start = start = (app) ->
console.log("Creating container:", app.imageId) console.log("Creating container:", app.imageId)
updateDeviceInfo(status: 'Starting') updateDeviceInfo(status: 'Starting')
ports = {} ports = {}
# Parse the env vars before trying to access them, that's because they have to be stringified for knex.. if portList?
env = JSON.parse(app.env)
if env.PORT?
portList = env.PORT
.split(',')
.map((port) -> port.trim())
.filter(isValidPort)
portList.forEach (port) -> portList.forEach (port) ->
ports[port + '/tcp'] = {} ports[port + '/tcp'] = {}
@ -116,7 +122,7 @@ exports.start = start = (app) ->
Env: _.map env, (v, k) -> k + '=' + v Env: _.map env, (v, k) -> k + '=' + v
ExposedPorts: ports ExposedPorts: ports
) )
.then (container) -> .tap (container) ->
console.log('Starting container:', app.imageId) console.log('Starting container:', app.imageId)
ports = {} ports = {}
if portList? if portList?
@ -140,16 +146,16 @@ exports.start = start = (app) ->
es.split() es.split()
es.mapSync(publish) es.mapSync(publish)
) )
.tap -> .tap (container) ->
utils.mixpanelTrack('Application start', app.imageId) utils.mixpanelTrack('Application start', app.imageId)
app.containerId = container.id
if app.id?
knex('app').update(app).where(id: app.id)
else
knex('app').insert(app)
.finally -> .finally ->
updateDeviceInfo(status: 'Idle') updateDeviceInfo(status: 'Idle')
exports.restart = restart = (app) ->
kill(app)
.then ->
start(app)
# 0 - Idle # 0 - Idle
# 1 - Updating # 1 - Updating
# 2 - Update required # 2 - Update required
@ -202,9 +208,9 @@ exports.update = update = ->
console.log(remoteImages) console.log(remoteImages)
console.log("Local apps") console.log("Local apps")
apps = _.map(apps, (app) -> _.pick(app, ['commit', 'imageId', 'env']))
apps = _.indexBy(apps, 'imageId') apps = _.indexBy(apps, 'imageId')
localImages = _.keys(apps) localApps = _.mapValues(apps, (app) -> _.pick(app, ['commit', 'imageId', 'env']))
localImages = _.keys(localApps)
console.log(localImages) console.log(localImages)
console.log("Apps to be removed") console.log("Apps to be removed")
@ -218,28 +224,25 @@ exports.update = update = ->
console.log("Apps to be updated") console.log("Apps to be updated")
toBeUpdated = _.intersection(remoteImages, localImages) toBeUpdated = _.intersection(remoteImages, localImages)
toBeUpdated = _.filter toBeUpdated, (imageId) -> toBeUpdated = _.filter toBeUpdated, (imageId) ->
return !_.isEqual(remoteApps[imageId], apps[imageId]) return !_.isEqual(remoteApps[imageId], localApps[imageId])
console.log(toBeUpdated) console.log(toBeUpdated)
# Delete all the ones to remove in one go # Delete all the ones to remove in one go
Promise.map toBeRemoved, (imageId) -> Promise.map toBeRemoved, (imageId) ->
kill(apps[imageId]) kill(apps[imageId])
.then ->
knex('app').where('imageId', imageId).delete()
.then -> .then ->
# Then install the apps and add each to the db as they succeed # Then install the apps and add each to the db as they succeed
installingPromises = toBeInstalled.map (imageId) -> installingPromises = toBeInstalled.map (imageId) ->
app = remoteApps[imageId] app = remoteApps[imageId]
start(app) start(app)
.then -> # And remove/recreate updated apps and update db as they succeed
knex('app').insert(app)
# And restart updated apps and update db as they succeed
updatingPromises = toBeUpdated.map (imageId) -> updatingPromises = toBeUpdated.map (imageId) ->
localApp = apps[imageId]
app = remoteApps[imageId] app = remoteApps[imageId]
utils.mixpanelTrack('Application update', app) utils.mixpanelTrack('Application update', app)
restart(app) kill(localApp)
.then -> .then ->
knex('app').update(app).where(imageId: app.imageId) start(app)
Promise.all(installingPromises.concat(updatingPromises)) Promise.all(installingPromises.concat(updatingPromises))
.then -> .then ->
failedUpdates = 0 failedUpdates = 0