Keep track of target and actual device state, and make sure we keep updating the api with any differences until the two match.

This commit is contained in:
Pagan Gazzard 2015-03-07 17:31:56 +00:00 committed by Pablo Carranza Vélez
parent e5d6235d89
commit 48c21e9275
2 changed files with 56 additions and 29 deletions

View File

@ -39,14 +39,12 @@ knex.init.then ->
# Let API know what version we are, and our api connection info. # Let API know what version we are, and our api connection info.
console.log('Updating supervisor version and api info') console.log('Updating supervisor version and api info')
application.updateDeviceInfo( application.updateDeviceState(
api_port: config.listenPort api_port: config.listenPort
api_secret: secret api_secret: secret
supervisor_version: utils.supervisorVersion supervisor_version: utils.supervisorVersion
provisioning_progress: null provisioning_progress: null
provisioning_state: '' provisioning_state: ''
# Retry the device info update every 5s until it finally succeeds.
5000
) )
console.log('Starting Apps..') console.log('Starting Apps..')
@ -64,7 +62,7 @@ knex.init.then ->
updateIpAddr = -> updateIpAddr = ->
utils.findIpAddrs().then (ipAddrs) -> utils.findIpAddrs().then (ipAddrs) ->
application.updateDeviceInfo( application.updateDeviceState(
ip_address: ipAddrs.join(' ') ip_address: ipAddrs.join(' ')
) )
console.log('Starting periodic check for IP addresses..') console.log('Starting periodic check for IP addresses..')

View File

@ -28,7 +28,7 @@ exports.logSystemEvent = logSystemEvent = (message) ->
kill = (app) -> kill = (app) ->
logSystemEvent('Killing application ' + app.imageId) logSystemEvent('Killing application ' + app.imageId)
utils.mixpanelTrack('Application kill', app) utils.mixpanelTrack('Application kill', app)
updateDeviceInfo(status: 'Stopping') updateDeviceState(status: 'Stopping')
container = docker.getContainer(app.containerId) container = docker.getContainer(app.containerId)
console.log('Stopping and deleting container:', container) console.log('Stopping and deleting container:', container)
tty.stop(app.id) tty.stop(app.id)
@ -64,11 +64,11 @@ fetch = (app) ->
.catch (error) -> .catch (error) ->
utils.mixpanelTrack('Application download', app) utils.mixpanelTrack('Application download', app)
logSystemEvent('Downloading application ' + app.imageId) logSystemEvent('Downloading application ' + app.imageId)
updateDeviceInfo(status: 'Downloading') updateDeviceState(status: 'Downloading')
dockerUtils.fetchImageWithProgress app.imageId, (progress) -> dockerUtils.fetchImageWithProgress app.imageId, (progress) ->
updateDeviceInfo(download_progress: progress.percentage) updateDeviceState(download_progress: progress.percentage)
.then -> .then ->
updateDeviceInfo(download_progress: null) updateDeviceState(download_progress: null)
docker.getImage(app.imageId).inspectAsync() docker.getImage(app.imageId).inspectAsync()
exports.start = start = (app) -> exports.start = start = (app) ->
@ -95,7 +95,7 @@ exports.start = start = (app) ->
.then (imageInfo) -> .then (imageInfo) ->
utils.mixpanelTrack('Application install', app) utils.mixpanelTrack('Application install', app)
logSystemEvent('Installing application ' + app.imageId) logSystemEvent('Installing application ' + app.imageId)
updateDeviceInfo(status: 'Installing') updateDeviceState(status: 'Installing')
ports = {} ports = {}
if portList? if portList?
@ -129,7 +129,7 @@ exports.start = start = (app) ->
.tap (container) -> .tap (container) ->
utils.mixpanelTrack('Application start', app) utils.mixpanelTrack('Application start', app)
logSystemEvent('Starting application ' + app.imageId) logSystemEvent('Starting application ' + app.imageId)
updateDeviceInfo(status: 'Starting') updateDeviceState(status: 'Starting')
ports = {} ports = {}
if portList? if portList?
portList.forEach (port) -> portList.forEach (port) ->
@ -146,13 +146,13 @@ exports.start = start = (app) ->
] ]
) )
.then -> .then ->
updateDeviceInfo(commit: app.commit) updateDeviceState(commit: app.commit)
logger.attach(app) logger.attach(app)
.tap -> .tap ->
utils.mixpanelTrack('Application start', app.imageId) utils.mixpanelTrack('Application start', app.imageId)
logSystemEvent('Starting application ' + app.imageId) logSystemEvent('Starting application ' + app.imageId)
.finally -> .finally ->
updateDeviceInfo(status: 'Idle') updateDeviceState(status: 'Idle')
# 0 - Idle # 0 - Idle
# 1 - Updating # 1 - Updating
@ -270,7 +270,7 @@ exports.update = update = ->
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) setTimeout(update, delayTime)
.finally -> .finally ->
updateDeviceInfo(status: 'Idle') updateDeviceState(status: 'Idle')
if currentlyUpdating is 2 if currentlyUpdating is 2
# If an update is required then schedule it # If an update is required then schedule it
setTimeout(update) setTimeout(update)
@ -303,20 +303,49 @@ getDeviceID = do ->
throw new Error('Could not find this device?!') throw new Error('Could not find this device?!')
return devices[0].id return devices[0].id
exports.updateDeviceInfo = updateDeviceInfo = (body, retry = false) -> exports.updateDeviceState = updateDeviceState = do ->
Promise.all([ applyPromise = Promise.resolve()
targetState = {}
actualState = {}
getStateDiff = ->
_.omit targetState, (value, key) ->
actualState[key] is value
applyState = ->
if _.size(getStateDiff()) is 0
return
applyPromise = Promise.join(
knex('config').select('value').where(key: 'apiKey') knex('config').select('value').where(key: 'apiKey')
getDeviceID() getDeviceID()
]).spread ([{value: apiKey}], deviceID) -> ([{value: apiKey}], deviceID) ->
resinAPI.patch( stateDiff = getStateDiff()
if _.size(stateDiff) is 0
return
resinAPI.patch
resource: 'device' resource: 'device'
id: deviceID id: deviceID
body: body body: stateDiff
customOptions: customOptions:
apikey: apiKey apikey: apiKey
) .then ->
# Update the actual state.
_.merge(actualState, stateDiff)
.catch (error) -> .catch (error) ->
utils.mixpanelTrack('Device info update failure', {error, body}) utils.mixpanelTrack('Device info update failure', {error, stateDiff})
if retry isnt false # Delay 5s before retrying a failed update
Promise.delay(retry).then -> Promise.delay(5000)
updateDeviceInfo(body, retry) )
.finally ->
# Check if any more state diffs have appeared whilst we've been processing this update.
applyState()
return (updatedState = {}, retry = false) ->
# Remove any updates that match the last we successfully sent.
_.merge(targetState, updatedState)
# Only trigger applying state if an apply isn't already in progress.
if !applyPromise.isPending()
applyState()
return