fix(fpagent): sweep scratch left by a SIGKILLed installer run
The EXIT trap cannot run on SIGKILL, so a killed run could leave its fpagent-install.* temp dir behind. Sweep those before any network work, scoped to our prefix and this user and only dirs older than a day so a concurrent install is never touched.
This commit is contained in:
@@ -4,11 +4,21 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createApp, FPAGENT_REPO } from '../server.js';
|
||||
import { startServer, stopServer, urlOf } from './helpers.js';
|
||||
|
||||
const SCRIPT = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..',
|
||||
'public',
|
||||
'fpagent',
|
||||
'install.sh'
|
||||
);
|
||||
|
||||
const TOKEN = 'release-install-secret';
|
||||
const TAG = 'v9.9.9';
|
||||
const ARCHIVE = 'fpagent-deadbee-linux-x86_64.tar.gz';
|
||||
@@ -150,12 +160,37 @@ test('install.sh is served publicly at /fpagent/install.sh', async () => {
|
||||
});
|
||||
|
||||
test('install.sh is valid bash', () => {
|
||||
const script = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..',
|
||||
'public',
|
||||
'fpagent',
|
||||
'install.sh'
|
||||
);
|
||||
execFileSync('bash', ['-n', script], { stdio: 'pipe' });
|
||||
execFileSync('bash', ['-n', SCRIPT], { stdio: 'pipe' });
|
||||
});
|
||||
|
||||
test('install.sh sweeps only its own stale scratch dirs', () => {
|
||||
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'fpagent-sweep-test-'));
|
||||
const stale = path.join(base, 'fpagent-install.oldoldold');
|
||||
const fresh = path.join(base, 'fpagent-install.freshfresh');
|
||||
const unrelated = path.join(base, 'someone-else.oldold');
|
||||
for (const dir of [stale, fresh, unrelated]) fs.mkdirSync(dir);
|
||||
const old = new Date(Date.now() - 2 * 24 * 60 * 60 * 1000);
|
||||
fs.utimesSync(stale, old, old);
|
||||
fs.utimesSync(unrelated, old, old);
|
||||
|
||||
try {
|
||||
// The base URL is unreachable, so the run fails after the sweep: enough to
|
||||
// exercise the cleanup without downloading anything.
|
||||
let failed = false;
|
||||
try {
|
||||
execFileSync('bash', [SCRIPT], {
|
||||
stdio: 'pipe',
|
||||
env: { ...process.env, TMPDIR: base, FPAGENT_BASE_URL: 'http://127.0.0.1:9' },
|
||||
});
|
||||
} catch {
|
||||
failed = true;
|
||||
}
|
||||
assert.equal(failed, true, 'the installer should fail on the unreachable base');
|
||||
|
||||
assert.equal(fs.existsSync(stale), false, 'stale fpagent scratch should be removed');
|
||||
assert.equal(fs.existsSync(fresh), true, 'fresh (possibly concurrent) scratch should survive');
|
||||
assert.equal(fs.existsSync(unrelated), true, 'another prefix should never be touched');
|
||||
} finally {
|
||||
fs.rmSync(base, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user