From ff404456b3c3edaf33fe2199387db47d9be03503 Mon Sep 17 00:00:00 2001 From: Miguel Casqueira Date: Fri, 7 Aug 2020 13:34:32 -0400 Subject: [PATCH 1/2] Refactor configurable backend class names Change-type: patch Signed-off-by: Miguel Casqueira --- src/config/backends/backend.ts | 4 +- src/config/backends/config-fs.ts | 16 ++--- .../{raspberry-pi.ts => config-txt.ts} | 29 +++++----- src/config/backends/extlinux.ts | 58 ++++++------------- src/config/backends/extra-uEnv.ts | 51 ++++++++-------- src/config/utils.ts | 26 ++++----- src/device-config.ts | 10 ++-- test/05-device-state.spec.ts | 4 +- test/13-device-config.spec.ts | 34 +++++------ test/17-config-utils.spec.ts | 6 +- test/27-extlinux-config.spec.ts | 8 +-- test/32-extra-uenv-config.spec.ts | 12 ++-- 12 files changed, 113 insertions(+), 145 deletions(-) rename src/config/backends/{raspberry-pi.ts => config-txt.ts} (80%) diff --git a/src/config/backends/backend.ts b/src/config/backends/backend.ts index 50f23449..30db26ad 100644 --- a/src/config/backends/backend.ts +++ b/src/config/backends/backend.ts @@ -21,7 +21,7 @@ export async function remountAndWriteAtomic( await writeFileAtomic(file, data); } -export abstract class DeviceConfigBackend { +export abstract class ConfigBackend { // Does this config backend support the given device type? public abstract async matches( deviceType: string, @@ -61,7 +61,7 @@ export abstract class DeviceConfigBackend { public abstract createConfigVarName(configName: string): string | null; // Allow a chosen config backend to be initialised - public async initialise(): Promise { + public async initialise(): Promise { return this; } } diff --git a/src/config/backends/config-fs.ts b/src/config/backends/config-fs.ts index 834a1110..51bd1d17 100644 --- a/src/config/backends/config-fs.ts +++ b/src/config/backends/config-fs.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import { ConfigOptions, - DeviceConfigBackend, + ConfigBackend, bootMountPoint, remountAndWriteAtomic, } from './backend'; @@ -21,7 +21,7 @@ import log from '../../lib/supervisor-console'; type ConfigfsConfig = Dictionary; -export class ConfigfsConfigBackend extends DeviceConfigBackend { +export class ConfigFs extends ConfigBackend { private readonly SystemAmlFiles = path.join( constants.rootMountPoint, 'boot/acpi-tables', @@ -129,7 +129,7 @@ export class ConfigfsConfigBackend extends DeviceConfigBackend { } } - public async initialise(): Promise { + public async initialise(): Promise { try { await super.initialise(); @@ -158,7 +158,7 @@ export class ConfigfsConfigBackend extends DeviceConfigBackend { } public async matches(deviceType: string): Promise { - return ConfigfsConfigBackend.SupportedDeviceTypes.includes(deviceType); + return ConfigFs.SupportedDeviceTypes.includes(deviceType); } public async getBootConfig(): Promise { @@ -195,15 +195,11 @@ export class ConfigfsConfigBackend extends DeviceConfigBackend { } public isSupportedConfig(name: string): boolean { - return ConfigfsConfigBackend.BootConfigVars.includes( - this.stripPrefix(name), - ); + return ConfigFs.BootConfigVars.includes(this.stripPrefix(name)); } public isBootConfigVar(name: string): boolean { - return ConfigfsConfigBackend.BootConfigVars.includes( - this.stripPrefix(name), - ); + return ConfigFs.BootConfigVars.includes(this.stripPrefix(name)); } public processConfigVarName(name: string): string { diff --git a/src/config/backends/raspberry-pi.ts b/src/config/backends/config-txt.ts similarity index 80% rename from src/config/backends/raspberry-pi.ts rename to src/config/backends/config-txt.ts index fafca606..c519c558 100644 --- a/src/config/backends/raspberry-pi.ts +++ b/src/config/backends/config-txt.ts @@ -3,7 +3,7 @@ import { fs } from 'mz'; import { ConfigOptions, - DeviceConfigBackend, + ConfigBackend, bootMountPoint, remountAndWriteAtomic, } from './backend'; @@ -21,12 +21,12 @@ import log from '../../lib/supervisor-console'; * - {BALENA|RESIN}_HOST_CONFIG_gpio = value | "value" | "value1","value2" */ -export class RPiConfigBackend extends DeviceConfigBackend { +export class ConfigTxt extends ConfigBackend { private static bootConfigVarPrefix = `${constants.hostConfigVarPrefix}CONFIG_`; private static bootConfigPath = `${bootMountPoint}/config.txt`; public static bootConfigVarRegex = new RegExp( - '(?:' + _.escapeRegExp(RPiConfigBackend.bootConfigVarPrefix) + ')(.+)', + '(?:' + _.escapeRegExp(ConfigTxt.bootConfigVarPrefix) + ')(.+)', ); private static arrayConfigKeys = [ @@ -57,13 +57,10 @@ export class RPiConfigBackend extends DeviceConfigBackend { public async getBootConfig(): Promise { let configContents = ''; - if (await fs.exists(RPiConfigBackend.bootConfigPath)) { - configContents = await fs.readFile( - RPiConfigBackend.bootConfigPath, - 'utf-8', - ); + if (await fs.exists(ConfigTxt.bootConfigPath)) { + configContents = await fs.readFile(ConfigTxt.bootConfigPath, 'utf-8'); } else { - await fs.writeFile(RPiConfigBackend.bootConfigPath, ''); + await fs.writeFile(ConfigTxt.bootConfigPath, ''); } const conf: ConfigOptions = {}; @@ -78,7 +75,7 @@ export class RPiConfigBackend extends DeviceConfigBackend { let keyValue = /^([^=]+)=(.*)$/.exec(configStr); if (keyValue != null) { const [, key, value] = keyValue; - if (!RPiConfigBackend.arrayConfigKeys.includes(key)) { + if (!ConfigTxt.arrayConfigKeys.includes(key)) { conf[key] = value; } else { if (conf[key] == null) { @@ -119,23 +116,23 @@ export class RPiConfigBackend extends DeviceConfigBackend { } }); const confStr = `${confStatements.join('\n')}\n`; - await remountAndWriteAtomic(RPiConfigBackend.bootConfigPath, confStr); + await remountAndWriteAtomic(ConfigTxt.bootConfigPath, confStr); } public isSupportedConfig(configName: string): boolean { - return !RPiConfigBackend.forbiddenConfigKeys.includes(configName); + return !ConfigTxt.forbiddenConfigKeys.includes(configName); } public isBootConfigVar(envVar: string): boolean { - return envVar.startsWith(RPiConfigBackend.bootConfigVarPrefix); + return envVar.startsWith(ConfigTxt.bootConfigVarPrefix); } public processConfigVarName(envVar: string): string { - return envVar.replace(RPiConfigBackend.bootConfigVarRegex, '$1'); + return envVar.replace(ConfigTxt.bootConfigVarRegex, '$1'); } public processConfigVarValue(key: string, value: string): string | string[] { - if (RPiConfigBackend.arrayConfigKeys.includes(key)) { + if (ConfigTxt.arrayConfigKeys.includes(key)) { if (!value.startsWith('"')) { return [value]; } else { @@ -146,6 +143,6 @@ export class RPiConfigBackend extends DeviceConfigBackend { } public createConfigVarName(configName: string): string { - return RPiConfigBackend.bootConfigVarPrefix + configName; + return ConfigTxt.bootConfigVarPrefix + configName; } } diff --git a/src/config/backends/extlinux.ts b/src/config/backends/extlinux.ts index 21d94139..a19bc6cc 100644 --- a/src/config/backends/extlinux.ts +++ b/src/config/backends/extlinux.ts @@ -4,7 +4,7 @@ import * as semver from 'semver'; import { ConfigOptions, - DeviceConfigBackend, + ConfigBackend, bootMountPoint, remountAndWriteAtomic, } from './backend'; @@ -29,7 +29,7 @@ const EXTLINUX_READONLY = '2.47.0'; * - {BALENA|RESIN}_HOST_EXTLINUX_fdt = value | "value" */ -export class ExtlinuxConfigBackend extends DeviceConfigBackend { +export class Extlinux extends ConfigBackend { private static bootConfigVarPrefix = `${constants.hostConfigVarPrefix}EXTLINUX_`; private static bootConfigPath = `${bootMountPoint}/extlinux/extlinux.conf`; private static supportedConfigValues = ['isolcpus', 'fdt']; @@ -38,13 +38,11 @@ export class ExtlinuxConfigBackend extends DeviceConfigBackend { private fdtDirective = new FDTDirective(); private appendDirective = new AppendDirective( // Pass in list of supportedConfigValues that APPEND can have - ExtlinuxConfigBackend.supportedConfigValues.filter( - (v) => !this.isDirective(v), - ), + Extlinux.supportedConfigValues.filter((v) => !this.isDirective(v)), ); public static bootConfigVarRegex = new RegExp( - '(?:' + _.escapeRegExp(ExtlinuxConfigBackend.bootConfigVarPrefix) + ')(.+)', + '(?:' + _.escapeRegExp(Extlinux.bootConfigVarPrefix) + ')(.+)', ); public async matches( @@ -63,10 +61,7 @@ export class ExtlinuxConfigBackend extends DeviceConfigBackend { let confContents: string; try { - confContents = await fs.readFile( - ExtlinuxConfigBackend.bootConfigPath, - 'utf-8', - ); + confContents = await fs.readFile(Extlinux.bootConfigPath, 'utf-8'); } catch { // In the rare case where the user might have deleted extlinux conf file between linux boot and supervisor boot // We do not have any backup to fallback too; warn the user of a possible brick @@ -76,18 +71,13 @@ export class ExtlinuxConfigBackend extends DeviceConfigBackend { } // Parse ExtlinuxFile from file contents - const parsedBootFile = ExtlinuxConfigBackend.parseExtlinuxFile( - confContents, - ); + const parsedBootFile = Extlinux.parseExtlinuxFile(confContents); // Get default label to know which label entry to parse - const defaultLabel = ExtlinuxConfigBackend.findDefaultLabel(parsedBootFile); + const defaultLabel = Extlinux.findDefaultLabel(parsedBootFile); // Get the label entry we will parse - const labelEntry = ExtlinuxConfigBackend.getLabelEntry( - parsedBootFile, - defaultLabel, - ); + const labelEntry = Extlinux.getLabelEntry(parsedBootFile, defaultLabel); // Parse APPEND directive and filter out unsupported values const appendConfig = _.pickBy( @@ -109,10 +99,7 @@ export class ExtlinuxConfigBackend extends DeviceConfigBackend { let confContents: string; try { - confContents = await fs.readFile( - ExtlinuxConfigBackend.bootConfigPath, - 'utf-8', - ); + confContents = await fs.readFile(Extlinux.bootConfigPath, 'utf-8'); } catch { // In the rare case where the user might have deleted extlinux conf file between linux boot and supervisor boot // We do not have any backup to fallback too; warn the user of a possible brick @@ -122,18 +109,13 @@ export class ExtlinuxConfigBackend extends DeviceConfigBackend { } // Parse ExtlinuxFile from file contents - const parsedBootFile = ExtlinuxConfigBackend.parseExtlinuxFile( - confContents, - ); + const parsedBootFile = Extlinux.parseExtlinuxFile(confContents); // Get default label to know which label entry to edit - const defaultLabel = ExtlinuxConfigBackend.findDefaultLabel(parsedBootFile); + const defaultLabel = Extlinux.findDefaultLabel(parsedBootFile); // Get the label entry we will edit - const defaultEntry = ExtlinuxConfigBackend.getLabelEntry( - parsedBootFile, - defaultLabel, - ); + const defaultEntry = Extlinux.getLabelEntry(parsedBootFile, defaultLabel); // Set `FDT` directive if a value is provided if (opts.fdt) { @@ -155,21 +137,21 @@ export class ExtlinuxConfigBackend extends DeviceConfigBackend { // Write new extlinux configuration return await remountAndWriteAtomic( - ExtlinuxConfigBackend.bootConfigPath, - ExtlinuxConfigBackend.extlinuxFileToString(parsedBootFile), + Extlinux.bootConfigPath, + Extlinux.extlinuxFileToString(parsedBootFile), ); } public isSupportedConfig(configName: string): boolean { - return ExtlinuxConfigBackend.supportedConfigValues.includes(configName); + return Extlinux.supportedConfigValues.includes(configName); } public isBootConfigVar(envVar: string): boolean { - return envVar.startsWith(ExtlinuxConfigBackend.bootConfigVarPrefix); + return envVar.startsWith(Extlinux.bootConfigVarPrefix); } public processConfigVarName(envVar: string): string { - return envVar.replace(ExtlinuxConfigBackend.bootConfigVarRegex, '$1'); + return envVar.replace(Extlinux.bootConfigVarRegex, '$1'); } public processConfigVarValue(_key: string, value: string): string { @@ -177,13 +159,11 @@ export class ExtlinuxConfigBackend extends DeviceConfigBackend { } public createConfigVarName(configName: string): string { - return `${ExtlinuxConfigBackend.bootConfigVarPrefix}${configName}`; + return `${Extlinux.bootConfigVarPrefix}${configName}`; } private isDirective(configName: string): boolean { - return ExtlinuxConfigBackend.supportedDirectives.includes( - configName.toUpperCase(), - ); + return Extlinux.supportedDirectives.includes(configName.toUpperCase()); } private static parseExtlinuxFile(confStr: string): ExtlinuxFile { diff --git a/src/config/backends/extra-uEnv.ts b/src/config/backends/extra-uEnv.ts index a80f6427..2d9affb1 100644 --- a/src/config/backends/extra-uEnv.ts +++ b/src/config/backends/extra-uEnv.ts @@ -3,7 +3,7 @@ import { fs } from 'mz'; import { ConfigOptions, - DeviceConfigBackend, + ConfigBackend, bootMountPoint, remountAndWriteAtomic, } from './backend'; @@ -40,7 +40,7 @@ const OPTION_REGEX = /^\s*(\w+)=(.*)$/; * - {BALENA|RESIN}_HOST_EXTLINUX_fdt = value | "value" */ -export class ExtraUEnvConfigBackend extends DeviceConfigBackend { +export class ExtraUEnv extends ConfigBackend { private static bootConfigVarPrefix = `${constants.hostConfigVarPrefix}EXTLINUX_`; private static bootConfigPath = `${bootMountPoint}/extra_uEnv.txt`; @@ -50,29 +50,27 @@ export class ExtraUEnvConfigBackend extends DeviceConfigBackend { }; private static supportedConfigs: Dictionary = { - fdt: ExtraUEnvConfigBackend.entries['custom_fdt_file'], - isolcpus: ExtraUEnvConfigBackend.entries['extra_os_cmdline'], + fdt: ExtraUEnv.entries['custom_fdt_file'], + isolcpus: ExtraUEnv.entries['extra_os_cmdline'], }; public static bootConfigVarRegex = new RegExp( - '(?:' + - _.escapeRegExp(ExtraUEnvConfigBackend.bootConfigVarPrefix) + - ')(.+)', + '(?:' + _.escapeRegExp(ExtraUEnv.bootConfigVarPrefix) + ')(.+)', ); public async matches(deviceType: string): Promise { return ( (deviceType === 'intel-nuc' || deviceType.startsWith('jetson')) && - (await fs.exists(ExtraUEnvConfigBackend.bootConfigPath)) + (await fs.exists(ExtraUEnv.bootConfigPath)) ); } public async getBootConfig(): Promise { // Get config contents at bootConfigPath - const confContents = await ExtraUEnvConfigBackend.readBootConfigPath(); + const confContents = await ExtraUEnv.readBootConfigPath(); // Parse ConfigOptions from bootConfigPath contents - const parsedConfigFile = ExtraUEnvConfigBackend.parseOptions(confContents); + const parsedConfigFile = ExtraUEnv.parseOptions(confContents); // Filter out unsupported values return _.pickBy(parsedConfigFile, (_value, key) => @@ -92,24 +90,21 @@ export class ExtraUEnvConfigBackend extends DeviceConfigBackend { // Write new extra_uEnv configuration return await remountAndWriteAtomic( - ExtraUEnvConfigBackend.bootConfigPath, - ExtraUEnvConfigBackend.configToString(supportedOptions), + ExtraUEnv.bootConfigPath, + ExtraUEnv.configToString(supportedOptions), ); } public isSupportedConfig(config: string): boolean { - return config in ExtraUEnvConfigBackend.supportedConfigs; + return config in ExtraUEnv.supportedConfigs; } public isBootConfigVar(envVar: string): boolean { - return envVar.startsWith(ExtraUEnvConfigBackend.bootConfigVarPrefix); + return envVar.startsWith(ExtraUEnv.bootConfigVarPrefix); } public processConfigVarName(envVar: string): string | null { - const name = envVar.replace( - ExtraUEnvConfigBackend.bootConfigVarRegex, - '$1', - ); + const name = envVar.replace(ExtraUEnv.bootConfigVarRegex, '$1'); if (name === envVar) { return null; } @@ -124,7 +119,7 @@ export class ExtraUEnvConfigBackend extends DeviceConfigBackend { if (configName === '') { return null; } - return `${ExtraUEnvConfigBackend.bootConfigVarPrefix}${configName}`; + return `${ExtraUEnv.bootConfigVarPrefix}${configName}`; } private static parseOptions(configFile: string): ConfigOptions { @@ -143,7 +138,7 @@ export class ExtraUEnvConfigBackend extends DeviceConfigBackend { } // Merge new option with existing options return { - ...ExtraUEnvConfigBackend.parseOption(optionValues), + ...ExtraUEnv.parseOption(optionValues), ...options, }; }, {}); @@ -152,13 +147,13 @@ export class ExtraUEnvConfigBackend extends DeviceConfigBackend { private static parseOption(optionArray: string[]): ConfigOptions { const [, KEY, VALUE] = optionArray; // Check if this key's value is a collection - if (ExtraUEnvConfigBackend.entries[KEY as EntryKey]?.collection) { + if (ExtraUEnv.entries[KEY as EntryKey]?.collection) { // Return split collection of options - return ExtraUEnvConfigBackend.parseOptionCollection(VALUE); + return ExtraUEnv.parseOptionCollection(VALUE); } // Find the option that belongs to this entry const optionKey = _.findKey( - ExtraUEnvConfigBackend.supportedConfigs, + ExtraUEnv.supportedConfigs, (config) => config.key === KEY, ); // Check if we found a corresponding option for this entry @@ -198,12 +193,12 @@ export class ExtraUEnvConfigBackend extends DeviceConfigBackend { private static async readBootConfigPath(): Promise { try { - return await fs.readFile(ExtraUEnvConfigBackend.bootConfigPath, 'utf-8'); + return await fs.readFile(ExtraUEnv.bootConfigPath, 'utf-8'); } catch { // In the rare case where the user might have deleted extra_uEnv conf file between linux boot and supervisor boot // We do not have any backup to fallback too; warn the user of a possible brick log.error( - `Unable to read extra_uEnv file at: ${ExtraUEnvConfigBackend.bootConfigPath}`, + `Unable to read extra_uEnv file at: ${ExtraUEnv.bootConfigPath}`, ); throw new ExtraUEnvError( 'Could not find extra_uEnv file. Device is possibly bricked', @@ -213,7 +208,7 @@ export class ExtraUEnvConfigBackend extends DeviceConfigBackend { private static configToString(configs: ConfigOptions): string { // Get Map of ConfigOptions object - const configMap = ExtraUEnvConfigBackend.configToMap(configs); + const configMap = ExtraUEnv.configToMap(configs); // Iterator over configMap and concat to configString let configString = ''; for (const [key, value] of configMap) { @@ -230,12 +225,12 @@ export class ExtraUEnvConfigBackend extends DeviceConfigBackend { const { key: ENTRY_KEY, collection: ENTRY_IS_COLLECTION, - } = ExtraUEnvConfigBackend.supportedConfigs[configKey]; + } = ExtraUEnv.supportedConfigs[configKey]; // Check if we have to build the value for the entry if (ENTRY_IS_COLLECTION) { return configMap.set( ENTRY_KEY, - ExtraUEnvConfigBackend.appendToCollection( + ExtraUEnv.appendToCollection( configMap.get(ENTRY_KEY), configKey, configValue, diff --git a/src/config/utils.ts b/src/config/utils.ts index e4c1555c..38bf179c 100644 --- a/src/config/utils.ts +++ b/src/config/utils.ts @@ -3,17 +3,17 @@ import * as _ from 'lodash'; import * as constants from '../lib/constants'; import { getMetaOSRelease } from '../lib/os-release'; import { EnvVarObject } from '../lib/types'; -import { ExtlinuxConfigBackend } from './backends/extlinux'; -import { ExtraUEnvConfigBackend } from './backends/extra-uEnv'; -import { RPiConfigBackend } from './backends/raspberry-pi'; -import { ConfigfsConfigBackend } from './backends/config-fs'; -import { ConfigOptions, DeviceConfigBackend } from './backends/backend'; +import { Extlinux } from './backends/extlinux'; +import { ExtraUEnv } from './backends/extra-uEnv'; +import { ConfigTxt } from './backends/config-txt'; +import { ConfigFs } from './backends/config-fs'; +import { ConfigOptions, ConfigBackend } from './backends/backend'; const configBackends = [ - new ExtlinuxConfigBackend(), - new ExtraUEnvConfigBackend(), - new RPiConfigBackend(), - new ConfigfsConfigBackend(), + new Extlinux(), + new ExtraUEnv(), + new ConfigTxt(), + new ConfigFs(), ]; export const initialiseConfigBackend = async (deviceType: string) => { @@ -26,7 +26,7 @@ export const initialiseConfigBackend = async (deviceType: string) => { async function getConfigBackend( deviceType: string, -): Promise { +): Promise { // Some backends are only supported by certain release versions so pass in metaRelease const metaRelease = await getMetaOSRelease(constants.hostOSVersionPath); let matched; @@ -39,7 +39,7 @@ async function getConfigBackend( } export function envToBootConfig( - configBackend: DeviceConfigBackend | null, + configBackend: ConfigBackend | null, env: EnvVarObject, ): ConfigOptions { if (configBackend == null) { @@ -56,7 +56,7 @@ export function envToBootConfig( } export function bootConfigToEnv( - configBackend: DeviceConfigBackend, + configBackend: ConfigBackend, config: ConfigOptions, ): EnvVarObject { return _(config) @@ -85,7 +85,7 @@ function filterNamespaceFromConfig( } export function formatConfigKeys( - configBackend: DeviceConfigBackend | null, + configBackend: ConfigBackend | null, allowedKeys: string[], conf: { [key: string]: any }, ): { [key: string]: any } { diff --git a/src/device-config.ts b/src/device-config.ts index 164b143d..0c159e37 100644 --- a/src/device-config.ts +++ b/src/device-config.ts @@ -6,7 +6,7 @@ import { SchemaTypeKey } from './config/schema-type'; import * as db from './db'; import * as logger from './logger'; -import { ConfigOptions, DeviceConfigBackend } from './config/backends/backend'; +import { ConfigOptions, ConfigBackend } from './config/backends/backend'; import * as configUtils from './config/utils'; import * as dbus from './lib/dbus'; import { UnitNotLoadedError } from './lib/errors'; @@ -108,7 +108,7 @@ const actionExecutors: DeviceActionExecutors = { }, }; -let configBackend: DeviceConfigBackend | null = null; +let configBackend: ConfigBackend | null = null; const configKeys: Dictionary = { appUpdatePollInterval: { @@ -314,7 +314,7 @@ export function resetRateLimits() { // Exported for tests export function bootConfigChangeRequired( - $configBackend: DeviceConfigBackend | null, + $configBackend: ConfigBackend | null, current: Dictionary, target: Dictionary, deviceType: string, @@ -492,7 +492,7 @@ export function isValidAction(action: string): boolean { } export async function getBootConfig( - backend: DeviceConfigBackend | null, + backend: ConfigBackend | null, ): Promise { if (backend == null) { return {}; @@ -503,7 +503,7 @@ export async function getBootConfig( // Exported for tests export async function setBootConfig( - backend: DeviceConfigBackend | null, + backend: ConfigBackend | null, target: Dictionary, ) { if (backend == null) { diff --git a/test/05-device-state.spec.ts b/test/05-device-state.spec.ts index c8b00524..43ba17ec 100644 --- a/test/05-device-state.spec.ts +++ b/test/05-device-state.spec.ts @@ -10,7 +10,7 @@ import Log from '../src/lib/supervisor-console'; import * as dockerUtils from '../src/lib/docker-utils'; import * as config from '../src/config'; import * as images from '../src/compose/images'; -import { RPiConfigBackend } from '../src/config/backends/raspberry-pi'; +import { ConfigTxt } from '../src/config/backends/config-txt'; import DeviceState from '../src/device-state'; import * as deviceConfig from '../src/device-config'; import { loadTargetFromFile } from '../src/device-state/preload'; @@ -254,7 +254,7 @@ describe('deviceState', () => { }; // @ts-expect-error Assigning to a RO property - deviceConfig.configBackend = new RPiConfigBackend(); + deviceConfig.configBackend = new ConfigTxt(); // @ts-expect-error Assigning to a RO property deviceConfig.getCurrent = async () => mockedInitialConfig; diff --git a/test/13-device-config.spec.ts b/test/13-device-config.spec.ts index f7221243..4ba83552 100644 --- a/test/13-device-config.spec.ts +++ b/test/13-device-config.spec.ts @@ -6,12 +6,12 @@ import { expect } from './lib/chai-config'; import * as deviceConfig from '../src/device-config'; import * as fsUtils from '../src/lib/fs-utils'; import * as logger from '../src/logger'; -import { ExtlinuxConfigBackend } from '../src/config/backends/extlinux'; -import { RPiConfigBackend } from '../src/config/backends/raspberry-pi'; +import { Extlinux } from '../src/config/backends/extlinux'; +import { ConfigTxt } from '../src/config/backends/config-txt'; import prepare = require('./lib/prepare'); -const extlinuxBackend = new ExtlinuxConfigBackend(); -const rpiConfigBackend = new RPiConfigBackend(); +const extlinuxBackend = new Extlinux(); +const configTxtBackend = new ConfigTxt(); describe('Device Backend Config', () => { let logSpy: SinonSpy; @@ -33,7 +33,7 @@ describe('Device Backend Config', () => { // Will try to parse /test/data/mnt/boot/config.txt await expect( // @ts-ignore accessing private value - deviceConfig.getBootConfig(rpiConfigBackend), + deviceConfig.getBootConfig(configTxtBackend), ).to.eventually.deep.equal({ HOST_CONFIG_dtparam: '"i2c_arm=on","spi=on","audio=on"', HOST_CONFIG_enable_uart: '1', @@ -54,7 +54,7 @@ describe('Device Backend Config', () => { await expect( // @ts-ignore accessing private value - deviceConfig.getBootConfig(rpiConfigBackend), + deviceConfig.getBootConfig(configTxtBackend), ).to.eventually.deep.equal({ HOST_CONFIG_initramfs: 'initramf.gz 0x00800000', HOST_CONFIG_dtparam: '"i2c=on","audio=on"', @@ -83,7 +83,7 @@ describe('Device Backend Config', () => { expect(() => // @ts-ignore accessing private value - deviceConfig.bootConfigChangeRequired(rpiConfigBackend, current, target), + deviceConfig.bootConfigChangeRequired(configTxtBackend, current, target), ).to.throw('Attempt to change blacklisted config value initramfs'); // Check if logs were called @@ -115,7 +115,7 @@ describe('Device Backend Config', () => { expect( // @ts-ignore accessing private value - deviceConfig.bootConfigChangeRequired(rpiConfigBackend, current, target), + deviceConfig.bootConfigChangeRequired(configTxtBackend, current, target), ).to.equal(false); expect(logSpy).to.not.be.called; }); @@ -140,11 +140,11 @@ describe('Device Backend Config', () => { expect( // @ts-ignore accessing private value - deviceConfig.bootConfigChangeRequired(rpiConfigBackend, current, target), + deviceConfig.bootConfigChangeRequired(configTxtBackend, current, target), ).to.equal(true); // @ts-ignore accessing private value - await deviceConfig.setBootConfig(rpiConfigBackend, target); + await deviceConfig.setBootConfig(configTxtBackend, target); expect(child_process.exec).to.be.calledOnce; expect(logSpy).to.be.calledTwice; expect(logSpy.getCall(1).args[2]).to.equal('Apply boot config success'); @@ -280,7 +280,7 @@ describe('Device Backend Config', () => { it('should not cause a config change when the cloud does not specify the balena-fin overlay', () => { expect( deviceConfig.bootConfigChangeRequired( - rpiConfigBackend, + configTxtBackend, { HOST_CONFIG_dtoverlay: '"test","balena-fin"' }, { HOST_CONFIG_dtoverlay: '"test"' }, 'fincm3', @@ -289,7 +289,7 @@ describe('Device Backend Config', () => { expect( deviceConfig.bootConfigChangeRequired( - rpiConfigBackend, + configTxtBackend, { HOST_CONFIG_dtoverlay: '"test","balena-fin"' }, { HOST_CONFIG_dtoverlay: 'test' }, 'fincm3', @@ -298,7 +298,7 @@ describe('Device Backend Config', () => { expect( deviceConfig.bootConfigChangeRequired( - rpiConfigBackend, + configTxtBackend, { HOST_CONFIG_dtoverlay: '"test","test2","balena-fin"' }, { HOST_CONFIG_dtoverlay: '"test","test2"' }, 'fincm3', @@ -341,7 +341,7 @@ describe('Device Backend Config', () => { it('should not cause a config change when the cloud does not specify the pi4 overlay', () => { expect( deviceConfig.bootConfigChangeRequired( - rpiConfigBackend, + configTxtBackend, { HOST_CONFIG_dtoverlay: '"test","vc4-fkms-v3d"' }, { HOST_CONFIG_dtoverlay: '"test"' }, 'raspberrypi4-64', @@ -349,7 +349,7 @@ describe('Device Backend Config', () => { ).to.equal(false); expect( deviceConfig.bootConfigChangeRequired( - rpiConfigBackend, + configTxtBackend, { HOST_CONFIG_dtoverlay: '"test","vc4-fkms-v3d"' }, { HOST_CONFIG_dtoverlay: 'test' }, 'raspberrypi4-64', @@ -357,7 +357,7 @@ describe('Device Backend Config', () => { ).to.equal(false); expect( deviceConfig.bootConfigChangeRequired( - rpiConfigBackend, + configTxtBackend, { HOST_CONFIG_dtoverlay: '"test","test2","vc4-fkms-v3d"' }, { HOST_CONFIG_dtoverlay: '"test","test2"' }, 'raspberrypi4-64', @@ -368,7 +368,7 @@ describe('Device Backend Config', () => { // describe('ConfigFS', () => { // const upboardConfig = new DeviceConfig(); - // let upboardConfigBackend: DeviceConfigBackend | null; + // let upboardConfigBackend: ConfigBackend | null; // before(async () => { // stub(child_process, 'exec').resolves(); diff --git a/test/17-config-utils.spec.ts b/test/17-config-utils.spec.ts index 116d658d..70e83598 100644 --- a/test/17-config-utils.spec.ts +++ b/test/17-config-utils.spec.ts @@ -1,13 +1,13 @@ import { expect } from './lib/chai-config'; import * as configUtils from '../src/config/utils'; -import { RPiConfigBackend } from '../src/config/backends/raspberry-pi'; +import { ConfigTxt } from '../src/config/backends/config-txt'; -const rpiBackend = new RPiConfigBackend(); +const configTxtBackend = new ConfigTxt(); describe('Config Utilities', () => { describe('Boot config', () => { it('correctly transforms environments to boot config objects', () => { - const bootConfig = configUtils.envToBootConfig(rpiBackend, { + const bootConfig = configUtils.envToBootConfig(configTxtBackend, { HOST_CONFIG_initramfs: 'initramf.gz 0x00800000', HOST_CONFIG_dtparam: '"i2c=on","audio=on"', HOST_CONFIG_dtoverlay: diff --git a/test/27-extlinux-config.spec.ts b/test/27-extlinux-config.spec.ts index c39ad3ba..6b0d6770 100644 --- a/test/27-extlinux-config.spec.ts +++ b/test/27-extlinux-config.spec.ts @@ -4,10 +4,10 @@ import { SinonStub, stub } from 'sinon'; import { expect } from './lib/chai-config'; import * as fsUtils from '../src/lib/fs-utils'; -import { ExtlinuxConfigBackend } from '../src/config/backends/extlinux'; +import { Extlinux } from '../src/config/backends/extlinux'; describe('Extlinux Configuration', () => { - const backend = new ExtlinuxConfigBackend(); + const backend = new Extlinux(); it('should parse a extlinux.conf file', () => { const text = stripIndent`\ @@ -24,7 +24,7 @@ describe('Extlinux Configuration', () => { `; // @ts-ignore accessing private method - const parsed = ExtlinuxConfigBackend.parseExtlinuxFile(text); + const parsed = Extlinux.parseExtlinuxFile(text); expect(parsed.globals).to.have.property('DEFAULT').that.equals('primary'); expect(parsed.globals).to.have.property('TIMEOUT').that.equals('30'); expect(parsed.globals) @@ -61,7 +61,7 @@ describe('Extlinux Configuration', () => { `; // @ts-ignore accessing private method - const parsed = ExtlinuxConfigBackend.parseExtlinuxFile(text); + const parsed = Extlinux.parseExtlinuxFile(text); expect(parsed.labels).to.have.property('primary').that.deep.equals({ LINUX: 'test1', FDT: '/boot/mycustomdtb.dtb', diff --git a/test/32-extra-uenv-config.spec.ts b/test/32-extra-uenv-config.spec.ts index 380a2aa9..df1cbbb0 100644 --- a/test/32-extra-uenv-config.spec.ts +++ b/test/32-extra-uenv-config.spec.ts @@ -5,10 +5,10 @@ import { SinonStub, spy, stub } from 'sinon'; import { expect } from './lib/chai-config'; import * as fsUtils from '../src/lib/fs-utils'; import Log from '../src/lib/supervisor-console'; -import { ExtraUEnvConfigBackend } from '../src/config/backends/extra-uEnv'; +import { ExtraUEnv } from '../src/config/backends/extra-uEnv'; describe('extra_uEnv Configuration', () => { - const backend = new ExtraUEnvConfigBackend(); + const backend = new ExtraUEnv(); let readFileStub: SinonStub; beforeEach(() => { @@ -25,7 +25,7 @@ describe('extra_uEnv Configuration', () => { extra_os_cmdline=isolcpus=3,4 splash console=tty0 `; // @ts-ignore accessing private method - const parsed = ExtraUEnvConfigBackend.parseOptions(fileContents); + const parsed = ExtraUEnv.parseOptions(fileContents); expect(parsed).to.deep.equal({ fdt: 'mycustom.dtb', isolcpus: '3,4', @@ -138,10 +138,10 @@ describe('extra_uEnv Configuration', () => { const logWarningStub = spy(Log, 'warn'); // @ts-ignore accessing private value - const previousSupportedConfigs = ExtraUEnvConfigBackend.supportedConfigs; + const previousSupportedConfigs = ExtraUEnv.supportedConfigs; // Stub isSupportedConfig so we can confirm collections work // @ts-ignore accessing private value - ExtraUEnvConfigBackend.supportedConfigs = { + ExtraUEnv.supportedConfigs = { fdt: { key: 'custom_fdt_file', collection: false }, isolcpus: { key: 'extra_os_cmdline', collection: true }, console: { key: 'extra_os_cmdline', collection: true }, @@ -166,7 +166,7 @@ describe('extra_uEnv Configuration', () => { (child_process.exec as SinonStub).restore(); logWarningStub.restore(); // @ts-ignore accessing private value - ExtraUEnvConfigBackend.supportedConfigs = previousSupportedConfigs; + ExtraUEnv.supportedConfigs = previousSupportedConfigs; }); it('only allows supported configuration options', () => { From 1d622095058b156fe46a48225026524492e0c397 Mon Sep 17 00:00:00 2001 From: Miguel Casqueira Date: Fri, 7 Aug 2020 19:21:46 -0400 Subject: [PATCH 2/2] Refactor device-config to support configuring multiple backends Signed-off-by: Miguel Casqueira --- src/config/backends/index.ts | 11 +++ src/config/utils.ts | 77 ++++++++------------ src/device-config.ts | 102 +++++++++++++++----------- test/17-config-utils.spec.ts | 137 ++++++++++++++++++++++++++++++----- 4 files changed, 216 insertions(+), 111 deletions(-) create mode 100644 src/config/backends/index.ts diff --git a/src/config/backends/index.ts b/src/config/backends/index.ts new file mode 100644 index 00000000..aa244467 --- /dev/null +++ b/src/config/backends/index.ts @@ -0,0 +1,11 @@ +import { Extlinux } from './extlinux'; +import { ExtraUEnv } from './extra-uEnv'; +import { ConfigTxt } from './config-txt'; +import { ConfigFs } from './config-fs'; + +export default [ + new Extlinux(), + new ExtraUEnv(), + new ConfigTxt(), + new ConfigFs(), +]; diff --git a/src/config/utils.ts b/src/config/utils.ts index 38bf179c..9359403a 100644 --- a/src/config/utils.ts +++ b/src/config/utils.ts @@ -1,41 +1,23 @@ import * as _ from 'lodash'; +import * as Bluebird from 'bluebird'; +import * as config from '../config'; import * as constants from '../lib/constants'; import { getMetaOSRelease } from '../lib/os-release'; import { EnvVarObject } from '../lib/types'; -import { Extlinux } from './backends/extlinux'; -import { ExtraUEnv } from './backends/extra-uEnv'; -import { ConfigTxt } from './backends/config-txt'; -import { ConfigFs } from './backends/config-fs'; +import Backends from './backends'; import { ConfigOptions, ConfigBackend } from './backends/backend'; -const configBackends = [ - new Extlinux(), - new ExtraUEnv(), - new ConfigTxt(), - new ConfigFs(), -]; - -export const initialiseConfigBackend = async (deviceType: string) => { - const backend = await getConfigBackend(deviceType); - if (backend) { - await backend.initialise(); - return backend; - } -}; - -async function getConfigBackend( - deviceType: string, -): Promise { - // Some backends are only supported by certain release versions so pass in metaRelease - const metaRelease = await getMetaOSRelease(constants.hostOSVersionPath); - let matched; - for (const backend of configBackends) { - if (await backend.matches(deviceType, metaRelease)) { - matched = backend; - } - } - return matched; +export async function getSupportedBackends(): Promise { + // Get required information to find supported backends + const [deviceType, metaRelease] = await Promise.all([ + config.get('deviceType'), + getMetaOSRelease(constants.hostOSVersionPath), + ]); + // Return list of configurable backends that match this deviceType and metaRelease + return Bluebird.filter(Backends, (backend: ConfigBackend) => + backend.matches(deviceType, metaRelease), + ); } export function envToBootConfig( @@ -45,7 +27,6 @@ export function envToBootConfig( if (configBackend == null) { return {}; } - return _(env) .pickBy((_val, key) => configBackend.isBootConfigVar(key)) .mapKeys((_val, key) => configBackend.processConfigVarName(key)) @@ -57,9 +38,9 @@ export function envToBootConfig( export function bootConfigToEnv( configBackend: ConfigBackend, - config: ConfigOptions, + configOptions: ConfigOptions, ): EnvVarObject { - return _(config) + return _(configOptions) .mapKeys((_val, key) => configBackend.createConfigVarName(key)) .mapValues((val) => { if (_.isArray(val)) { @@ -70,20 +51,6 @@ export function bootConfigToEnv( .value(); } -function filterNamespaceFromConfig( - namespace: RegExp, - conf: { [key: string]: any }, -): { [key: string]: any } { - return _.mapKeys( - _.pickBy(conf, (_v, k) => { - return namespace.test(k); - }), - (_v, k) => { - return k.replace(namespace, '$1'); - }, - ); -} - export function formatConfigKeys( configBackend: ConfigBackend | null, allowedKeys: string[], @@ -113,3 +80,17 @@ export function formatConfigKeys( ); }); } + +function filterNamespaceFromConfig( + namespace: RegExp, + conf: { [key: string]: any }, +): { [key: string]: any } { + return _.mapKeys( + _.pickBy(conf, (_v, k) => { + return namespace.test(k); + }), + (_v, k) => { + return k.replace(namespace, '$1'); + }, + ); +} diff --git a/src/device-config.ts b/src/device-config.ts index 0c159e37..68a887c1 100644 --- a/src/device-config.ts +++ b/src/device-config.ts @@ -100,15 +100,17 @@ const actionExecutors: DeviceActionExecutors = { } }, setBootConfig: async (step) => { - const $configBackend = await getConfigBackend(); if (!_.isObject(step.target)) { throw new Error('Non-dictionary passed to DeviceConfig.setBootConfig'); } - await setBootConfig($configBackend, step.target as Dictionary); + const backends = await getConfigBackends(); + for (const backend of backends) { + await setBootConfig(backend, step.target as Dictionary); + } }, }; -let configBackend: ConfigBackend | null = null; +const configBackends: ConfigBackend[] = []; const configKeys: Dictionary = { appUpdatePollInterval: { @@ -206,14 +208,19 @@ const rateLimits: Dictionary<{ }, }; -async function getConfigBackend() { - if (configBackend != null) { - return configBackend; +async function getConfigBackends(): Promise { + // Exit early if we already have a list + if (configBackends.length > 0) { + return configBackends; } - const dt = await config.get('deviceType'); - configBackend = (await configUtils.initialiseConfigBackend(dt)) ?? null; - - return configBackend; + // Get all the configurable backends this device supports + const backends = await configUtils.getSupportedBackends(); + // Initialize each backend + for (const backend of backends) { + await backend.initialise(); + } + // Return list of initialized ConfigBackends + return backends; } export async function setTarget( @@ -264,37 +271,45 @@ export async function getTarget({ return conf; } -export async function getCurrent() { +export async function getCurrent(): Promise> { + // Build a Dictionary of currently set config values + const currentConf: Dictionary = {}; + // Get environment variables const conf = await config.getMany( ['deviceType'].concat(_.keys(configKeys)) as SchemaTypeKey[], ); - - const $configBackend = await getConfigBackend(); - - const [vpnStatus, bootConfig] = await Promise.all([ - getVPNEnabled(), - getBootConfig($configBackend), - ]); - - const currentConf: Dictionary = { - // TODO: Fix this mess of half strings half boolean values everywhere - SUPERVISOR_VPN_CONTROL: vpnStatus != null ? vpnStatus.toString() : 'true', - }; - + // Add each value for (const key of _.keys(configKeys)) { const { envVarName } = configKeys[key]; const confValue = conf[key as SchemaTypeKey]; currentConf[envVarName] = confValue != null ? confValue.toString() : ''; } - - return _.assign(currentConf, bootConfig); + // Add VPN information + currentConf['SUPERVISOR_VPN_CONTROL'] = (await isVPNEnabled()) + ? 'true' + : 'false'; + // Get list of configurable backends + const backends = await getConfigBackends(); + // Add each backends configurable values + for (const backend of backends) { + _.assign(currentConf, await getBootConfig(backend)); + } + // Return compiled configuration + return currentConf; } export async function formatConfigKeys( conf: Dictionary, ): Promise> { - const backend = await getConfigBackend(); - return configUtils.formatConfigKeys(backend, validKeys, conf); + const backends = await getConfigBackends(); + const formattedKeys: Dictionary = {}; + for (const backend of backends) { + _.assign( + formattedKeys, + configUtils.formatConfigKeys(backend, validKeys, conf), + ); + } + return formattedKeys; } export function getDefaults() { @@ -314,16 +329,13 @@ export function resetRateLimits() { // Exported for tests export function bootConfigChangeRequired( - $configBackend: ConfigBackend | null, + configBackend: ConfigBackend | null, current: Dictionary, target: Dictionary, deviceType: string, ): boolean { - const targetBootConfig = configUtils.envToBootConfig($configBackend, target); - const currentBootConfig = configUtils.envToBootConfig( - $configBackend, - current, - ); + const targetBootConfig = configUtils.envToBootConfig(configBackend, target); + const currentBootConfig = configUtils.envToBootConfig(configBackend, current); // Some devices require specific overlays, here we apply them ensureRequiredOverlay(deviceType, targetBootConfig); @@ -331,7 +343,7 @@ export function bootConfigChangeRequired( if (!_.isEqual(currentBootConfig, targetBootConfig)) { _.each(targetBootConfig, (value, key) => { // Ignore null check because we can't get here if configBackend is null - if (!$configBackend!.isSupportedConfig(key)) { + if (!configBackend!.isSupportedConfig(key)) { if (currentBootConfig[key] !== value) { const err = `Attempt to change blacklisted config value ${key}`; logger.logSystemMessage( @@ -369,7 +381,6 @@ export async function getRequiredSteps( 'deviceType', 'unmanaged', ]); - const backend = await getConfigBackend(); const configChanges: Dictionary = {}; const humanReadableConfigChanges: Dictionary = {}; @@ -455,13 +466,16 @@ export async function getRequiredSteps( return step; }); - // Do we need to change the boot config? - if (bootConfigChangeRequired(backend, current, target, deviceType)) { - steps.push({ - action: 'setBootConfig', - target, - }); - } + const backends = await getConfigBackends(); + // Check for required bootConfig changes + backends.forEach((backend) => { + if (bootConfigChangeRequired(backend, current, target, deviceType)) { + steps.push({ + action: 'setBootConfig', + target, + }); + } + }); // Check if there is either no steps, or they are all // noops, and we need to reboot. We want to do this @@ -539,7 +553,7 @@ export async function setBootConfig( } } -async function getVPNEnabled(): Promise { +async function isVPNEnabled(): Promise { try { const activeState = await dbus.serviceActiveState(vpnServiceName); return !_.includes(['inactive', 'deactivating'], activeState); diff --git a/test/17-config-utils.spec.ts b/test/17-config-utils.spec.ts index 70e83598..8537f7b2 100644 --- a/test/17-config-utils.spec.ts +++ b/test/17-config-utils.spec.ts @@ -1,25 +1,124 @@ -import { expect } from './lib/chai-config'; -import * as configUtils from '../src/config/utils'; -import { ConfigTxt } from '../src/config/backends/config-txt'; +import { stub } from 'sinon'; +import * as _ from 'lodash'; -const configTxtBackend = new ConfigTxt(); +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'; +import { ConfigTxt } from '../src/config/backends/config-txt'; +import { ConfigFs } from '../src/config/backends/config-fs'; +import { ConfigBackend } from '../src/config/backends/backend'; describe('Config Utilities', () => { - describe('Boot config', () => { - it('correctly transforms environments to boot config objects', () => { - const bootConfig = configUtils.envToBootConfig(configTxtBackend, { - HOST_CONFIG_initramfs: 'initramf.gz 0x00800000', - HOST_CONFIG_dtparam: '"i2c=on","audio=on"', - HOST_CONFIG_dtoverlay: - '"ads7846","lirc-rpi,gpio_out_pin=17,gpio_in_pin=13"', - HOST_CONFIG_foobar: 'baz', - }); - expect(bootConfig).to.deep.equal({ - initramfs: 'initramf.gz 0x00800000', - dtparam: ['i2c=on', 'audio=on'], - dtoverlay: ['ads7846', 'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13'], - foobar: 'baz', - }); + it('gets list of supported backends', async () => { + // Stub so that we get an array containing only config-txt backend + const configStub = stub(config, 'get').resolves('raspberry'); + // Get list of backends + const devices = await configUtils.getSupportedBackends(); + expect(devices.length).to.equal(1); + expect(devices[0].constructor.name).to.equal('ConfigTxt'); + // Restore stub + configStub.restore(); + // TO-DO: When we have a device that will match for multiple backends + // add a test that we get more then 1 backend for that device + }); + + it('transforms environment variables to boot configs', () => { + _.forEach(CONFIGS, (configObj: any, key: string) => { + expect( + configUtils.envToBootConfig(BACKENDS[key], configObj.envVars), + ).to.deep.equal(configObj.bootConfig); + }); + }); + + it('transforms boot configs to environment variables', () => { + _.forEach(CONFIGS, (configObj: any, key: string) => { + expect( + configUtils.bootConfigToEnv(BACKENDS[key], configObj.bootConfig), + ).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 = { + extraUEnv: new ExtraUEnv(), + extlinux: new Extlinux(), + configtxt: new ConfigTxt(), + configfs: new ConfigFs(), +}; + +const CONFIGS = { + extraUEnv: { + envVars: { + HOST_EXTLINUX_fdt: '/boot/mycustomdtb.dtb', + HOST_EXTLINUX_isolcpus: '1,2,3', + HOST_EXTLINUX_rootwait: '', + }, + bootConfig: { + fdt: '/boot/mycustomdtb.dtb', + isolcpus: '1,2,3', + rootwait: '', + }, + }, + extlinux: { + envVars: { + HOST_EXTLINUX_fdt: '/boot/mycustomdtb.dtb', + HOST_EXTLINUX_isolcpus: '1,2,3', + HOST_EXTLINUX_rootwait: '', + }, + bootConfig: { + fdt: '/boot/mycustomdtb.dtb', + isolcpus: '1,2,3', + rootwait: '', + }, + }, + configtxt: { + envVars: { + HOST_CONFIG_initramfs: 'initramf.gz 0x00800000', + HOST_CONFIG_dtparam: '"i2c=on","audio=on"', + HOST_CONFIG_dtoverlay: + '"ads7846","lirc-rpi,gpio_out_pin=17,gpio_in_pin=13"', + HOST_CONFIG_foobar: 'baz', + }, + bootConfig: { + initramfs: 'initramf.gz 0x00800000', + dtparam: ['i2c=on', 'audio=on'], + dtoverlay: ['ads7846', 'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13'], + foobar: 'baz', + }, + }, + // TO-DO: Config-FS is commented out because it behaves differently and doesn't + // add value to the Config Utilities if we make it work but would like to add it + // configfs: { + // envVars: { + // ssdt: 'spidev1,1' + // }, + // bootConfig: { + // ssdt: ['spidev1,1'] + // }, + // }, +};