mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-01-31 00:23:57 +00:00
Allow directories to be used as lockfiles
Some libraries, like [proper-lockfile](https://www.npmjs.com/package/proper-lockfile) use directories instead of files for locking. This PR allows the supervisor to be able to work with those types of locks when lock override is requested. Closes: #1978 Change-type: patch
This commit is contained in:
parent
d11d4fba91
commit
861e902d7f
@ -1,8 +1,8 @@
|
||||
import * as fs from 'fs';
|
||||
import { promises as fs, unlinkSync, rmdirSync } from 'fs';
|
||||
import * as os from 'os';
|
||||
import { dirname } from 'path';
|
||||
|
||||
import { exec, unlinkAll } from './fs-utils';
|
||||
import { exec } from './fs-utils';
|
||||
|
||||
// Equivalent to `drwxrwxrwt`
|
||||
const STICKY_WRITE_PERMISSIONS = 0o1777;
|
||||
@ -66,7 +66,7 @@ export async function lock(path: string, uid: number = os.userInfo().uid) {
|
||||
* `chmod` does not fail or throw if the directory already has the proper permissions.
|
||||
*/
|
||||
if (uid !== 0) {
|
||||
await fs.promises.chmod(dirname(path), STICKY_WRITE_PERMISSIONS);
|
||||
await fs.chmod(dirname(path), STICKY_WRITE_PERMISSIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,11 +108,28 @@ export async function lock(path: string, uid: number = os.userInfo().uid) {
|
||||
|
||||
export async function unlock(path: string): Promise<void> {
|
||||
// Removing the lockfile releases the lock
|
||||
await unlinkAll(path);
|
||||
await fs.unlink(path).catch((e) => {
|
||||
// if the error is EPERM, the file is a directory
|
||||
if (e.code === 'EPERM') {
|
||||
return fs.rmdir(path).catch(() => {
|
||||
// if the directory is not empty or something else
|
||||
// happens, ignore
|
||||
});
|
||||
}
|
||||
// If the file does not exist or some other error
|
||||
// happens, then ignore the error
|
||||
});
|
||||
// Remove lockfile's in-memory tracking of a file
|
||||
delete locksTaken[path];
|
||||
}
|
||||
|
||||
export function unlockSync(path: string) {
|
||||
return fs.unlinkSync(path);
|
||||
try {
|
||||
return unlinkSync(path);
|
||||
} catch (e) {
|
||||
if (e.code === 'EPERM') {
|
||||
return rmdirSync(path);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ describe('lib/lockfile', () => {
|
||||
},
|
||||
'7654321': {
|
||||
two: opts.createLock
|
||||
? { 'updates.lock': mock.file({ uid: LOCKFILE_UID }) }
|
||||
? { 'updates.lock': mock.directory({ uid: LOCKFILE_UID }) }
|
||||
: {},
|
||||
},
|
||||
},
|
||||
@ -127,9 +127,11 @@ describe('lib/lockfile', () => {
|
||||
await checkLockDirFiles(lockPath, { shouldExist: true });
|
||||
|
||||
await lockfile.unlock(lockPath);
|
||||
await lockfile.unlock(lockPath2);
|
||||
|
||||
// Verify lockfile removal
|
||||
await checkLockDirFiles(lockPath, { shouldExist: false });
|
||||
await checkLockDirFiles(lockPath2, { shouldExist: false });
|
||||
});
|
||||
|
||||
it('should not error on async unlock if lockfile does not exist', async () => {
|
||||
@ -149,11 +151,15 @@ describe('lib/lockfile', () => {
|
||||
mockDir({ createLock: true });
|
||||
|
||||
lockfile.unlockSync(lockPath);
|
||||
lockfile.unlockSync(lockPath2);
|
||||
|
||||
// Verify lockfile does not exist
|
||||
return checkLockDirFiles(lockPath, { shouldExist: false }).catch((err) => {
|
||||
expect.fail((err as Error)?.message ?? err);
|
||||
});
|
||||
return Promise.all([
|
||||
checkLockDirFiles(lockPath, { shouldExist: false }).catch((err) => {
|
||||
expect.fail((err as Error)?.message ?? err);
|
||||
}),
|
||||
checkLockDirFiles(lockPath2, { shouldExist: false }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should try to clean up existing locks on process exit', async () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user