mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-07 03:40:16 +00:00
Move reboot breadcrumb check to device-state
This was on device-config before, but we'll need to set the reboot breadcrumb from the application-manager as well when we introduce `requires-reboot` as a label. Change-type: patch
This commit is contained in:
parent
51f1fb0f30
commit
75127c6074
@ -1,6 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import { inspect } from 'util';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
import * as config from '../config';
|
||||
import * as db from '../db';
|
||||
@ -10,8 +9,7 @@ import type { EnvVarObject } from '../types';
|
||||
import { UnitNotLoadedError } from '../lib/errors';
|
||||
import { checkInt, checkTruthy } from '../lib/validation';
|
||||
import log from '../lib/supervisor-console';
|
||||
import * as fsUtils from '../lib/fs-utils';
|
||||
import { pathOnRoot } from '../lib/host-utils';
|
||||
import { setRebootBreadcrumb } from '../lib/reboot';
|
||||
|
||||
import * as configUtils from '../config/utils';
|
||||
import type { SchemaTypeKey } from '../config/schema-type';
|
||||
@ -21,15 +19,6 @@ import { Odmdata } from '../config/backends/odmdata';
|
||||
|
||||
const vpnServiceName = 'openvpn';
|
||||
|
||||
// This indicates the file on the host /tmp directory that
|
||||
// marks the need for a reboot. Since reboot is only triggered for now
|
||||
// by some config changes, we leave this here for now. There is planned
|
||||
// functionality to allow image installs to require reboots, at that moment
|
||||
// this constant can be moved somewhere else
|
||||
const REBOOT_BREADCRUMB = pathOnRoot(
|
||||
'/tmp/balena-supervisor/reboot-after-apply',
|
||||
);
|
||||
|
||||
interface ConfigOption {
|
||||
envVarName: string;
|
||||
varType: string;
|
||||
@ -40,10 +29,7 @@ interface ConfigOption {
|
||||
// FIXME: Bring this and the deviceState and
|
||||
// applicationState steps together
|
||||
export interface ConfigStep {
|
||||
// TODO: This is a bit of a mess, the DeviceConfig class shouldn't
|
||||
// know that the reboot action exists as it is implemented by
|
||||
// DeviceState. Fix this weird circular dependency
|
||||
action: keyof DeviceActionExecutors | 'reboot' | 'noop';
|
||||
action: keyof DeviceActionExecutors | 'noop';
|
||||
humanReadableTarget?: Dictionary<string>;
|
||||
target?: string | Dictionary<string>;
|
||||
}
|
||||
@ -118,11 +104,7 @@ const actionExecutors: DeviceActionExecutors = {
|
||||
await setBootConfig(backend, step.target as Dictionary<string>);
|
||||
}
|
||||
},
|
||||
setRebootBreadcrumb: async () => {
|
||||
// Just create the file. The last step in the target state calculation will check
|
||||
// the file and create a reboot step
|
||||
await fsUtils.touch(REBOOT_BREADCRUMB);
|
||||
},
|
||||
setRebootBreadcrumb,
|
||||
};
|
||||
|
||||
const configBackends: ConfigBackend[] = [];
|
||||
@ -551,18 +533,6 @@ async function getBackendSteps(
|
||||
];
|
||||
}
|
||||
|
||||
async function isRebootRequired() {
|
||||
const hasBreadcrumb = await fsUtils.exists(REBOOT_BREADCRUMB);
|
||||
if (hasBreadcrumb) {
|
||||
const stats = await fs.stat(REBOOT_BREADCRUMB);
|
||||
|
||||
// If the breadcrumb exists and the last modified time is greater than the
|
||||
// boot time, that means we need to reboot
|
||||
return stats.mtime.getTime() > fsUtils.getBootTime().getTime();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function getRequiredSteps(
|
||||
currentState: { local?: { config?: EnvVarObject } },
|
||||
targetState: { local?: { config: EnvVarObject } },
|
||||
@ -585,19 +555,6 @@ export async function getRequiredSteps(
|
||||
: await getBackendSteps(current, target)),
|
||||
];
|
||||
|
||||
// Check if there is either no steps, or they are all
|
||||
// noops, and we need to reboot. We want to do this
|
||||
// because in a preloaded setting with no internet
|
||||
// connection, the device will try to start containers
|
||||
// before any boot config has been applied, which can
|
||||
// cause problems
|
||||
const rebootRequired = await isRebootRequired();
|
||||
if (_.every(steps, { action: 'noop' }) && rebootRequired) {
|
||||
steps.push({
|
||||
action: 'reboot',
|
||||
});
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
@ -643,7 +600,7 @@ export function executeStepAction(
|
||||
step: ConfigStep,
|
||||
opts: DeviceActionExecutorOpts,
|
||||
) {
|
||||
if (step.action !== 'reboot' && step.action !== 'noop') {
|
||||
if (step.action !== 'noop') {
|
||||
return actionExecutors[step.action](step, opts);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import * as updateLock from '../lib/update-lock';
|
||||
import { getGlobalApiKey } from '../lib/api-keys';
|
||||
import * as sysInfo from '../lib/system-info';
|
||||
import { log } from '../lib/supervisor-console';
|
||||
import { isRebootRequired } from '../lib/reboot';
|
||||
import { loadTargetFromFile } from './preload';
|
||||
import * as applicationManager from '../compose/application-manager';
|
||||
import * as commitStore from '../compose/commit';
|
||||
@ -518,7 +519,7 @@ export async function executeStepAction(
|
||||
}
|
||||
}
|
||||
|
||||
export async function applyStep(
|
||||
async function applyStep(
|
||||
step: DeviceStateStep<PossibleStepTargets>,
|
||||
{
|
||||
force,
|
||||
@ -615,11 +616,12 @@ export const applyTarget = async ({
|
||||
({ action }) => action === 'noop',
|
||||
);
|
||||
|
||||
let backoff: boolean;
|
||||
const rebootRequired = await isRebootRequired();
|
||||
|
||||
let backoff = false;
|
||||
let steps: Array<DeviceStateStep<PossibleStepTargets>>;
|
||||
|
||||
if (!noConfigSteps) {
|
||||
backoff = false;
|
||||
steps = deviceConfigSteps;
|
||||
} else {
|
||||
const appSteps = await applicationManager.getRequiredSteps(
|
||||
@ -646,6 +648,21 @@ export const applyTarget = async ({
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is either no steps, or they are all
|
||||
// noops, and we need to reboot. We want to do this
|
||||
// because in a preloaded setting with no internet
|
||||
// connection, the device will try to start containers
|
||||
// before any boot config has been applied, which can
|
||||
// cause problems
|
||||
// For application manager, the reboot breadcrumb should
|
||||
// be set after all downloads are ready and target containers
|
||||
// have been installed
|
||||
if (_.every(steps, ({ action }) => action === 'noop') && rebootRequired) {
|
||||
steps.push({
|
||||
action: 'reboot',
|
||||
});
|
||||
}
|
||||
|
||||
if (_.isEmpty(steps)) {
|
||||
emitAsync('apply-target-state-end', null);
|
||||
if (!intermediate) {
|
||||
|
30
src/lib/reboot.ts
Normal file
30
src/lib/reboot.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { pathOnRoot } from '../lib/host-utils';
|
||||
import * as fsUtils from '../lib/fs-utils';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
// This indicates the file on the host /tmp directory that
|
||||
// marks the need for a reboot. Since reboot is only triggered for now
|
||||
// by some config changes, we leave this here for now. There is planned
|
||||
// functionality to allow image installs to require reboots, at that moment
|
||||
// this constant can be moved somewhere else
|
||||
const REBOOT_BREADCRUMB = pathOnRoot(
|
||||
'/tmp/balena-supervisor/reboot-after-apply',
|
||||
);
|
||||
|
||||
export async function setRebootBreadcrumb() {
|
||||
// Just create the file. The last step in the target state calculation will check
|
||||
// the file and create a reboot step
|
||||
await fsUtils.touch(REBOOT_BREADCRUMB);
|
||||
}
|
||||
|
||||
export async function isRebootRequired() {
|
||||
const hasBreadcrumb = await fsUtils.exists(REBOOT_BREADCRUMB);
|
||||
if (hasBreadcrumb) {
|
||||
const stats = await fs.stat(REBOOT_BREADCRUMB);
|
||||
|
||||
// If the breadcrumb exists and the last modified time is greater than the
|
||||
// boot time, that means we need to reboot
|
||||
return stats.mtime.getTime() > fsUtils.getBootTime().getTime();
|
||||
}
|
||||
return false;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user