mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-12 05:55:38 +00:00
Ignore empty dtoverlay coming from target state
Before v16, the supervisor would incorrectly interpret an empty dtoverlay in config.txt (i.e. a line with `dtoverlay=`) as a proper variable and create it as config var if found on config.txt before provisioning. With the latest supervisor, an empty dtoverlay on config.txt is interpreted in the [correct way](https://www.raspberrypi.com/documentation/computers/config_txt.html#dtoverlay), but that makes an empty dtoverlay on the target state meaningless, leading to looping behavior as the current and target state won't match. This PR modifies the pre-processing function to filter out empty array variables before the comparison, to prevent this sort of looping behavior. This allows empty dtoverlay to be ignored. Change-type: patch
This commit is contained in:
parent
a71cc374db
commit
82cbc74853
@ -219,7 +219,11 @@ export class ConfigTxt extends ConfigBackend {
|
||||
// Split dtoverlays from their params to avoid running into char limits
|
||||
// and write at the end to prevent overriding the base overlay
|
||||
if (opts.dtoverlay != null) {
|
||||
for (const entry of opts.dtoverlay) {
|
||||
for (let entry of opts.dtoverlay) {
|
||||
entry = entry.trim();
|
||||
if (entry.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const [overlay, ...params] = entry.split(',');
|
||||
confStatements.push(`dtoverlay=${overlay}`);
|
||||
confStatements.push(...params.map((p) => `dtparam=${p}`));
|
||||
@ -245,9 +249,16 @@ export class ConfigTxt extends ConfigBackend {
|
||||
public processConfigVarValue(key: string, value: string): string | string[] {
|
||||
if (isArrayConfig(key)) {
|
||||
if (!value.startsWith('"')) {
|
||||
if (key === 'dtoverlay' && value.trim().length === 0) {
|
||||
return [];
|
||||
}
|
||||
return [value];
|
||||
} else {
|
||||
return JSON.parse(`[${value}]`);
|
||||
const res: string[] = JSON.parse(`[${value}]`);
|
||||
if (key === 'dtoverlay') {
|
||||
return res.filter((s) => s.trim().length > 0);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
@ -33,6 +33,7 @@ export function envToBootConfig(
|
||||
.mapValues((val, key) =>
|
||||
configBackend.processConfigVarValue(key, val || ''),
|
||||
)
|
||||
.pickBy((val) => !Array.isArray(val) || val.length > 0)
|
||||
.value();
|
||||
}
|
||||
|
||||
|
@ -306,4 +306,55 @@ describe('config/config-txt', () => {
|
||||
|
||||
await tfs.restore();
|
||||
});
|
||||
|
||||
it('ignores empty dtoverlay on the target state', async () => {
|
||||
const tfs = await testfs({
|
||||
[hostUtils.pathOnBoot('config.txt')]: stripIndent`
|
||||
enable_uart=1
|
||||
dtparam=i2c_arm=on
|
||||
dtparam=spi=on
|
||||
disable_splash=1
|
||||
dtparam=audio=on
|
||||
gpu_mem=16
|
||||
`,
|
||||
}).enable();
|
||||
|
||||
const configTxt = new ConfigTxt();
|
||||
|
||||
await configTxt.setBootConfig({
|
||||
dtparam: ['i2c=on', 'audio=on'],
|
||||
dtoverlay: [''],
|
||||
enable_uart: '1',
|
||||
avoid_warnings: '1',
|
||||
gpu_mem: '256',
|
||||
initramfs: 'initramf.gz 0x00800000',
|
||||
'hdmi_force_hotplug:1': '1',
|
||||
});
|
||||
|
||||
await expect(
|
||||
fs.readFile(hostUtils.pathOnBoot('config.txt'), 'utf8'),
|
||||
).to.eventually.equal(
|
||||
stripIndent`
|
||||
dtparam=i2c=on
|
||||
dtparam=audio=on
|
||||
enable_uart=1
|
||||
avoid_warnings=1
|
||||
gpu_mem=256
|
||||
initramfs initramf.gz 0x00800000
|
||||
hdmi_force_hotplug:1=1
|
||||
` + '\n',
|
||||
);
|
||||
|
||||
// Will try to parse /test/data/mnt/boot/config.txt
|
||||
await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
|
||||
dtparam: ['i2c=on', 'audio=on'],
|
||||
enable_uart: '1',
|
||||
avoid_warnings: '1',
|
||||
gpu_mem: '256',
|
||||
initramfs: 'initramf.gz 0x00800000',
|
||||
'hdmi_force_hotplug:1': '1',
|
||||
});
|
||||
|
||||
await tfs.restore();
|
||||
});
|
||||
});
|
||||
|
@ -228,6 +228,30 @@ describe('device-config', () => {
|
||||
expect(logSpy).to.not.be.called;
|
||||
});
|
||||
|
||||
it('ignores empty dtoverlay when comparing current and target state', async () => {
|
||||
const current = {
|
||||
HOST_CONFIG_initramfs: 'initramf.gz 0x00800000',
|
||||
HOST_CONFIG_dtparam: '"i2c=on","audio=on"',
|
||||
HOST_CONFIG_foobar: 'baz',
|
||||
};
|
||||
const target = {
|
||||
HOST_CONFIG_initramfs: 'initramf.gz 0x00800000',
|
||||
HOST_CONFIG_dtparam: '"i2c=on","audio=on"',
|
||||
HOST_CONFIG_dtoverlay: '',
|
||||
HOST_CONFIG_foobar: 'baz',
|
||||
};
|
||||
|
||||
expect(
|
||||
// @ts-expect-error accessing private value
|
||||
deviceConfig.bootConfigChangeRequired(
|
||||
configTxtBackend,
|
||||
current,
|
||||
target,
|
||||
),
|
||||
).to.equal(false);
|
||||
expect(logSpy).to.not.be.called;
|
||||
});
|
||||
|
||||
it('writes the target config.txt', async () => {
|
||||
const current = {
|
||||
HOST_CONFIG_initramfs: 'initramf.gz 0x00800000',
|
||||
|
Loading…
x
Reference in New Issue
Block a user