From d98897cdcf5f35ad54553f233d0ba74f30689f0a Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 20 Oct 2017 13:47:19 -0700 Subject: [PATCH 1/4] Ensure preloaded apps get the deviceApiKey in the env vars, and apps never get the provisioning key, and improve detection of cases when the device has been pre-provisioned It appears preloaded apps have been getting restarted because the "apiKey" configuration value was only available after provisioning succeeded. This change ensures the deviceApiKey that the device will use is injected into the env vars of preloaded apps, ensuring the app is not restarted (unless provisioning fails and the uuid and deviceApiKey are regenerated, but this should be rare). We also ensure that whenever an app's RESIN_API_KEY env var is populated, it is *always* done with the deviceApiKey and never with the provisioning apiKey. Closes #457 Change-Type: patch Signed-off-by: Pablo Carranza Velez --- src/application.coffee | 8 +++++--- src/bootstrap.coffee | 5 ++++- src/utils.coffee | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/application.coffee b/src/application.coffee index 16481345..8f208e9a 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -633,9 +633,9 @@ getRemoteState = (uuid, apiKey) -> throw err # TODO: Actually store and use app.environment and app.config separately -parseEnvAndFormatRemoteApps = (remoteApps, uuid, apiKey) -> +parseEnvAndFormatRemoteApps = (remoteApps, uuid, deviceApiKey) -> appsWithEnv = _.mapValues remoteApps, (app, appId) -> - utils.extendEnvVars(app.environment, uuid, appId, app.name, app.commit) + utils.extendEnvVars(app.environment, uuid, deviceApiKey, appId, app.name, app.commit) .then (env) -> app.config ?= {} return { @@ -716,7 +716,9 @@ application.update = update = (force, scheduled = false) -> .then -> utils.setConfig('name', local.name) if local.name != deviceName .then -> - parseEnvAndFormatRemoteApps(local.apps, uuid, apiKey) + utils.getConfig('deviceApiKey') + .then (deviceApiKey) -> + parseEnvAndFormatRemoteApps(local.apps, uuid, deviceApiKey) .then (remoteApps) -> localApps = formatLocalApps(apps) resourcesForUpdate = compareForUpdate(localApps, remoteApps) diff --git a/src/bootstrap.coffee b/src/bootstrap.coffee index 47191dd6..758a5c3a 100644 --- a/src/bootstrap.coffee +++ b/src/bootstrap.coffee @@ -40,7 +40,7 @@ loadPreloadedApps = -> fs.readFileAsync(appsPath, 'utf8') .then(JSON.parse) .map (app) -> - utils.extendEnvVars(app.env, userConfig.uuid, app.appId, app.name, app.commit) + utils.extendEnvVars(app.env, userConfig.uuid, userConfig.deviceApiKey, app.appId, app.name, app.commit) .then (extendedEnv) -> app.env = JSON.stringify(extendedEnv) _.merge(devConfig, app.config) @@ -135,6 +135,7 @@ bootstrap = -> # We use the provisioning/user `apiKey` if it still exists because if it does it means we were already registered # using that key and have to rely on the exchange key mechanism to swap the keys as appropriate later { key: 'apiKey', value: userConfig.apiKey ? userConfig.deviceApiKey } + { key: 'deviceApiKey', value: userConfig.deviceApiKey } { key: 'username', value: userConfig.username } { key: 'userId', value: userConfig.userId } { key: 'version', value: utils.supervisorVersion } @@ -202,6 +203,8 @@ bootstrapper.done = new Promise (resolve) -> delete userConfig.apiKey else userConfig.apiKey = userConfig.deviceApiKey + utils.setConfig('deviceApiKey', userConfig.deviceApiKey) + .then -> utils.setConfig('apiKey', userConfig.deviceApiKey) .then -> writeAndSyncFile(configPath, JSON.stringify(userConfig)) diff --git a/src/utils.coffee b/src/utils.coffee index 91701547..bebdf5b1 100644 --- a/src/utils.coffee +++ b/src/utils.coffee @@ -155,7 +155,7 @@ exports.setConfig = (key, value = null) -> .then (n) -> knex('config').insert({ key, value }) if n == 0 -exports.extendEnvVars = (env, uuid, appId, appName, commit) -> +exports.extendEnvVars = (env, uuid, apiKey, appId, appName, commit) -> host = '127.0.0.1' newEnv = RESIN_APP_ID: appId.toString() @@ -170,7 +170,7 @@ exports.extendEnvVars = (env, uuid, appId, appName, commit) -> RESIN_SUPERVISOR_PORT: config.listenPort RESIN_SUPERVISOR_API_KEY: exports.getOrGenerateSecret('api') RESIN_SUPERVISOR_VERSION: exports.supervisorVersion - RESIN_API_KEY: getConfig('apiKey') + RESIN_API_KEY: apiKey RESIN: '1' USER: 'root' if env? From 3f198fc6aa2fb78b7ea067c39dc387e4279fa96b Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 23 Oct 2017 17:28:36 -0700 Subject: [PATCH 2/4] Improve the check for when the device has been provisioned but the supervisor doesn't have knowledge of it in its local state This change improves the check for the DuplicateUuidError that can happen if a device has been provisioned but the API's response hasn't been persisted - the error message returned from the API has been known to have a few variations (usually an extra dot at the end), so we now use _.startsWith instead of checking for equal strings to make the supervisor still work under these variations. Change-Type: patch Signed-off-by: Pablo Carranza Velez --- src/bootstrap.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap.coffee b/src/bootstrap.coffee index 758a5c3a..c9d7ae0c 100644 --- a/src/bootstrap.coffee +++ b/src/bootstrap.coffee @@ -16,7 +16,7 @@ semverRegex = require('semver-regex') userConfig = {} -DuplicateUuidError = message: '"uuid" must be unique.' +DuplicateUuidError = (err) -> _.startsWith(err.message, '"uuid" must be unique') exports.ExchangeKeyError = class ExchangeKeyError extends TypedError bootstrapper = {} From a87c6682a21a319c8eef71cac3e3daafe2503872 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 23 Oct 2017 09:44:59 -0700 Subject: [PATCH 3/4] Ensure preloaded apps are properly loaded by setting their internal markedForDeletion to false, and run apps that have it set to null Currently preloaded apps don't run because their markedForDeletion field in the database is null. In this commit we set it to false, and we also change the startup check to also run any apps that have markedForDeletion as null (which should now never happen, but is still good as a backup plan in case something else fails and to avoid regressions). Change-Type: patch Signed-off-by: Pablo Carranza Velez --- src/application.coffee | 2 +- src/bootstrap.coffee | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/application.coffee b/src/application.coffee index 8f208e9a..b8b83ff5 100644 --- a/src/application.coffee +++ b/src/application.coffee @@ -882,7 +882,7 @@ application.initialize = -> listenToEvents() getAndApplyDeviceConfig() .then -> - knex('app').whereNot(markedForDeletion: true).select() + knex('app').whereNot(markedForDeletion: true).orWhereNull('markedForDeletion').select() .map (app) -> unlockAndStart(app) if !application.localMode and !device.shuttingDown .catch (error) -> diff --git a/src/bootstrap.coffee b/src/bootstrap.coffee index c9d7ae0c..fffd3984 100644 --- a/src/bootstrap.coffee +++ b/src/bootstrap.coffee @@ -43,6 +43,7 @@ loadPreloadedApps = -> utils.extendEnvVars(app.env, userConfig.uuid, userConfig.deviceApiKey, app.appId, app.name, app.commit) .then (extendedEnv) -> app.env = JSON.stringify(extendedEnv) + app.markedForDeletion = false _.merge(devConfig, app.config) app.config = JSON.stringify(app.config) knex('app').insert(app) From a5ee786cde09dda469d6b90037042af984bf7e35 Mon Sep 17 00:00:00 2001 From: "resin-io-versionbot[bot]" Date: Tue, 24 Oct 2017 05:07:25 +0000 Subject: [PATCH 4/4] v6.3.6 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ddd68fa..d49318b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ 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.3.6 - 2017-10-24 + +* Ensure preloaded apps are properly loaded by setting their internal markedForDeletion to false, and run apps that have it set to null #510 [Pablo Carranza Velez] +* Improve the check for when the device has been provisioned but the supervisor doesn't have knowledge of it in its local state #510 [Pablo Carranza Velez] +* Ensure preloaded apps get the deviceApiKey in the env vars, and apps never get the provisioning key, and improve detection of cases when the device has been pre-provisioned #510 [Pablo Carranza Velez] + ## v6.3.5 - 2017-10-19 * Update docker-toolbelt to fix applying deltas on overlay2 with huge images #509 [Akis Kesoglou] diff --git a/package.json b/package.json index 4328e2de..7c234b45 100644 --- a/package.json +++ b/package.json @@ -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.3.5", + "version": "6.3.6", "license": "Apache-2.0", "repository": { "type": "git",