device-config: Use default values for any invalid target values

If a value is requested which does not pass validation, we instead set
it to the default value, to ensure that the state engine continues to
work and move towards the target state.

Change-type: minor
Closes: #938
Signed-off-by: Cameron Diver <cameron@balena.io>
This commit is contained in:
Cameron Diver 2019-03-27 13:08:04 +00:00
parent 3e6a6359a7
commit c211efe399
No known key found for this signature in database
GPG Key ID: 49690ED87032539F
2 changed files with 50 additions and 6 deletions

View File

@ -223,6 +223,23 @@ export class Config extends (EventEmitter as {
return generateUniqueKey();
}
public valueIsValid<T extends SchemaTypeKey>(
key: T,
value: unknown,
): boolean {
// If the default entry in the schema is a type and not a value,
// use this in the validation of the value
const schemaTypesEntry = schemaTypes[key as SchemaTypeKey];
let type: t.Type<unknown>;
if (schemaTypesEntry.default instanceof t.Type) {
type = t.union([schemaTypesEntry.type, schemaTypesEntry.default]);
} else {
type = schemaTypesEntry.type;
}
return type.decode(value).isRight();
}
private async getSchema<T extends Schema.SchemaKey>(
key: T,
db: Transaction,

View File

@ -1,11 +1,12 @@
import * as _ from 'lodash';
import { inspect } from 'util';
import Config from './config';
import { SchemaTypeKey } from './config/schema-type';
import Database, { Transaction } from './db';
import Logger from './logger';
import { DeviceConfigBackend, ConfigOptions } from './config/backend';
import { ConfigOptions, DeviceConfigBackend } from './config/backend';
import * as configUtils from './config/utils';
import { UnitNotLoadedError } from './lib/errors';
import * as systemd from './lib/systemd';
@ -386,7 +387,8 @@ export class DeviceConfig {
_.each(
DeviceConfig.configKeys,
({ envVarName, varType, rebootRequired }, key) => {
({ envVarName, varType, rebootRequired, defaultValue }, key) => {
let changingValue: null | string = null;
// Test if the key is different
if (
!DeviceConfig.configTest(
@ -395,10 +397,35 @@ export class DeviceConfig {
target[envVarName],
)
) {
// Save the change if it is
configChanges[key] = target[envVarName];
humanReadableConfigChanges[envVarName] = target[envVarName];
reboot = rebootRequired || reboot;
// Check that the difference is not due to the variable having an invalid
// value set from the cloud
if (
this.config.valueIsValid(key as SchemaTypeKey, target[envVarName])
) {
// Save the change if it is both valid and different
changingValue = target[envVarName];
} else {
console.log(
`Warning: Ignoring invalid device configuration value for ${key}, value: ${inspect(
target[envVarName],
)}. Falling back to default (${defaultValue})`,
);
if (
!DeviceConfig.configTest(
varType,
current[envVarName],
defaultValue,
)
) {
// Set it to the default value if it is different to the current
changingValue = defaultValue;
}
}
if (changingValue != null) {
configChanges[key] = changingValue;
humanReadableConfigChanges[envVarName] = changingValue;
reboot = rebootRequired || reboot;
}
}
},
);