From 98d9aca92deebae2aad53bf2af5660792675497b Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Sat, 23 Jul 2016 01:17:00 -0300 Subject: [PATCH] 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. --- CHANGELOG.md | 1 + src/app.coffee | 9 +++++---- src/application.coffee | 3 ++- src/bootstrap.coffee | 19 +++++++++++++------ src/device.coffee | 13 +++++++++---- src/lib/logger.coffee | 3 +++ src/utils.coffee | 8 ++++++-- 7 files changed, 39 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0fdf9be..4bfc4f9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +* Allow the supervisor to work in offline mode [Pablo] * Fix duplicate logs issue [Kostas] * **[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] diff --git a/src/app.coffee b/src/app.coffee index d7df0183..bd8a1031 100644 --- a/src/app.coffee +++ b/src/app.coffee @@ -20,16 +20,17 @@ knex.init.then -> utils.mixpanelProperties.uuid = uuid api = require './api' - application = require('./application')(logsChannel) + application = require('./application')(logsChannel, bootstrap.offlineMode) device = require './device' + console.log('Starting API server..') + apiServer = api(application).listen(config.listenPort) + apiServer.timeout = config.apiTimeout + bootstrap.done .then -> device.getOSVersion() .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. console.log('Updating supervisor version and api info') device.updateState( diff --git a/src/application.coffee b/src/application.coffee index 67d87cea..13f59bf2 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -703,10 +703,11 @@ application.initialize = -> application.poll() application.update() -module.exports = (logsChannel) -> +module.exports = (logsChannel, offlineMode) -> logger.init( dockerSocket: config.dockerSocket pubnub: config.pubnub channel: "device-#{logsChannel}-logs" + offlineMode: offlineMode ) return application diff --git a/src/bootstrap.coffee b/src/bootstrap.coffee index 7281f6fa..a321c562 100644 --- a/src/bootstrap.coffee +++ b/src/bootstrap.coffee @@ -64,12 +64,12 @@ bootstrap = -> .tap -> bootstrapper.doneBootstrapping() -readConfigAndEnsureUUID = -> - # Load config file +readConfig = -> fs.readFileAsync(configPath, 'utf8') .then(JSON.parse) - .then (configFromFile) -> - userConfig = configFromFile + +readConfigAndEnsureUUID = -> + Promise.try -> return userConfig.uuid if userConfig.uuid? deviceRegister.generateUUID() .then (uuid) -> @@ -84,6 +84,8 @@ readConfigAndEnsureUUID = -> bootstrapOrRetry = -> 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) -> utils.mixpanelTrack('Device bootstrap failed, retrying', { error: err, delay: config.bootstrapRetryDelay }) setTimeout(bootstrapOrRetry, config.bootstrapRetryDelay) @@ -95,10 +97,15 @@ bootstrapper.done = new Promise (resolve) -> bootstrapper.bootstrapped = false 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 ]) -> if uuid?.value - bootstrapper.doneBootstrapping() + bootstrapper.doneBootstrapping() if !bootstrapper.offlineMode return uuid.value console.log('New device detected. Bootstrapping..') readConfigAndEnsureUUID() diff --git a/src/device.coffee b/src/device.coffee index babaa953..a191940b 100644 --- a/src/device.coffee +++ b/src/device.coffee @@ -9,6 +9,7 @@ configPath = '/boot/config.json' request = Promise.promisifyAll(require('request')) execAsync = Promise.promisify(require('child_process').exec) fs = Promise.promisifyAll(require('fs')) +bootstrap = require './bootstrap' exports.getID = do -> deviceIdPromise = null @@ -17,10 +18,14 @@ exports.getID = do -> deviceIdPromise ?= Promise.rejected() # Only fetch the device id once (when successful, otherwise retry for each request) deviceIdPromise = deviceIdPromise.catch -> - Promise.all([ - knex('config').select('value').where(key: 'apiKey') - knex('config').select('value').where(key: 'uuid') - ]) + # Wait for bootstrapping to be done before querying the Resin API + # This will also block users of getID (like applyState below until this is resolved) + bootstrap.done + .then -> + Promise.all([ + knex('config').select('value').where(key: 'apiKey') + knex('config').select('value').where(key: 'uuid') + ]) .spread ([{ value: apiKey }], [{ value: uuid }]) -> resinApi.get( resource: 'device' diff --git a/src/lib/logger.coffee b/src/lib/logger.coffee index 9f9a1f71..da5ce726 100644 --- a/src/lib/logger.coffee +++ b/src/lib/logger.coffee @@ -23,6 +23,9 @@ publish = do -> publishQueue = [] initialised.then (config) -> + if config.offlineMode + publish = _.noop + return pubnub = PUBNUB.init(config.pubnub) channel = config.channel diff --git a/src/utils.coffee b/src/utils.coffee index d6119f05..9240ad59 100644 --- a/src/utils.coffee +++ b/src/utils.coffee @@ -18,10 +18,14 @@ tagExtra = process.env.SUPERVISOR_TAG_EXTRA version += '+' + tagExtra if !_.isEmpty(tagExtra) 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 = - username: require('/boot/config.json').username + username: configJson.username exports.mixpanelTrack = (event, properties = {}) -> # Allow passing in an error directly and having it assigned to the error property.