mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-06-22 00:57:24 +00:00
Add update lock release functionality to state funnel
releaseLock is a step that will be inferred if there are services in target state, and if some of those services have locks taken by the Supervisor. The releaseLock composition step calls the method of the same name in the updateLock module, which takes the exclusive process lock before disposing all Supervisor lockfiles in the target appId. This is half of the update lock incorporation into the state funnel, as we also need to introduce a takeLock step which triggers during crucial stages of device state transition. Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
@ -25,6 +25,7 @@ import { checkTruthy } from '../lib/validation';
|
||||
import type { ServiceComposeConfig, DeviceMetadata } from './types/service';
|
||||
import { pathExistsOnRoot } from '../lib/host-utils';
|
||||
import { isSupervisor } from '../lib/supervisor-metadata';
|
||||
import type { LocksTakenMap } from '../lib/update-lock';
|
||||
|
||||
export interface AppConstructOpts {
|
||||
appId: number;
|
||||
@ -43,6 +44,7 @@ export interface UpdateState {
|
||||
availableImages: Image[];
|
||||
containerIds: Dictionary<string>;
|
||||
downloading: string[];
|
||||
locksTaken: LocksTakenMap;
|
||||
}
|
||||
|
||||
interface ChangingPair<T> {
|
||||
@ -166,17 +168,29 @@ export class App {
|
||||
),
|
||||
);
|
||||
|
||||
if (
|
||||
steps.length === 0 &&
|
||||
target.commit != null &&
|
||||
this.commit !== target.commit
|
||||
) {
|
||||
steps.push(
|
||||
generateStep('updateCommit', {
|
||||
target: target.commit,
|
||||
appId: this.appId,
|
||||
}),
|
||||
);
|
||||
if (steps.length === 0) {
|
||||
// Update commit in db if different
|
||||
if (target.commit != null && this.commit !== target.commit) {
|
||||
steps.push(
|
||||
generateStep('updateCommit', {
|
||||
target: target.commit,
|
||||
appId: this.appId,
|
||||
}),
|
||||
);
|
||||
} else if (
|
||||
target.services.length > 0 &&
|
||||
target.services.some(({ appId, serviceName }) =>
|
||||
state.locksTaken.isLocked(appId, serviceName),
|
||||
)
|
||||
) {
|
||||
// Release locks for current services before settling state.
|
||||
// Current services should be the same as target services at this point.
|
||||
steps.push(
|
||||
generateStep('releaseLock', {
|
||||
appId: target.appId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
ContractViolationError,
|
||||
InternalInconsistencyError,
|
||||
} from '../lib/errors';
|
||||
import { lock } from '../lib/update-lock';
|
||||
import { getServicesLockedByAppId, lock } from '../lib/update-lock';
|
||||
import { checkTruthy } from '../lib/validation';
|
||||
|
||||
import App from './app';
|
||||
@ -149,6 +149,7 @@ export async function getRequiredSteps(
|
||||
downloading,
|
||||
availableImages,
|
||||
containerIdsByAppId,
|
||||
locksTaken: getServicesLockedByAppId(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -165,6 +166,7 @@ export async function inferNextSteps(
|
||||
containerIdsByAppId = {} as {
|
||||
[appId: number]: UpdateState['containerIds'];
|
||||
},
|
||||
locksTaken = getServicesLockedByAppId(),
|
||||
} = {},
|
||||
) {
|
||||
const currentAppIds = Object.keys(currentApps).map((i) => parseInt(i, 10));
|
||||
@ -216,6 +218,7 @@ export async function inferNextSteps(
|
||||
availableImages,
|
||||
containerIds: containerIdsByAppId[id],
|
||||
downloading,
|
||||
locksTaken,
|
||||
},
|
||||
targetApps[id],
|
||||
),
|
||||
@ -229,6 +232,7 @@ export async function inferNextSteps(
|
||||
keepVolumes,
|
||||
downloading,
|
||||
containerIds: containerIdsByAppId[id],
|
||||
locksTaken,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@ -252,6 +256,7 @@ export async function inferNextSteps(
|
||||
availableImages,
|
||||
containerIds: containerIdsByAppId[id] ?? {},
|
||||
downloading,
|
||||
locksTaken,
|
||||
},
|
||||
targetApps[id],
|
||||
),
|
||||
|
@ -11,6 +11,7 @@ import * as volumeManager from './volume-manager';
|
||||
import type Volume from './volume';
|
||||
import * as commitStore from './commit';
|
||||
import { checkTruthy } from '../lib/validation';
|
||||
import * as updateLock from '../lib/update-lock';
|
||||
import type { DeviceLegacyReport } from '../types/state';
|
||||
|
||||
interface BaseCompositionStepArgs {
|
||||
@ -94,6 +95,12 @@ interface CompositionStepArgs {
|
||||
};
|
||||
ensureSupervisorNetwork: object;
|
||||
noop: object;
|
||||
takeLock: {
|
||||
appId: number;
|
||||
};
|
||||
releaseLock: {
|
||||
appId: number;
|
||||
};
|
||||
}
|
||||
|
||||
export type CompositionStepAction = keyof CompositionStepArgs;
|
||||
@ -276,6 +283,12 @@ export function getExecutors(app: {
|
||||
noop: async () => {
|
||||
/* async noop */
|
||||
},
|
||||
takeLock: async () => {
|
||||
// TODO
|
||||
},
|
||||
releaseLock: async (step) => {
|
||||
await updateLock.releaseLock(step.appId);
|
||||
},
|
||||
};
|
||||
|
||||
return executors;
|
||||
|
Reference in New Issue
Block a user