mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-01 08:47:56 +00:00
Merge pull request #1440 from balena-io/1437-patch-empty-config
Preventing removing all configurations if device has no backends
This commit is contained in:
commit
ac69f04c10
@ -447,9 +447,7 @@ export class APIBinder {
|
|||||||
const defaultConfig = deviceConfig.getDefaults();
|
const defaultConfig = deviceConfig.getDefaults();
|
||||||
|
|
||||||
const currentState = await this.deviceState.getCurrentForComparison();
|
const currentState = await this.deviceState.getCurrentForComparison();
|
||||||
const targetConfig = await deviceConfig.formatConfigKeys(
|
const targetConfig = deviceConfig.formatConfigKeys(targetConfigUnformatted);
|
||||||
targetConfigUnformatted,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!currentState.local.config) {
|
if (!currentState.local.config) {
|
||||||
throw new InternalInconsistencyError(
|
throw new InternalInconsistencyError(
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { Extlinux } from './extlinux';
|
import { Extlinux } from './extlinux';
|
||||||
import { ExtraUEnv } from './extra-uEnv';
|
import { ExtraUEnv } from './extra-uEnv';
|
||||||
import { ConfigTxt } from './config-txt';
|
import { ConfigTxt } from './config-txt';
|
||||||
import { ConfigFs } from './config-fs';
|
import { ConfigFs } from './config-fs';
|
||||||
|
|
||||||
export default [
|
export const allBackends = [
|
||||||
new Extlinux(),
|
new Extlinux(),
|
||||||
new ExtraUEnv(),
|
new ExtraUEnv(),
|
||||||
new ConfigTxt(),
|
new ConfigTxt(),
|
||||||
new ConfigFs(),
|
new ConfigFs(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function matchesAnyBootConfig(envVar: string): boolean {
|
||||||
|
return allBackends.some((a) => a.isBootConfigVar(envVar));
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ import * as config from '../config';
|
|||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
import { getMetaOSRelease } from '../lib/os-release';
|
import { getMetaOSRelease } from '../lib/os-release';
|
||||||
import { EnvVarObject } from '../lib/types';
|
import { EnvVarObject } from '../lib/types';
|
||||||
import Backends from './backends';
|
import { allBackends as Backends } from './backends';
|
||||||
import { ConfigOptions, ConfigBackend } from './backends/backend';
|
import { ConfigOptions, ConfigBackend } from './backends/backend';
|
||||||
|
|
||||||
export async function getSupportedBackends(): Promise<ConfigBackend[]> {
|
export async function getSupportedBackends(): Promise<ConfigBackend[]> {
|
||||||
@ -51,37 +51,7 @@ export function bootConfigToEnv(
|
|||||||
.value();
|
.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatConfigKeys(
|
export function filterNamespaceFromConfig(
|
||||||
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(
|
|
||||||
namespace: RegExp,
|
namespace: RegExp,
|
||||||
conf: { [key: string]: any },
|
conf: { [key: string]: any },
|
||||||
): { [key: string]: any } {
|
): { [key: string]: any } {
|
||||||
|
@ -2,17 +2,17 @@ import * as _ from 'lodash';
|
|||||||
import { inspect } from 'util';
|
import { inspect } from 'util';
|
||||||
|
|
||||||
import * as config from './config';
|
import * as config from './config';
|
||||||
import { SchemaTypeKey } from './config/schema-type';
|
|
||||||
import * as db from './db';
|
import * as db from './db';
|
||||||
import * as logger from './logger';
|
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 * as dbus from './lib/dbus';
|
||||||
import { UnitNotLoadedError } from './lib/errors';
|
|
||||||
import { EnvVarObject } from './lib/types';
|
import { EnvVarObject } from './lib/types';
|
||||||
|
import { UnitNotLoadedError } from './lib/errors';
|
||||||
import { checkInt, checkTruthy } from './lib/validation';
|
import { checkInt, checkTruthy } from './lib/validation';
|
||||||
import { DeviceStatus } from './types/state';
|
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';
|
const vpnServiceName = 'openvpn';
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ export async function setTarget(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const $db = trx ?? db.models.bind(db);
|
const $db = trx ?? db.models.bind(db);
|
||||||
|
|
||||||
const formatted = await formatConfigKeys(target);
|
const formatted = formatConfigKeys(target);
|
||||||
// check for legacy keys
|
// check for legacy keys
|
||||||
if (formatted['OVERRIDE_LOCK'] != null) {
|
if (formatted['OVERRIDE_LOCK'] != null) {
|
||||||
formatted['SUPERVISOR_OVERRIDE_LOCK'] = formatted['OVERRIDE_LOCK'];
|
formatted['SUPERVISOR_OVERRIDE_LOCK'] = formatted['OVERRIDE_LOCK'];
|
||||||
@ -298,18 +298,30 @@ export async function getCurrent(): Promise<Dictionary<string>> {
|
|||||||
return currentConf;
|
return currentConf;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function formatConfigKeys(
|
export function formatConfigKeys(conf: {
|
||||||
conf: Dictionary<string>,
|
[key: string]: any;
|
||||||
): Promise<Dictionary<any>> {
|
}): Dictionary<any> {
|
||||||
const backends = await getConfigBackends();
|
const namespaceRegex = /^BALENA_(.*)/;
|
||||||
const formattedKeys: Dictionary<any> = {};
|
const legacyNamespaceRegex = /^RESIN_(.*)/;
|
||||||
for (const backend of backends) {
|
const confFromNamespace = configUtils.filterNamespaceFromConfig(
|
||||||
_.assign(
|
namespaceRegex,
|
||||||
formattedKeys,
|
conf,
|
||||||
configUtils.formatConfigKeys(backend, validKeys, conf),
|
);
|
||||||
);
|
const confFromLegacyNamespace = configUtils.filterNamespaceFromConfig(
|
||||||
}
|
legacyNamespaceRegex,
|
||||||
return formattedKeys;
|
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() {
|
export function getDefaults() {
|
||||||
|
@ -80,9 +80,7 @@ export async function loadTargetFromFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deviceConf = await deviceConfig.getCurrent();
|
const deviceConf = await deviceConfig.getCurrent();
|
||||||
const formattedConf = await deviceConfig.formatConfigKeys(
|
const formattedConf = deviceConfig.formatConfigKeys(preloadState.config);
|
||||||
preloadState.config,
|
|
||||||
);
|
|
||||||
preloadState.config = { ...formattedConf, ...deviceConf };
|
preloadState.config = { ...formattedConf, ...deviceConf };
|
||||||
const localState = {
|
const localState = {
|
||||||
local: { name: '', ...preloadState },
|
local: { name: '', ...preloadState },
|
||||||
|
@ -168,19 +168,22 @@ describe('Device Backend Config', () => {
|
|||||||
it('accepts RESIN_ and BALENA_ variables', async () => {
|
it('accepts RESIN_ and BALENA_ variables', async () => {
|
||||||
return expect(
|
return expect(
|
||||||
deviceConfig.formatConfigKeys({
|
deviceConfig.formatConfigKeys({
|
||||||
FOO: 'bar',
|
FOO: 'bar', // should be removed
|
||||||
BAR: 'baz',
|
BAR: 'baz', // should be removed
|
||||||
RESIN_HOST_CONFIG_foo: 'foobaz',
|
RESIN_SUPERVISOR_LOCAL_MODE: 'false', // any device
|
||||||
BALENA_HOST_CONFIG_foo: 'foobar',
|
BALENA_SUPERVISOR_OVERRIDE_LOCK: 'false', // any device
|
||||||
RESIN_HOST_CONFIG_other: 'val',
|
BALENA_SUPERVISOR_POLL_INTERVAL: '100', // any device
|
||||||
BALENA_HOST_CONFIG_baz: 'bad',
|
RESIN_HOST_CONFIG_dtparam: 'i2c_arm=on","spi=on","audio=on', // config.txt backend
|
||||||
BALENA_SUPERVISOR_POLL_INTERVAL: '100',
|
RESIN_HOST_CONFIGFS_ssdt: 'spidev1.0', // configfs backend
|
||||||
|
BALENA_HOST_EXTLINUX_isolcpus: '1,2,3', // extlinux backend
|
||||||
}),
|
}),
|
||||||
).to.eventually.deep.equal({
|
).to.deep.equal({
|
||||||
HOST_CONFIG_foo: 'foobar',
|
SUPERVISOR_LOCAL_MODE: 'false',
|
||||||
HOST_CONFIG_other: 'val',
|
SUPERVISOR_OVERRIDE_LOCK: 'false',
|
||||||
HOST_CONFIG_baz: 'bad',
|
|
||||||
SUPERVISOR_POLL_INTERVAL: '100',
|
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',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { expect } from './lib/chai-config';
|
import { expect } from './lib/chai-config';
|
||||||
import * as config from '../src/config';
|
import * as config from '../src/config';
|
||||||
import { validKeys } from '../src/device-config';
|
|
||||||
import * as configUtils from '../src/config/utils';
|
import * as configUtils from '../src/config/utils';
|
||||||
import { ExtraUEnv } from '../src/config/backends/extra-uEnv';
|
import { ExtraUEnv } from '../src/config/backends/extra-uEnv';
|
||||||
import { Extlinux } from '../src/config/backends/extlinux';
|
import { Extlinux } from '../src/config/backends/extlinux';
|
||||||
@ -40,28 +39,6 @@ describe('Config Utilities', () => {
|
|||||||
).to.deep.equal(configObj.envVars);
|
).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<string, ConfigBackend> = {
|
const BACKENDS: Record<string, ConfigBackend> = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user