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 eventTracker from '../event-tracker';
import * as images from '../compose/images'; import * as images from '../compose/images';
import constants = require('../lib/constants');
import { AppsJsonParseError, EISDIR, ENOENT } from '../lib/errors'; import { AppsJsonParseError, EISDIR, ENOENT } from '../lib/errors';
import log from '../lib/supervisor-console'; import log from '../lib/supervisor-console';
import { convertLegacyAppsJson } from '../lib/migration'; import { convertLegacyAppsJson } from '../lib/migration';
import { AppsJsonFormat } from '../types/state'; import { AppsJsonFormat } from '../types/state';
import * as fsUtils from '../lib/fs-utils';
export async function loadTargetFromFile( export function appsJsonBackup(appsPath: string) {
appsPath: Nullable<string>, return `${appsPath}.preloaded`;
): Promise<void> { }
export async function loadTargetFromFile(appsPath: string): Promise<void> {
log.info('Attempting to load any preloaded applications'); log.info('Attempting to load any preloaded applications');
if (!appsPath) {
appsPath = constants.appsJsonPath;
}
try { try {
const content = await fs.readFile(appsPath, 'utf8'); const content = await fs.readFile(appsPath, 'utf8');
@ -73,7 +71,7 @@ export async function loadTargetFromFile(
} }
for (const image of imgs) { for (const image of imgs) {
const name = await images.normalise(image.name); const name = images.normalise(image.name);
image.name = name; image.name = name;
await images.save(image); await images.save(image);
} }
@ -114,5 +112,23 @@ export async function loadTargetFromFile(
error: e, 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'; import { checkString } from './validation';
const bootMountPointFromEnv = checkString(process.env.BOOT_MOUNTPOINT); const bootMountPointFromEnv = checkString(process.env.BOOT_MOUNTPOINT);
@ -49,7 +50,9 @@ const constants = {
'lo', 'lo',
supervisorNetworkInterface, 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, ipAddressUpdateInterval: 30 * 1000,
imageCleanupErrorIgnoreTimeout: 3600 * 1000, imageCleanupErrorIgnoreTimeout: 3600 * 1000,
maxDeltaDownloads: 3, maxDeltaDownloads: 3,

View File

@ -10,10 +10,14 @@ import * as images from '../src/compose/images';
import { ConfigTxt } from '../src/config/backends/config-txt'; import { ConfigTxt } from '../src/config/backends/config-txt';
import * as deviceState from '../src/device-state'; import * as deviceState from '../src/device-state';
import * as deviceConfig from '../src/device-config'; 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 Service from '../src/compose/service';
import { intialiseContractRequirements } from '../src/lib/contracts'; import { intialiseContractRequirements } from '../src/lib/contracts';
import * as updateLock from '../src/lib/update-lock'; import * as updateLock from '../src/lib/update-lock';
import * as fsUtils from '../src/lib/fs-utils';
const mockedInitialConfig = { const mockedInitialConfig = {
RESIN_SUPERVISOR_CONNECTIVITY_CHECK: 'true', 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 () => { 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(); const targetState = await deviceState.getTarget();
expect(await fsUtils.exists(appsJsonBackup(appsJson))).to.be.true;
expect(targetState) expect(targetState)
.to.have.property('local') .to.have.property('local')
@ -283,14 +289,22 @@ describe('deviceState', () => {
.to.have.property('labels') .to.have.property('labels')
.that.has.property('io.balena.something') .that.has.property('io.balena.something')
.that.equals('bar'); .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 () => { 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'); const pinned = await config.get('pinDevice');
expect(pinned).to.have.property('app').that.equals(1234); expect(pinned).to.have.property('app').that.equals(1234);
expect(pinned).to.have.property('commit').that.equals('abcdef'); 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) => { it('emits a change event when a new state is reported', (done) => {
@ -303,7 +317,7 @@ describe('deviceState', () => {
const services: Service[] = []; const services: Service[] = [];
for (const service of testTarget.local.apps['1234'].services) { 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.image = imageName;
(service as any).imageName = imageName; (service as any).imageName = imageName;
services.push( services.push(