mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-03-11 06:54:17 +00:00
Download/update/remove/install all apps in a map
This commit is contained in:
parent
4aea117330
commit
233004d130
@ -285,16 +285,16 @@ UPDATE_UPDATING = 1
|
|||||||
UPDATE_REQUIRED = 2
|
UPDATE_REQUIRED = 2
|
||||||
|
|
||||||
updateStatus =
|
updateStatus =
|
||||||
currentState: UPDATE_IDLE
|
state: UPDATE_IDLE
|
||||||
failedUpdates: 0
|
failed: 0
|
||||||
forceNextUpdate: false
|
forceNext: false
|
||||||
exports.update = update = (force) ->
|
exports.update = update = (force) ->
|
||||||
if updateStatus.currentState isnt UPDATE_IDLE
|
if updateStatus.state isnt UPDATE_IDLE
|
||||||
# Mark an update required after the current.
|
# Mark an update required after the current.
|
||||||
updateStatus.forceNextUpdate = force
|
updateStatus.forceNext = force
|
||||||
updateStatus.currentState = UPDATE_REQUIRED
|
updateStatus.state = UPDATE_REQUIRED
|
||||||
return
|
return
|
||||||
updateStatus.currentState = UPDATE_UPDATING
|
updateStatus.state = UPDATE_UPDATING
|
||||||
Promise.all([
|
Promise.all([
|
||||||
knex('config').select('value').where(key: 'apiKey')
|
knex('config').select('value').where(key: 'apiKey')
|
||||||
knex('config').select('value').where(key: 'uuid')
|
knex('config').select('value').where(key: 'uuid')
|
||||||
@ -364,36 +364,30 @@ exports.update = update = (force) ->
|
|||||||
return !_.isEqual(remoteApps[appId].imageId, localApps[appId].imageId)
|
return !_.isEqual(remoteApps[appId].imageId, localApps[appId].imageId)
|
||||||
toBeDownloaded = _.union(toBeDownloaded, toBeInstalled)
|
toBeDownloaded = _.union(toBeDownloaded, toBeInstalled)
|
||||||
|
|
||||||
# Fetch any updated images first
|
allAppIds = _.union(localAppIds, remoteAppIds)
|
||||||
Promise.map toBeDownloaded, (appId) ->
|
|
||||||
app = remoteApps[appId]
|
Promise.map allAppIds, (appId) ->
|
||||||
fetch(app)
|
Promise.try ->
|
||||||
.then ->
|
fetch(remoteApps[appId]) if _.includes(toBeDownloaded, appId)
|
||||||
failures = []
|
|
||||||
# Then delete all the ones to remove in one go
|
|
||||||
Promise.map toBeRemoved, (appId) ->
|
|
||||||
Promise.using lockUpdates(apps[appId], force), ->
|
|
||||||
# We get the app from the DB again in case someone restarted it
|
|
||||||
# (which would have changed its containerId)
|
|
||||||
knex('app').select().where({ appId })
|
|
||||||
.then ([ app ]) ->
|
|
||||||
if !app?
|
|
||||||
throw new Error('App not found')
|
|
||||||
kill(app)
|
|
||||||
.then ->
|
|
||||||
knex('app').where('appId', appId).delete()
|
|
||||||
.catch (err) ->
|
|
||||||
logSystemEvent(logTypes.stopAppError, apps[appId], err)
|
|
||||||
failures.push(err)
|
|
||||||
.then ->
|
.then ->
|
||||||
# Then install the apps and add each to the db as they succeed
|
if _.includes(toBeRemoved, appId)
|
||||||
installingPromises = toBeInstalled.map (appId) ->
|
Promise.using lockUpdates(apps[appId], force), ->
|
||||||
|
# We get the app from the DB again in case someone restarted it
|
||||||
|
# (which would have changed its containerId)
|
||||||
|
knex('app').select().where({ appId })
|
||||||
|
.then ([ app ]) ->
|
||||||
|
if !app?
|
||||||
|
throw new Error('App not found')
|
||||||
|
kill(app)
|
||||||
|
.then ->
|
||||||
|
knex('app').where('appId', appId).delete()
|
||||||
|
.catch (err) ->
|
||||||
|
logSystemEvent(logTypes.updateAppError, app, err)
|
||||||
|
throw err
|
||||||
|
else if _.includes(toBeInstalled, appId)
|
||||||
app = remoteApps[appId]
|
app = remoteApps[appId]
|
||||||
start(app)
|
start(app)
|
||||||
.catch (err) ->
|
else if _.includes(toBeUpdated, appId)
|
||||||
failures.push(err)
|
|
||||||
# And remove/recreate updated apps and update db as they succeed
|
|
||||||
updatingPromises = toBeUpdated.map (appId) ->
|
|
||||||
localApp = apps[appId]
|
localApp = apps[appId]
|
||||||
app = remoteApps[appId]
|
app = remoteApps[appId]
|
||||||
logSystemEvent(logTypes.updateApp, app) if localApp.imageId == app.imageId
|
logSystemEvent(logTypes.updateApp, app) if localApp.imageId == app.imageId
|
||||||
@ -407,30 +401,31 @@ exports.update = update = (force) ->
|
|||||||
.then ->
|
.then ->
|
||||||
start(app)
|
start(app)
|
||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
logSystemEvent(logTypes.updateAppError, apps[appId], err)
|
logSystemEvent(logTypes.updateAppError, app, err)
|
||||||
failures.push(err)
|
throw err
|
||||||
Promise.all(installingPromises.concat(updatingPromises))
|
.catch(_.identity)
|
||||||
.then ->
|
.filter(_.isError)
|
||||||
throw new Error(joinErrorMessages(failures)) if failures.length > 0
|
.then (failures) ->
|
||||||
|
throw new Error(joinErrorMessages(failures)) if failures.length > 0
|
||||||
.then ->
|
.then ->
|
||||||
updateStatus.failedUpdates = 0
|
updateStatus.failed = 0
|
||||||
# We cleanup here as we want a point when we have a consistent apps/images state, rather than potentially at a
|
# We cleanup here as we want a point when we have a consistent apps/images state, rather than potentially at a
|
||||||
# point where we might clean up an image we still want.
|
# point where we might clean up an image we still want.
|
||||||
dockerUtils.cleanupContainersAndImages()
|
dockerUtils.cleanupContainersAndImages()
|
||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
updateStatus.failedUpdates++
|
updateStatus.failed++
|
||||||
if updateStatus.currentState is UPDATE_REQUIRED
|
if updateStatus.state is UPDATE_REQUIRED
|
||||||
console.log('Updating failed, but there is already another update scheduled immediately: ', err)
|
console.log('Updating failed, but there is already another update scheduled immediately: ', err)
|
||||||
return
|
return
|
||||||
delayTime = Math.min(updateStatus.failedUpdates * 500, 30000)
|
delayTime = Math.min(updateStatus.failed * 500, 30000)
|
||||||
# If there was an error then schedule another attempt briefly in the future.
|
# If there was an error then schedule another attempt briefly in the future.
|
||||||
console.log('Scheduling another update attempt due to failure: ', delayTime, err)
|
console.log('Scheduling another update attempt due to failure: ', delayTime, err)
|
||||||
setTimeout(update, delayTime, force)
|
setTimeout(update, delayTime, force)
|
||||||
.finally ->
|
.finally ->
|
||||||
device.updateState(status: 'Idle')
|
device.updateState(status: 'Idle')
|
||||||
if updateStatus.currentState is UPDATE_REQUIRED
|
if updateStatus.state is UPDATE_REQUIRED
|
||||||
# If an update is required then schedule it
|
# If an update is required then schedule it
|
||||||
setTimeout(update, 1, updateStatus.forceNextUpdate)
|
setTimeout(update, 1, updateStatus.forceNext)
|
||||||
.finally ->
|
.finally ->
|
||||||
# Set the updating as finished in its own block, so it never has to worry about other code stopping this.
|
# Set the updating as finished in its own block, so it never has to worry about other code stopping this.
|
||||||
updateStatus.currentState = UPDATE_IDLE
|
updateStatus.state = UPDATE_IDLE
|
||||||
|
Loading…
x
Reference in New Issue
Block a user