mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-04-24 21:09:47 +00:00
Ensure we get input on parsing errors
We wrap JSON and date parsing code to ensure input data is logged in case of an error. Change-type: minor Signed-off-by: Roman Mazur <roman@balena.io>
This commit is contained in:
parent
7c4d8d7653
commit
645bc6c185
@ -159,6 +159,16 @@ export class LocalModeManager {
|
||||
});
|
||||
}
|
||||
|
||||
// Ensures an error is thrown id timestamp string cannot be parsed.
|
||||
// Date.parse may both throw or return NaN depending on a case.
|
||||
private static parseTimestamp(input: string): Date {
|
||||
const ms = Date.parse(input);
|
||||
if (isNaN(ms)) {
|
||||
throw new Error('bad date string - got Nan parsing it');
|
||||
}
|
||||
return new Date(ms);
|
||||
}
|
||||
|
||||
// Read the latest stored snapshot from the database.
|
||||
public async retrieveLatestSnapshot(): Promise<EngineSnapshotRecord | null> {
|
||||
const r = await this.db
|
||||
@ -170,10 +180,18 @@ export class LocalModeManager {
|
||||
if (!r) {
|
||||
return null;
|
||||
}
|
||||
return new EngineSnapshotRecord(
|
||||
EngineSnapshot.fromJSON(r.snapshot),
|
||||
new Date(Date.parse(r.timestamp)),
|
||||
);
|
||||
try {
|
||||
return new EngineSnapshotRecord(
|
||||
EngineSnapshot.fromJSON(r.snapshot),
|
||||
LocalModeManager.parseTimestamp(r.timestamp),
|
||||
);
|
||||
} catch (e) {
|
||||
// Some parsing error happened. Ensure we add data details to the error description.
|
||||
throw new Error(
|
||||
`Cannot parse snapshot data ${JSON.stringify(r)}.` +
|
||||
`Original message: [${e.message}].`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async removeLocalModeArtifacts(objects: EngineSnapshot) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import * as assert from 'assert';
|
||||
import { expect } from 'chai';
|
||||
import * as Docker from 'dockerode';
|
||||
import * as sinon from 'sinon';
|
||||
@ -277,6 +278,24 @@ describe('LocalModeManager', () => {
|
||||
expect(dockerStub.getNetwork.callCount).to.be.equal(2);
|
||||
removeStubs.forEach(s => expect(s.remove.callCount).to.be.equal(2));
|
||||
});
|
||||
|
||||
it.only('skips cleanup in case of data corruption', async () => {
|
||||
const removeStubs = stubRemoveMethods(false);
|
||||
|
||||
await db.models('engineSnapshot').insert({
|
||||
snapshot: 'bad json',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
localMode.startLocalModeChangeHandling(false);
|
||||
await localMode.switchCompletion();
|
||||
|
||||
expect(dockerStub.getImage.notCalled).to.be.true;
|
||||
expect(dockerStub.getContainer.notCalled).to.be.true;
|
||||
expect(dockerStub.getVolume.notCalled).to.be.true;
|
||||
expect(dockerStub.getNetwork.notCalled).to.be.true;
|
||||
removeStubs.forEach(s => expect(s.remove.notCalled).to.be.true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -310,6 +329,45 @@ describe('LocalModeManager', () => {
|
||||
await localMode.storeEngineSnapshot(recordSample);
|
||||
expect(await recordsCount()).to.equal(1);
|
||||
});
|
||||
|
||||
describe('in case of data corruption', () => {
|
||||
beforeEach(async () => {
|
||||
await db.models('engineSnapshot').delete();
|
||||
});
|
||||
|
||||
it('deals with snapshot data corruption', async () => {
|
||||
// Write bad data to simulate corruption.
|
||||
await db.models('engineSnapshot').insert({
|
||||
snapshot: 'bad json',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
await localMode.retrieveLatestSnapshot();
|
||||
assert.fail('Parsing error was expected');
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
expect(e.message).to.contain('bad json');
|
||||
}
|
||||
});
|
||||
|
||||
it('deals with snapshot timestamp corruption', async () => {
|
||||
// Write bad data to simulate corruption.
|
||||
await db.models('engineSnapshot').insert({
|
||||
snapshot:
|
||||
'{"containers": [], "images": [], "volumes": [], "networks": []}',
|
||||
timestamp: 'bad timestamp',
|
||||
});
|
||||
|
||||
try {
|
||||
await localMode.retrieveLatestSnapshot();
|
||||
assert.fail('Parsing error was expected');
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
expect(e.message).to.contain('bad timestamp');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user