diff --git a/src/device-state.ts b/src/device-state.ts index 85e9c803..24ff24ee 100644 --- a/src/device-state.ts +++ b/src/device-state.ts @@ -638,7 +638,7 @@ export function reportCurrentState( } export async function reboot(force?: boolean, skipLock?: boolean) { - await updateLock.ensureNoHUPBreadcrumbsOnHost(); + await updateLock.abortIfHUPInProgress({ force }); await applicationManager.stopAll({ force, skipLock }); logger.logSystemMessage('Rebooting', {}, 'Reboot'); const $reboot = await dbus.reboot(); @@ -648,7 +648,7 @@ export async function reboot(force?: boolean, skipLock?: boolean) { } export async function shutdown(force?: boolean, skipLock?: boolean) { - await updateLock.ensureNoHUPBreadcrumbsOnHost(); + await updateLock.abortIfHUPInProgress({ force }); await applicationManager.stopAll({ force, skipLock }); logger.logSystemMessage('Shutting down', {}, 'Shutdown'); const $shutdown = await dbus.shutdown(); diff --git a/src/lib/update-lock.ts b/src/lib/update-lock.ts index ee5e8ff2..db1d24b2 100644 --- a/src/lib/update-lock.ts +++ b/src/lib/update-lock.ts @@ -42,7 +42,11 @@ function lockFilesOnHost(appId: number, serviceName: string): string[] { * prevent reboot. If the Supervisor reboots while those services are still running, * the device may become stuck in an invalid state during HUP. */ -export function ensureNoHUPBreadcrumbsOnHost(): Promise { +export function abortIfHUPInProgress({ + force = false, +}: { + force: boolean | undefined; +}): Promise { return Promise.all( [ 'rollback-health-breadcrumb', @@ -52,7 +56,7 @@ export function ensureNoHUPBreadcrumbsOnHost(): Promise { ), ).then((existsArray) => { const anyExists = existsArray.some((exists) => exists); - if (anyExists) { + if (anyExists && !force) { throw new UpdatesLockedError('Waiting for Host OS update to finish'); } return anyExists; diff --git a/test/05-device-state.spec.ts b/test/05-device-state.spec.ts index 34d9b3f7..5bcc3754 100644 --- a/test/05-device-state.spec.ts +++ b/test/05-device-state.spec.ts @@ -359,7 +359,7 @@ describe('deviceState', () => { it('prevents reboot or shutdown when HUP rollback breadcrumbs are present', async () => { const testErrMsg = 'Waiting for Host OS updates to finish'; - stub(updateLock, 'ensureNoHUPBreadcrumbsOnHost').throws( + stub(updateLock, 'abortIfHUPInProgress').throws( new UpdatesLockedError(testErrMsg), ); @@ -370,6 +370,6 @@ describe('deviceState', () => { .to.eventually.be.rejectedWith(testErrMsg) .and.be.an.instanceOf(UpdatesLockedError); - (updateLock.ensureNoHUPBreadcrumbsOnHost as SinonStub).restore(); + (updateLock.abortIfHUPInProgress as SinonStub).restore(); }); }); diff --git a/test/src/lib/update-lock.spec.ts b/test/src/lib/update-lock.spec.ts index 3aaa989e..9817e0a8 100644 --- a/test/src/lib/update-lock.spec.ts +++ b/test/src/lib/update-lock.spec.ts @@ -79,24 +79,33 @@ describe('lib/update-lock', () => { }); }); - describe('ensureNoHUPBreadcrumbsOnHost', () => { + describe('abortIfHUPInProgress', () => { afterEach(() => mockFs.restore()); it('should throw if any breadcrumbs exist on host', async () => { for (const bc of breadcrumbFiles) { mockBreadcrumbs(bc); - await expect(updateLock.ensureNoHUPBreadcrumbsOnHost()) + await expect(updateLock.abortIfHUPInProgress({ force: false })) .to.eventually.be.rejectedWith('Waiting for Host OS update to finish') .and.be.an.instanceOf(UpdatesLockedError); } }); - it('should resolve to true if no breadcrumbs on host', async () => { + it('should resolve to false if no breadcrumbs on host', async () => { mockBreadcrumbs(); await expect( - updateLock.ensureNoHUPBreadcrumbsOnHost(), + updateLock.abortIfHUPInProgress({ force: false }), ).to.eventually.equal(false); }); + + it('should resolve to true if breadcrumbs are on host but force is passed', async () => { + for (const bc of breadcrumbFiles) { + mockBreadcrumbs(bc); + await expect( + updateLock.abortIfHUPInProgress({ force: true }), + ).to.eventually.equal(true); + } + }); }); describe('Lock/dispose functionality', () => {