Auto-merge for PR #472 via VersionBot

When the device is about to reboot or shutdown, close the API server and avoid applying updates
This commit is contained in:
resin-io-versionbot[bot] 2017-07-28 00:35:58 +00:00 committed by GitHub
commit bb6652c52d
7 changed files with 43 additions and 25 deletions

View File

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
This project adheres to [Semantic Versioning](http://semver.org/).
## v6.1.2 - 2017-07-27
* When the device is about to reboot or shutdown, close the API server and avoid applying updates [Pablo Carranza Velez]
## v6.1.1 - 2017-07-27
* Avoid unhandled errors when in offline mode due to a missing apiEndpoint [Pablo Carranza Velez]

View File

@ -77,7 +77,7 @@ $ curl -X POST --header "Content-Type:application/json" \
Triggers an update check on the supervisor. Optionally, forces an update when updates are locked.
Responds with an empty 204 (Accepted) response.
Responds with an empty 204 (No Content) response.
#### Request body
Can be a JSON object with a `force` property. If this property is true, the update lock will be overridden.
@ -110,7 +110,7 @@ $ curl -X POST --header "Content-Type:application/json" \
Reboots the device
When successful, responds with 204 accepted and a JSON object:
When successful, responds with 202 accepted and a JSON object:
```json
{
"Data": "OK",
@ -144,7 +144,7 @@ $ curl -X POST --header "Content-Type:application/json" \
**Dangerous**. Shuts down the device.
When successful, responds with 204 accepted and a JSON object:
When successful, responds with 202 accepted and a JSON object:
```json
{
"Data": "OK",

View File

@ -1,7 +1,7 @@
{
"name": "resin-supervisor",
"description": "This is resin.io's Supervisor, a program that runs on IoT devices and has the task of running user Apps (which are Docker containers), and updating them as Resin's API informs it to.",
"version": "6.1.1",
"version": "6.1.2",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@ -57,18 +57,16 @@ module.exports = (application) ->
.then (app) ->
application.kill(app, removeContainer: false) if app?
.then ->
new Promise (resolve, reject) ->
application.logSystemMessage('Rebooting', {}, 'Reboot')
utils.gosuper.post('/v1/reboot')
.on('error', reject)
.on('response', -> resolve())
.pipe(res)
application.logSystemMessage('Rebooting', {}, 'Reboot')
device.reboot()
.then (response) ->
res.status(202).json(response)
.catch (err) ->
if err instanceof application.UpdatesLockedError
status = 423
else
status = 500
res.status(status).send(err?.message or err or 'Unknown error')
res.status(status).json({ Data: '', Error: err?.message or err or 'Unknown error' })
parsedRouter.post '/v1/shutdown', (req, res) ->
force = req.body.force
@ -80,18 +78,16 @@ module.exports = (application) ->
.then (app) ->
application.kill(app, removeContainer: false) if app?
.then ->
new Promise (resolve, reject) ->
application.logSystemMessage('Shutting down', {}, 'Shutdown')
utils.gosuper.post('/v1/shutdown')
.on('error', reject)
.on('response', -> resolve())
.pipe(res)
application.logSystemMessage('Shutting down', {}, 'Shutdown')
device.shutdown()
.then (response) ->
res.status(202).json(response)
.catch (err) ->
if err instanceof application.UpdatesLockedError
status = 423
else
status = 500
res.status(status).send(err?.message or err or 'Unknown error')
res.status(status).json({ Data: '', Error: err?.message or err or 'Unknown error' })
parsedRouter.post '/v1/purge', (req, res) ->
appId = req.body.appId

View File

@ -29,7 +29,8 @@ knex.init.then ->
.then ->
apiServer = api(application).listen(config.listenPort)
apiServer.timeout = config.apiTimeout
device.events.on 'shutdown', ->
apiServer.close()
bootstrap.done
.then ->
Promise.join(

View File

@ -709,7 +709,7 @@ application.update = update = (force, scheduled = false) ->
.tap (remoteApps) ->
# Before running the updates, try to clean up any images that aren't in use
# and will not be used in the target state
return if application.localMode
return if application.localMode or device.shuttingDown
dockerUtils.cleanupContainersAndImages(_.map(remoteApps, 'imageId'))
.catch (err) ->
console.log('Cleanup failed: ', err, err.stack)
@ -737,7 +737,7 @@ application.update = update = (force, scheduled = false) ->
logSystemMessage("Error fetching/applying device configuration: #{err}", { error: err }, 'Set device configuration error')
.return(allAppIds)
.map (appId) ->
return if application.localMode
return if application.localMode or device.shuttingDown
Promise.try ->
needsDownload = _.includes(toBeDownloaded, appId)
if _.includes(toBeRemoved, appId)
@ -792,9 +792,9 @@ application.update = update = (force, scheduled = false) ->
_.each(failures, (err) -> console.error('Error:', err, err.stack))
throw new Error(joinErrorMessages(failures)) if failures.length > 0
.then ->
proxyvisor.sendUpdates()
proxyvisor.sendUpdates() if !device.shuttingDown
.then ->
return if application.localMode
return if application.localMode or device.shuttingDown
updateStatus.failed = 0
device.setUpdateState(update_pending: false, update_downloaded: false, update_failed: false)
# We cleanup here as we want a point when we have a consistent apps/images state, rather than potentially at a

View File

@ -1,7 +1,6 @@
_ = require 'lodash'
Promise = require 'bluebird'
memoizee = require 'memoizee'
knex = require './db'
utils = require './utils'
{ resinApi } = require './request'
device = exports
@ -12,6 +11,7 @@ fs = Promise.promisifyAll(require('fs'))
bootstrap = require './bootstrap'
{ checkTruthy } = require './lib/validation'
osRelease = require './lib/os-release'
EventEmitter = require 'events'
# If we don't use promise: 'then', exceptions will crash the program
memoizePromise = _.partial(memoizee, _, promise: 'then')
@ -40,8 +40,25 @@ exports.getID = memoizePromise ->
throw new Error('Could not find this device?!')
return devices[0].id
exports.shuttingDown = false
exports.events = new EventEmitter()
exports.reboot = ->
utils.gosuper.postAsync('/v1/reboot')
utils.gosuper.postAsync('/v1/reboot', { json: true })
.spread (res, body) ->
if res.statusCode != 202
throw new Error(body.Error)
exports.shuttingDown = true
exports.events.emit('shutdown')
return body
exports.shutdown = ->
utils.gosuper.postAsync('/v1/shutdown', { json: true })
.spread (res, body) ->
if res.statusCode != 202
throw new Error(body.Error)
exports.shuttingDown = true
exports.events.emit('shutdown')
return body
exports.hostConfigConfigVarPrefix = 'RESIN_HOST_'
bootConfigEnvVarPrefix = 'RESIN_HOST_CONFIG_'