Allow the supervisor to work in offline mode

A supervisorOfflineMode true-ish attribute in config.json will cause that:
* If unprovisioned, the supervisor won't try to provision on Resin
* The update cycle will not start as the device won't consider itself provisioned
* Logs will not be sent to pubnub
* Mixpanel events won't be tracked
* The device state won't be updated to the Resin API

This change will also make the Supervisor API work with an unprovisioned device.
This commit is contained in:
Pablo Carranza Velez 2016-07-23 01:17:00 -03:00
parent d652e13005
commit 98d9aca92d
7 changed files with 39 additions and 17 deletions

View File

@ -1,3 +1,4 @@
* Allow the supervisor to work in offline mode [Pablo]
* Fix duplicate logs issue [Kostas] * Fix duplicate logs issue [Kostas]
* **[Breaking]** Do not bind mount /run/dbus to /run/dbus [Pablo] * **[Breaking]** Do not bind mount /run/dbus to /run/dbus [Pablo]
* Default to not bind mounting kmod if container distro can't be found [Pablo] * Default to not bind mounting kmod if container distro can't be found [Pablo]

View File

@ -20,16 +20,17 @@ knex.init.then ->
utils.mixpanelProperties.uuid = uuid utils.mixpanelProperties.uuid = uuid
api = require './api' api = require './api'
application = require('./application')(logsChannel) application = require('./application')(logsChannel, bootstrap.offlineMode)
device = require './device' device = require './device'
console.log('Starting API server..')
apiServer = api(application).listen(config.listenPort)
apiServer.timeout = config.apiTimeout
bootstrap.done bootstrap.done
.then -> .then ->
device.getOSVersion() device.getOSVersion()
.then (osVersion) -> .then (osVersion) ->
console.log('Starting API server..')
apiServer = api(application).listen(config.listenPort)
apiServer.timeout = config.apiTimeout
# 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')
device.updateState( device.updateState(

View File

@ -703,10 +703,11 @@ application.initialize = ->
application.poll() application.poll()
application.update() application.update()
module.exports = (logsChannel) -> module.exports = (logsChannel, offlineMode) ->
logger.init( logger.init(
dockerSocket: config.dockerSocket dockerSocket: config.dockerSocket
pubnub: config.pubnub pubnub: config.pubnub
channel: "device-#{logsChannel}-logs" channel: "device-#{logsChannel}-logs"
offlineMode: offlineMode
) )
return application return application

View File

@ -64,12 +64,12 @@ bootstrap = ->
.tap -> .tap ->
bootstrapper.doneBootstrapping() bootstrapper.doneBootstrapping()
readConfigAndEnsureUUID = -> readConfig = ->
# Load config file
fs.readFileAsync(configPath, 'utf8') fs.readFileAsync(configPath, 'utf8')
.then(JSON.parse) .then(JSON.parse)
.then (configFromFile) ->
userConfig = configFromFile readConfigAndEnsureUUID = ->
Promise.try ->
return userConfig.uuid if userConfig.uuid? return userConfig.uuid if userConfig.uuid?
deviceRegister.generateUUID() deviceRegister.generateUUID()
.then (uuid) -> .then (uuid) ->
@ -84,6 +84,8 @@ readConfigAndEnsureUUID = ->
bootstrapOrRetry = -> bootstrapOrRetry = ->
utils.mixpanelTrack('Device bootstrap') utils.mixpanelTrack('Device bootstrap')
# If we're in offline mode, we don't start the provisioning process so bootstrap.done will never fulfill
return if bootstrapper.offlineMode
bootstrap().catch (err) -> bootstrap().catch (err) ->
utils.mixpanelTrack('Device bootstrap failed, retrying', { error: err, delay: config.bootstrapRetryDelay }) utils.mixpanelTrack('Device bootstrap failed, retrying', { error: err, delay: config.bootstrapRetryDelay })
setTimeout(bootstrapOrRetry, config.bootstrapRetryDelay) setTimeout(bootstrapOrRetry, config.bootstrapRetryDelay)
@ -95,10 +97,15 @@ bootstrapper.done = new Promise (resolve) ->
bootstrapper.bootstrapped = false bootstrapper.bootstrapped = false
bootstrapper.startBootstrapping = -> bootstrapper.startBootstrapping = ->
knex('config').select('value').where(key: 'uuid') # Load config file
readConfig()
.then (configFromFile) ->
userConfig = configFromFile
bootstrapper.offlineMode = Boolean(userConfig.supervisorOfflineMode)
knex('config').select('value').where(key: 'uuid')
.then ([ uuid ]) -> .then ([ uuid ]) ->
if uuid?.value if uuid?.value
bootstrapper.doneBootstrapping() bootstrapper.doneBootstrapping() if !bootstrapper.offlineMode
return uuid.value return uuid.value
console.log('New device detected. Bootstrapping..') console.log('New device detected. Bootstrapping..')
readConfigAndEnsureUUID() readConfigAndEnsureUUID()

View File

@ -9,6 +9,7 @@ configPath = '/boot/config.json'
request = Promise.promisifyAll(require('request')) request = Promise.promisifyAll(require('request'))
execAsync = Promise.promisify(require('child_process').exec) execAsync = Promise.promisify(require('child_process').exec)
fs = Promise.promisifyAll(require('fs')) fs = Promise.promisifyAll(require('fs'))
bootstrap = require './bootstrap'
exports.getID = do -> exports.getID = do ->
deviceIdPromise = null deviceIdPromise = null
@ -17,10 +18,14 @@ exports.getID = do ->
deviceIdPromise ?= Promise.rejected() deviceIdPromise ?= Promise.rejected()
# Only fetch the device id once (when successful, otherwise retry for each request) # Only fetch the device id once (when successful, otherwise retry for each request)
deviceIdPromise = deviceIdPromise.catch -> deviceIdPromise = deviceIdPromise.catch ->
Promise.all([ # Wait for bootstrapping to be done before querying the Resin API
knex('config').select('value').where(key: 'apiKey') # This will also block users of getID (like applyState below until this is resolved)
knex('config').select('value').where(key: 'uuid') bootstrap.done
]) .then ->
Promise.all([
knex('config').select('value').where(key: 'apiKey')
knex('config').select('value').where(key: 'uuid')
])
.spread ([{ value: apiKey }], [{ value: uuid }]) -> .spread ([{ value: apiKey }], [{ value: uuid }]) ->
resinApi.get( resinApi.get(
resource: 'device' resource: 'device'

View File

@ -23,6 +23,9 @@ publish = do ->
publishQueue = [] publishQueue = []
initialised.then (config) -> initialised.then (config) ->
if config.offlineMode
publish = _.noop
return
pubnub = PUBNUB.init(config.pubnub) pubnub = PUBNUB.init(config.pubnub)
channel = config.channel channel = config.channel

View File

@ -18,10 +18,14 @@ tagExtra = process.env.SUPERVISOR_TAG_EXTRA
version += '+' + tagExtra if !_.isEmpty(tagExtra) version += '+' + tagExtra if !_.isEmpty(tagExtra)
exports.supervisorVersion = version exports.supervisorVersion = version
mixpanelClient = mixpanel.init(config.mixpanelToken) configJson = require('/boot/config.json')
if Boolean(configJson.supervisorOfflineMode)
mixpanelClient = mixpanel.init(config.mixpanelToken)
else
mixpanelClient = { track: _.noop }
exports.mixpanelProperties = mixpanelProperties = exports.mixpanelProperties = mixpanelProperties =
username: require('/boot/config.json').username username: configJson.username
exports.mixpanelTrack = (event, properties = {}) -> exports.mixpanelTrack = (event, properties = {}) ->
# Allow passing in an error directly and having it assigned to the error property. # Allow passing in an error directly and having it assigned to the error property.