From 7ea49bf4fb317346badf141e6eea8910e599c837 Mon Sep 17 00:00:00 2001 From: Miguel Casqueira Date: Tue, 18 Aug 2020 14:11:36 -0400 Subject: [PATCH] Preventing removing all configurations if device has no backends Closes: #1437 Change-type: patch Signed-off-by: Miguel Casqueira --- src/api-binder.ts | 4 +-- src/config/backends/index.ts | 8 +++++- src/config/utils.ts | 34 ++----------------------- src/device-config.ts | 48 ++++++++++++++++++++++------------- src/device-state/preload.ts | 4 +-- test/13-device-config.spec.ts | 25 ++++++++++-------- test/17-config-utils.spec.ts | 23 ----------------- 7 files changed, 55 insertions(+), 91 deletions(-) diff --git a/src/api-binder.ts b/src/api-binder.ts index 93c9272d..b9de9359 100644 --- a/src/api-binder.ts +++ b/src/api-binder.ts @@ -447,9 +447,7 @@ export class APIBinder { const defaultConfig = deviceConfig.getDefaults(); const currentState = await this.deviceState.getCurrentForComparison(); - const targetConfig = await deviceConfig.formatConfigKeys( - targetConfigUnformatted, - ); + const targetConfig = deviceConfig.formatConfigKeys(targetConfigUnformatted); if (!currentState.local.config) { throw new InternalInconsistencyError( diff --git a/src/config/backends/index.ts b/src/config/backends/index.ts index aa244467..f7f2a29e 100644 --- a/src/config/backends/index.ts +++ b/src/config/backends/index.ts @@ -1,11 +1,17 @@ +import * as _ from 'lodash'; + import { Extlinux } from './extlinux'; import { ExtraUEnv } from './extra-uEnv'; import { ConfigTxt } from './config-txt'; import { ConfigFs } from './config-fs'; -export default [ +export const allBackends = [ new Extlinux(), new ExtraUEnv(), new ConfigTxt(), new ConfigFs(), ]; + +export function matchesAnyBootConfig(envVar: string): boolean { + return allBackends.some((a) => a.isBootConfigVar(envVar)); +} diff --git a/src/config/utils.ts b/src/config/utils.ts index 9359403a..2eb23cf5 100644 --- a/src/config/utils.ts +++ b/src/config/utils.ts @@ -5,7 +5,7 @@ import * as config from '../config'; import * as constants from '../lib/constants'; import { getMetaOSRelease } from '../lib/os-release'; import { EnvVarObject } from '../lib/types'; -import Backends from './backends'; +import { allBackends as Backends } from './backends'; import { ConfigOptions, ConfigBackend } from './backends/backend'; export async function getSupportedBackends(): Promise { @@ -51,37 +51,7 @@ export function bootConfigToEnv( .value(); } -export function formatConfigKeys( - configBackend: ConfigBackend | null, - allowedKeys: string[], - conf: { [key: string]: any }, -): { [key: string]: any } { - const isConfigType = configBackend != null; - const namespaceRegex = /^BALENA_(.*)/; - const legacyNamespaceRegex = /^RESIN_(.*)/; - const confFromNamespace = filterNamespaceFromConfig(namespaceRegex, conf); - const confFromLegacyNamespace = filterNamespaceFromConfig( - legacyNamespaceRegex, - conf, - ); - 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 ( - _.includes(allowedKeys, k) || - (isConfigType && configBackend!.isBootConfigVar(k)) - ); - }); -} - -function filterNamespaceFromConfig( +export function filterNamespaceFromConfig( namespace: RegExp, conf: { [key: string]: any }, ): { [key: string]: any } { diff --git a/src/device-config.ts b/src/device-config.ts index 9ba18fe2..d34325bf 100644 --- a/src/device-config.ts +++ b/src/device-config.ts @@ -2,17 +2,17 @@ import * as _ from 'lodash'; import { inspect } from 'util'; import * as config from './config'; -import { SchemaTypeKey } from './config/schema-type'; import * as db from './db'; import * as logger from './logger'; - -import { ConfigOptions, ConfigBackend } from './config/backends/backend'; -import * as configUtils from './config/utils'; import * as dbus from './lib/dbus'; -import { UnitNotLoadedError } from './lib/errors'; import { EnvVarObject } from './lib/types'; +import { UnitNotLoadedError } from './lib/errors'; import { checkInt, checkTruthy } from './lib/validation'; import { DeviceStatus } from './types/state'; +import * as configUtils from './config/utils'; +import { SchemaTypeKey } from './config/schema-type'; +import { matchesAnyBootConfig } from './config/backends'; +import { ConfigOptions, ConfigBackend } from './config/backends/backend'; const vpnServiceName = 'openvpn'; @@ -229,7 +229,7 @@ export async function setTarget( ): Promise { const $db = trx ?? db.models.bind(db); - const formatted = await formatConfigKeys(target); + const formatted = formatConfigKeys(target); // check for legacy keys if (formatted['OVERRIDE_LOCK'] != null) { formatted['SUPERVISOR_OVERRIDE_LOCK'] = formatted['OVERRIDE_LOCK']; @@ -298,18 +298,30 @@ export async function getCurrent(): Promise> { return currentConf; } -export async function formatConfigKeys( - conf: Dictionary, -): Promise> { - const backends = await getConfigBackends(); - const formattedKeys: Dictionary = {}; - for (const backend of backends) { - _.assign( - formattedKeys, - configUtils.formatConfigKeys(backend, validKeys, conf), - ); - } - return formattedKeys; +export function formatConfigKeys(conf: { + [key: string]: any; +}): Dictionary { + const namespaceRegex = /^BALENA_(.*)/; + const legacyNamespaceRegex = /^RESIN_(.*)/; + const confFromNamespace = configUtils.filterNamespaceFromConfig( + namespaceRegex, + conf, + ); + const confFromLegacyNamespace = configUtils.filterNamespaceFromConfig( + legacyNamespaceRegex, + conf, + ); + 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 _.includes(validKeys, k) || matchesAnyBootConfig(k); + }); } export function getDefaults() { diff --git a/src/device-state/preload.ts b/src/device-state/preload.ts index 9cdc6007..29240e14 100644 --- a/src/device-state/preload.ts +++ b/src/device-state/preload.ts @@ -80,9 +80,7 @@ export async function loadTargetFromFile( } const deviceConf = await deviceConfig.getCurrent(); - const formattedConf = await deviceConfig.formatConfigKeys( - preloadState.config, - ); + const formattedConf = deviceConfig.formatConfigKeys(preloadState.config); preloadState.config = { ...formattedConf, ...deviceConf }; const localState = { local: { name: '', ...preloadState }, diff --git a/test/13-device-config.spec.ts b/test/13-device-config.spec.ts index 4a70bc5f..4b83ea5d 100644 --- a/test/13-device-config.spec.ts +++ b/test/13-device-config.spec.ts @@ -168,19 +168,22 @@ describe('Device Backend Config', () => { it('accepts RESIN_ and BALENA_ variables', async () => { return expect( deviceConfig.formatConfigKeys({ - FOO: 'bar', - BAR: 'baz', - RESIN_HOST_CONFIG_foo: 'foobaz', - BALENA_HOST_CONFIG_foo: 'foobar', - RESIN_HOST_CONFIG_other: 'val', - BALENA_HOST_CONFIG_baz: 'bad', - BALENA_SUPERVISOR_POLL_INTERVAL: '100', + FOO: 'bar', // should be removed + BAR: 'baz', // should be removed + RESIN_SUPERVISOR_LOCAL_MODE: 'false', // any device + BALENA_SUPERVISOR_OVERRIDE_LOCK: 'false', // any device + BALENA_SUPERVISOR_POLL_INTERVAL: '100', // any device + RESIN_HOST_CONFIG_dtparam: 'i2c_arm=on","spi=on","audio=on', // config.txt backend + RESIN_HOST_CONFIGFS_ssdt: 'spidev1.0', // configfs backend + BALENA_HOST_EXTLINUX_isolcpus: '1,2,3', // extlinux backend }), - ).to.eventually.deep.equal({ - HOST_CONFIG_foo: 'foobar', - HOST_CONFIG_other: 'val', - HOST_CONFIG_baz: 'bad', + ).to.deep.equal({ + SUPERVISOR_LOCAL_MODE: 'false', + SUPERVISOR_OVERRIDE_LOCK: 'false', SUPERVISOR_POLL_INTERVAL: '100', + HOST_CONFIG_dtparam: 'i2c_arm=on","spi=on","audio=on', + HOST_CONFIGFS_ssdt: 'spidev1.0', + HOST_EXTLINUX_isolcpus: '1,2,3', }); }); diff --git a/test/17-config-utils.spec.ts b/test/17-config-utils.spec.ts index 8537f7b2..313c0979 100644 --- a/test/17-config-utils.spec.ts +++ b/test/17-config-utils.spec.ts @@ -3,7 +3,6 @@ import * as _ from 'lodash'; import { expect } from './lib/chai-config'; import * as config from '../src/config'; -import { validKeys } from '../src/device-config'; import * as configUtils from '../src/config/utils'; import { ExtraUEnv } from '../src/config/backends/extra-uEnv'; import { Extlinux } from '../src/config/backends/extlinux'; @@ -40,28 +39,6 @@ describe('Config Utilities', () => { ).to.deep.equal(configObj.envVars); }); }); - - it('formats keys from config', () => { - // Pick any backend to use for test - // note: some of the values used will be specific to this backend - const backend = BACKENDS['extlinux']; - const formattedKeys = configUtils.formatConfigKeys(backend, validKeys, { - FOO: 'bar', - BAR: 'baz', - RESIN_HOST_CONFIG_foo: 'foobaz', - BALENA_HOST_CONFIG_foo: 'foobar', - RESIN_HOST_CONFIG_other: 'val', - BALENA_HOST_CONFIG_baz: 'bad', - BALENA_SUPERVISOR_POLL_INTERVAL: '100', // any device - BALENA_HOST_EXTLINUX_isolcpus: '1,2,3', // specific to backend - RESIN_HOST_EXTLINUX_fdt: '/boot/mycustomdtb.dtb', // specific to backend - }); - expect(formattedKeys).to.deep.equal({ - HOST_EXTLINUX_isolcpus: '1,2,3', - HOST_EXTLINUX_fdt: '/boot/mycustomdtb.dtb', - SUPERVISOR_POLL_INTERVAL: '100', - }); - }); }); const BACKENDS: Record = {