mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-19 21:57:54 +00:00
Remove apps.json after initial preload
This avoids the supervisor trying to get back to the preloaded target state if the database is deleted by any reason. It does this by moving the used apps.json to a backup location. Change-type: patch Depends-on: #1841
This commit is contained in:
parent
0d4c902e35
commit
9c6e5ee11f
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user