mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-05-28 21:34:19 +00:00
fix: Store and retrieve device config without namespaces
This avoids issues on provisioning where the current state (esp. config.txt) that we want to save is retrieved without a RESIN_ or BALENA_ prefix, causing those values to be lost. Change-type: patch Signed-off-by: Pablo Carranza Velez <pablo@balena.io>
This commit is contained in:
parent
cb31474d7a
commit
b3860b2b70
@ -65,7 +65,7 @@ function filterNamespaceFromConfig(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterAndFormatConfigKeys(
|
export function formatConfigKeys(
|
||||||
configBackend: DeviceConfigBackend | null,
|
configBackend: DeviceConfigBackend | null,
|
||||||
allowedKeys: string[],
|
allowedKeys: string[],
|
||||||
conf: { [key: string]: any },
|
conf: { [key: string]: any },
|
||||||
@ -76,7 +76,10 @@ export function filterAndFormatConfigKeys(
|
|||||||
const legacyNamespaceRegex = /^RESIN_(.*)/;
|
const legacyNamespaceRegex = /^RESIN_(.*)/;
|
||||||
const confFromNamespace = filterNamespaceFromConfig(namespaceRegex, conf);
|
const confFromNamespace = filterNamespaceFromConfig(namespaceRegex, conf);
|
||||||
const confFromLegacyNamespace = filterNamespaceFromConfig(legacyNamespaceRegex, conf);
|
const confFromLegacyNamespace = filterNamespaceFromConfig(legacyNamespaceRegex, conf);
|
||||||
const confWithoutNamespace = _.defaults(confFromNamespace, confFromLegacyNamespace);
|
const noNamespaceConf = _.pickBy(conf, (_v,k) => {
|
||||||
|
return !_.startsWith(k, 'RESIN_') && !_.startsWith(k, 'BALENA_');
|
||||||
|
});
|
||||||
|
const confWithoutNamespace = _.defaults(confFromNamespace, confFromLegacyNamespace, noNamespaceConf);
|
||||||
|
|
||||||
return _.pickBy(confWithoutNamespace, (_v, k) => {
|
return _.pickBy(confWithoutNamespace, (_v, k) => {
|
||||||
return _.includes(allowedKeys, k) || (isConfigType && configBackend!.isBootConfigVar(k));
|
return _.includes(allowedKeys, k) || (isConfigType && configBackend!.isBootConfigVar(k));
|
||||||
|
@ -67,20 +67,17 @@ module.exports = class DeviceConfig
|
|||||||
|
|
||||||
setTarget: (target, trx) =>
|
setTarget: (target, trx) =>
|
||||||
db = trx ? @db.models.bind(@db)
|
db = trx ? @db.models.bind(@db)
|
||||||
confToUpdate = {
|
@formatConfigKeys(target)
|
||||||
targetValues: JSON.stringify(target)
|
.then (formattedTarget) ->
|
||||||
}
|
confToUpdate = {
|
||||||
db('deviceConfig').update(confToUpdate)
|
targetValues: JSON.stringify(formattedTarget)
|
||||||
|
}
|
||||||
|
db('deviceConfig').update(confToUpdate)
|
||||||
|
|
||||||
getTarget: ({ initial = false } = {}) =>
|
getTarget: ({ initial = false } = {}) =>
|
||||||
@db.models('deviceConfig').select('targetValues')
|
@db.models('deviceConfig').select('targetValues')
|
||||||
.then ([ devConfig ]) =>
|
.then ([ devConfig ]) =>
|
||||||
return Promise.all [
|
conf = JSON.parse(devConfig.targetValues)
|
||||||
JSON.parse(devConfig.targetValues)
|
|
||||||
@getConfigBackend()
|
|
||||||
]
|
|
||||||
.then ([ conf, configBackend ]) =>
|
|
||||||
conf = configUtils.filterAndFormatConfigKeys(configBackend, @validKeys, conf)
|
|
||||||
if initial or !conf.SUPERVISOR_VPN_CONTROL?
|
if initial or !conf.SUPERVISOR_VPN_CONTROL?
|
||||||
conf.SUPERVISOR_VPN_CONTROL = 'true'
|
conf.SUPERVISOR_VPN_CONTROL = 'true'
|
||||||
for own k, { envVarName, defaultValue } of @configKeys
|
for own k, { envVarName, defaultValue } of @configKeys
|
||||||
@ -105,10 +102,10 @@ module.exports = class DeviceConfig
|
|||||||
return _.assign(currentConf, bootConfig)
|
return _.assign(currentConf, bootConfig)
|
||||||
)
|
)
|
||||||
|
|
||||||
filterAndFormatConfigKeys: (conf) =>
|
formatConfigKeys: (conf) =>
|
||||||
@getConfigBackend()
|
@getConfigBackend()
|
||||||
.then (configBackend) =>
|
.then (configBackend) =>
|
||||||
configUtils.filterAndFormatConfigKeys(configBackend, @validKeys, conf)
|
configUtils.formatConfigKeys(configBackend, @validKeys, conf)
|
||||||
|
|
||||||
getDefaults: =>
|
getDefaults: =>
|
||||||
Promise.try =>
|
Promise.try =>
|
||||||
|
@ -319,13 +319,12 @@ module.exports = class DeviceState extends EventEmitter
|
|||||||
@emitAsync('change')
|
@emitAsync('change')
|
||||||
|
|
||||||
_convertLegacyAppsJson: (appsArray) =>
|
_convertLegacyAppsJson: (appsArray) =>
|
||||||
deviceConf = _.reduce(appsArray, (conf, app) =>
|
Promise.try =>
|
||||||
return _.merge({}, conf, app.config)
|
deviceConf = _.reduce(appsArray, (conf, app) =>
|
||||||
, {})
|
return _.merge({}, conf, app.config)
|
||||||
apps = _.keyBy(_.map(appsArray, singleToMulticontainerApp), 'appId')
|
, {})
|
||||||
@deviceConfig.filterAndFormatConfigKeys(deviceConf)
|
apps = _.keyBy(_.map(appsArray, singleToMulticontainerApp), 'appId')
|
||||||
.then (filteredDeviceConf)->
|
return { apps, config: deviceConf }
|
||||||
return { apps, config: filteredDeviceConf }
|
|
||||||
|
|
||||||
loadTargetFromFile: (appsPath) ->
|
loadTargetFromFile: (appsPath) ->
|
||||||
console.log('Attempting to load preloaded apps...')
|
console.log('Attempting to load preloaded apps...')
|
||||||
@ -366,11 +365,13 @@ module.exports = class DeviceState extends EventEmitter
|
|||||||
.then =>
|
.then =>
|
||||||
@deviceConfig.getCurrent()
|
@deviceConfig.getCurrent()
|
||||||
.then (deviceConf) =>
|
.then (deviceConf) =>
|
||||||
_.defaults(stateFromFile.config, deviceConf)
|
@deviceConfig.formatConfigKeys(stateFromFile.config)
|
||||||
stateFromFile.name ?= ''
|
.then (formattedConf) =>
|
||||||
@setTarget({
|
stateFromFile.config = _.defaults(formattedConf, deviceConf)
|
||||||
local: stateFromFile
|
stateFromFile.name ?= ''
|
||||||
})
|
@setTarget({
|
||||||
|
local: stateFromFile
|
||||||
|
})
|
||||||
.then =>
|
.then =>
|
||||||
console.log('Preloading complete')
|
console.log('Preloading complete')
|
||||||
if stateFromFile.pinDevice
|
if stateFromFile.pinDevice
|
||||||
|
20
src/migrations/M00000.js
Normal file
20
src/migrations/M00000.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
// We take legacy deviceConfig targets and store them without the RESIN_ prefix
|
||||||
|
// (we also strip the BALENA_ prefix for completeness, even though no supervisors
|
||||||
|
// using this prefix made it to production)
|
||||||
|
exports.up = function (knex, Promise) {
|
||||||
|
return knex('deviceConfig').select('targetValues')
|
||||||
|
.then((devConfigs) => {
|
||||||
|
const devConfig = devConfigs[0];
|
||||||
|
const targetValues = JSON.parse(devConfig.targetValues);
|
||||||
|
const filteredTargetValues = _.mapKeys( (_v, k) => {
|
||||||
|
return k.replace(/^(?:RESIN|BALENA)_(.*)/, '$1');
|
||||||
|
});
|
||||||
|
return knex('deviceConfig').update({ targetValues: JSON.stringify(filteredTargetValues) });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = function (knex, Promise) {
|
||||||
|
return Promise.reject(new Error('Not Implemented'));
|
||||||
|
}
|
@ -149,7 +149,7 @@ describe 'DeviceConfig', ->
|
|||||||
@fakeLogger.logSystemMessage.reset()
|
@fakeLogger.logSystemMessage.reset()
|
||||||
|
|
||||||
it 'accepts RESIN_ and BALENA_ variables', ->
|
it 'accepts RESIN_ and BALENA_ variables', ->
|
||||||
@deviceConfig.filterAndFormatConfigKeys({
|
@deviceConfig.formatConfigKeys({
|
||||||
FOO: 'bar',
|
FOO: 'bar',
|
||||||
BAR: 'baz',
|
BAR: 'baz',
|
||||||
RESIN_HOST_CONFIG_foo: 'foobaz',
|
RESIN_HOST_CONFIG_foo: 'foobaz',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user