Merge pull request #1490 from balena-io/1472-device-type

Change source of deviceType to device-type.json
This commit is contained in:
bulldozer-balena[bot] 2020-10-27 13:26:56 +00:00 committed by GitHub
commit 22402dbb93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 194 additions and 24 deletions

View File

@ -1,5 +1,6 @@
import * as Bluebird from 'bluebird';
import * as _ from 'lodash';
import * as memoizee from 'memoizee';
import { fs } from 'mz';
import { URL } from 'url';
@ -38,22 +39,44 @@ export const fnSchema = {
macAddress: () => {
return macAddress.getAll(constants.macAddressPath);
},
deviceArch: async () => {
try {
// FIXME: We should be mounting the following file into the supervisor from the
// start-resin-supervisor script, changed in meta-resin - but until then, hardcode it
const data = await fs.readFile(
`${constants.rootMountPoint}/resin-boot/device-type.json`,
'utf8',
);
const deviceInfo = JSON.parse(data);
deviceArch: memoizee(
async () => {
try {
// FIXME: We should be mounting the following file into the supervisor from the
// start-resin-supervisor script, changed in meta-resin - but until then, hardcode it
const data = await fs.readFile(
`${constants.bootMountPoint}/device-type.json`,
'utf8',
);
const deviceInfo = JSON.parse(data);
return deviceInfo.arch;
} catch (e) {
log.error(`Unable to get architecture: ${e}`);
return 'unknown';
}
},
return deviceInfo.arch;
} catch (e) {
log.error(`Unable to get architecture: ${e}`);
throw e;
}
},
{ promise: true },
),
deviceType: memoizee(
async () => {
try {
// FIXME: We should be mounting the following file into the supervisor from the
// start-resin-supervisor script, changed in meta-resin - but until then, hardcode it
const data = await fs.readFile(
`${constants.bootMountPoint}/device-type.json`,
'utf8',
);
const deviceInfo = JSON.parse(data);
return deviceInfo.slug;
} catch (e) {
log.error(`Unable to get device type: ${e}`);
throw e;
}
},
{ promise: true },
),
provisioningOptions: () => {
return config
.getMany([

View File

@ -87,11 +87,29 @@ export async function get<T extends SchemaTypeKey>(
// Cast the promise as something that produces an unknown, and this means that
// we can validate the output of the function as well, ensuring that the type matches
const promiseValue = FnSchema.fnSchema[fnKey]();
return promiseValue.then((value: unknown) => {
const decoded = schemaTypes[key].type.decode(value);
return promiseValue
.then((value: unknown) => {
const decoded = schemaTypes[key].type.decode(value);
return checkValueDecode(decoded, key, value) && decoded.right;
});
return checkValueDecode(decoded, key, value) && decoded.right;
})
.catch(() => {
const defaultValue = schemaTypes[key].default;
if (defaultValue instanceof t.Type) {
// For functions, this can happen if t.never is used as default
// value. In that case decoding and the value check below will throw
// (which is what is expected)
// if future functions use NullOrUndefined as with values above
// this branch will return undefined. In any case this should never
// happen
const maybeDecoded = (defaultValue as t.Type<any>).decode(undefined);
return (
checkValueDecode(maybeDecoded, key, undefined) && maybeDecoded.right
);
}
return defaultValue as SchemaReturn<T>;
});
} else {
throw new Error(`Unknown config value ${key}`);
}

View File

@ -34,11 +34,6 @@ export const schema = {
mutable: true,
removeIfNull: false,
},
deviceType: {
source: 'config.json',
mutable: false,
removeIfNull: false,
},
deviceId: {
source: 'config.json',
mutable: true,

View File

@ -1,5 +1,6 @@
import * as _ from 'lodash';
import { fs } from 'mz';
import { SinonStub, stub } from 'sinon';
import chai = require('./lib/chai-config');
import prepare = require('./lib/prepare');
@ -7,6 +8,7 @@ import * as conf from '../src/config';
import constants = require('../src/lib/constants');
import { SchemaTypeKey } from '../src/config/schema-type';
import { fnSchema } from '../src/config/functions';
// tslint:disable-next-line
chai.use(require('chai-events'));
@ -130,4 +132,136 @@ describe('Config', () => {
expect(conf.remove('version' as any)).to.be.rejected;
});
});
describe('Config data sources', () => {
after(() => {
// Clean up memoized values
});
it('should obtain deviceArch from device-type.json', async () => {
const [slug, arch] = ['raspberrypi3', 'armv7hf'];
stub(fs, 'readFile').resolves(
JSON.stringify({
slug,
arch,
}),
);
const deviceArch = await conf.get('deviceArch');
expect(deviceArch).to.equal(arch);
expect(fs.readFile).to.be.calledOnce;
expect(fs.readFile).to.be.calledWith(
`${constants.bootMountPoint}/device-type.json`,
'utf8',
);
(fs.readFile as SinonStub).restore();
});
it('should obtain deviceType from device-type.json', async () => {
const [slug, arch] = ['raspberrypi3', 'armv7hf'];
stub(fs, 'readFile').resolves(
JSON.stringify({
slug,
arch,
}),
);
const deviceType = await conf.get('deviceType');
expect(deviceType).to.equal(slug);
expect(fs.readFile).to.be.calledOnce;
expect(fs.readFile).to.be.calledWith(
`${constants.bootMountPoint}/device-type.json`,
'utf8',
);
(fs.readFile as SinonStub).restore();
});
it('should memoize values from device-type.json', async () => {
const [slug, arch] = ['raspberrypi3', 'armv7hf'];
stub(fs, 'readFile').resolves(
JSON.stringify({
slug,
arch,
}),
);
const deviceArch = await conf.get('deviceArch');
expect(deviceArch).to.equal(arch);
// The result should still be memoized from the
// call on the previous test
expect(fs.readFile).to.not.be.called;
const deviceType = await conf.get('deviceType');
expect(deviceType).to.equal(slug);
// The result should still be memoized from the
// call on the previous test
expect(fs.readFile).to.not.be.called;
(fs.readFile as SinonStub).restore();
});
it('should not memoize errors when reading deviceArch', (done) => {
// Clean up memoized value
fnSchema.deviceArch.clear();
// File not found
stub(fs, 'readFile').throws('File not found');
expect(conf.get('deviceArch')).to.eventually.equal('unknown');
expect(fs.readFile).to.be.calledOnce;
(fs.readFile as SinonStub).restore();
// Next call should not throw
const [slug, arch] = ['raspberrypi3', 'armv7hf'];
stub(fs, 'readFile').resolves(
JSON.stringify({
slug,
arch,
}),
);
// We need to let rejection be discovered
// https://github.com/medikoo/memoizee/issues/93
setTimeout(() => {
expect(conf.get('deviceArch')).to.eventually.equal(arch);
expect(fs.readFile).to.be.calledOnce;
(fs.readFile as SinonStub).restore();
done();
});
});
it('should not memoize errors when reading deviceType', (done) => {
// Clean up memoized value
fnSchema.deviceType.clear();
// File not found
stub(fs, 'readFile').throws('File not found');
expect(conf.get('deviceType')).to.eventually.equal('unknown');
expect(fs.readFile).to.be.calledOnce;
(fs.readFile as SinonStub).restore();
// Next call should not throw
const [slug, arch] = ['raspberrypi3', 'armv7hf'];
stub(fs, 'readFile').resolves(
JSON.stringify({
slug,
arch,
}),
);
// We need to let rejection be discovered
// https://github.com/medikoo/memoizee/issues/93
setTimeout(() => {
expect(conf.get('deviceType')).to.eventually.equal(slug);
expect(fs.readFile).to.be.calledOnce;
(fs.readFile as SinonStub).restore();
done();
});
});
});
});