Merge pull request #1847 from balena-os/apps-json-rename

Rename apps.json after initial preload
This commit is contained in:
bulldozer-balena[bot] 2022-01-13 20:24:23 +00:00 committed by GitHub
commit 70ec8c9af4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 14 deletions

View File

@ -8,21 +8,19 @@ import * as deviceConfig from '../device-config';
import * as eventTracker from '../event-tracker';
import * as images from '../compose/images';
import constants = require('../lib/constants');
import { AppsJsonParseError, EISDIR, ENOENT } from '../lib/errors';
import log from '../lib/supervisor-console';
import { convertLegacyAppsJson } from '../lib/migration';
import { AppsJsonFormat } from '../types/state';
import * as fsUtils from '../lib/fs-utils';
export async function loadTargetFromFile(
appsPath: Nullable<string>,
): Promise<void> {
export function appsJsonBackup(appsPath: string) {
return `${appsPath}.preloaded`;
}
export async function loadTargetFromFile(appsPath: string): Promise<void> {
log.info('Attempting to load any preloaded applications');
if (!appsPath) {
appsPath = constants.appsJsonPath;
}
try {
const content = await fs.readFile(appsPath, 'utf8');
@ -73,7 +71,7 @@ export async function loadTargetFromFile(
}
for (const image of imgs) {
const name = await images.normalise(image.name);
const name = images.normalise(image.name);
image.name = name;
await images.save(image);
}
@ -114,5 +112,23 @@ export async function loadTargetFromFile(
error: e,
});
}
} finally {
const targetPath = appsJsonBackup(appsPath);
if (!(await fsUtils.exists(targetPath))) {
// Try to rename the path so the preload target state won't
// be used again if the database gets deleted for any reason.
// If the target file already exists or something fails, just debug
// the failure.
await fsUtils
.safeRename(appsPath, targetPath)
.then(() => fsUtils.writeFileAtomic(appsPath, '{}'))
.then(() => log.debug(`Migrated existing apps.json`))
.catch((e) =>
log.debug(
`Continuing without migrating apps.json because of`,
e.message,
),
);
}
}
}

View File

@ -1,3 +1,4 @@
import * as path from 'path';
import { checkString } from './validation';
const bootMountPointFromEnv = checkString(process.env.BOOT_MOUNTPOINT);
@ -49,7 +50,9 @@ const constants = {
'lo',
supervisorNetworkInterface,
],
appsJsonPath: process.env.APPS_JSON_PATH || '/boot/apps.json',
appsJsonPath:
process.env.APPS_JSON_PATH ||
path.join(rootMountPoint, '/mnt/data', 'apps.json'),
ipAddressUpdateInterval: 30 * 1000,
imageCleanupErrorIgnoreTimeout: 3600 * 1000,
maxDeltaDownloads: 3,

View File

@ -10,10 +10,14 @@ import * as images from '../src/compose/images';
import { ConfigTxt } from '../src/config/backends/config-txt';
import * as deviceState from '../src/device-state';
import * as deviceConfig from '../src/device-config';
import { loadTargetFromFile } from '../src/device-state/preload';
import {
loadTargetFromFile,
appsJsonBackup,
} from '../src/device-state/preload';
import Service from '../src/compose/service';
import { intialiseContractRequirements } from '../src/lib/contracts';
import * as updateLock from '../src/lib/update-lock';
import * as fsUtils from '../src/lib/fs-utils';
const mockedInitialConfig = {
RESIN_SUPERVISOR_CONNECTIVITY_CHECK: 'true',
@ -224,8 +228,10 @@ describe('deviceState', () => {
});
it('loads a target state from an apps.json file and saves it as target state, then returns it', async () => {
await loadTargetFromFile(process.env.ROOT_MOUNTPOINT + '/apps.json');
const appsJson = process.env.ROOT_MOUNTPOINT + '/apps.json';
await loadTargetFromFile(appsJson);
const targetState = await deviceState.getTarget();
expect(await fsUtils.exists(appsJsonBackup(appsJson))).to.be.true;
expect(targetState)
.to.have.property('local')
@ -283,14 +289,22 @@ describe('deviceState', () => {
.to.have.property('labels')
.that.has.property('io.balena.something')
.that.equals('bar');
// Restore renamed apps.json
await fsUtils.safeRename(appsJsonBackup(appsJson), appsJson);
});
it('stores info for pinning a device after loading an apps.json with a pinDevice field', async () => {
await loadTargetFromFile(process.env.ROOT_MOUNTPOINT + '/apps-pin.json');
const appsJson = process.env.ROOT_MOUNTPOINT + '/apps-pin.json';
await loadTargetFromFile(appsJson);
const pinned = await config.get('pinDevice');
expect(pinned).to.have.property('app').that.equals(1234);
expect(pinned).to.have.property('commit').that.equals('abcdef');
expect(await fsUtils.exists(appsJsonBackup(appsJson))).to.be.true;
// Restore renamed apps.json
await fsUtils.safeRename(appsJsonBackup(appsJson), appsJson);
});
it('emits a change event when a new state is reported', (done) => {
@ -303,7 +317,7 @@ describe('deviceState', () => {
const services: Service[] = [];
for (const service of testTarget.local.apps['1234'].services) {
const imageName = await images.normalise(service.image);
const imageName = images.normalise(service.image);
service.image = imageName;
(service as any).imageName = imageName;
services.push(