141 lines
4.2 KiB
TypeScript
141 lines
4.2 KiB
TypeScript
import { execSync } from 'child_process';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
import { readEnvFile } from '../src/env.js';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const PACKAGE_DIR = path.resolve(__dirname, '../infra/packages');
|
|
|
|
export type PackageListName =
|
|
| 'host-baseline.txt'
|
|
| 'worker-jail.txt'
|
|
| 'agent-worker-jail.txt'
|
|
| 'git-jail.txt'
|
|
| 'forgejo-jail.txt'
|
|
| 'cms-jail.txt'
|
|
| 'db-jail.txt'
|
|
| 'browser-jail.txt'
|
|
| 'ollama-jail.txt'
|
|
| 'llama-cpp-jail.txt'
|
|
| 'management-jail.txt'
|
|
| 'cnc-jail.txt';
|
|
|
|
export function loadPackageList(name: PackageListName): string[] {
|
|
const file = path.join(PACKAGE_DIR, name);
|
|
const content = fs.readFileSync(file, 'utf-8');
|
|
return content
|
|
.split('\n')
|
|
.map((line) => line.trim())
|
|
.filter((line) => line.length > 0 && !line.startsWith('#'));
|
|
}
|
|
|
|
/**
|
|
* Nullfs-mount the shared host pkg cache into a jail.
|
|
* Idempotent — checks the jail fstab before adding the mount.
|
|
* Silently skips if the cache dataset or bastille fstab don't exist yet.
|
|
* Call this after jail creation and before any bastille pkg install.
|
|
*/
|
|
export function mountPkgCacheInJail(jailName: string): void {
|
|
const fstabPath = `/usr/local/bastille/jails/${jailName}/fstab`;
|
|
if (!fs.existsSync('/var/cache/pkg')) return;
|
|
|
|
const desiredLine = `/var/cache/pkg /usr/local/bastille/jails/${jailName}/root/var/cache/pkg nullfs rw 0 0`;
|
|
if (fs.existsSync(fstabPath)) {
|
|
const existing = fs.readFileSync(fstabPath, 'utf-8');
|
|
if (existing.includes(desiredLine)) return;
|
|
|
|
if (existing.includes('/var/cache/pkg') && existing.includes(' nullfs ro ')) {
|
|
const updated = existing.replace(
|
|
/^\/var\/cache\/pkg\s+.*?\/var\/cache\/pkg\s+nullfs\s+ro\s+0\s+0\s*$/gmu,
|
|
desiredLine,
|
|
);
|
|
fs.writeFileSync(fstabPath, updated);
|
|
try {
|
|
execSync(`bastille umount -a ${jailName} /var/cache/pkg`, { stdio: 'ignore' });
|
|
} catch {
|
|
// ignore — may not be mounted yet
|
|
}
|
|
try {
|
|
execSync(`bastille mount ${jailName} /var/cache/pkg /var/cache/pkg nullfs rw 0 0`, {
|
|
stdio: 'ignore',
|
|
});
|
|
} catch {
|
|
// mount failed — jail pkg installs will fall back to network downloads
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (existing.includes('/var/cache/pkg')) return;
|
|
}
|
|
|
|
try {
|
|
execSync(
|
|
`bastille mount ${jailName} /var/cache/pkg /var/cache/pkg nullfs rw 0 0`,
|
|
{
|
|
stdio: 'ignore',
|
|
},
|
|
);
|
|
} catch {
|
|
// mount failed — jail pkg installs will fall back to network downloads
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the pkg-cache nullfs mount from a jail and clean any matching fstab
|
|
* entry. Safe to call even if the mount was never added.
|
|
*/
|
|
export function unmountPkgCacheInJail(jailName: string): void {
|
|
const fstabPath = `/usr/local/bastille/jails/${jailName}/fstab`;
|
|
|
|
try {
|
|
execSync(`bastille umount ${jailName} /var/cache/pkg`, { stdio: 'ignore' });
|
|
} catch {
|
|
// ignore — may already be unmounted or absent
|
|
}
|
|
|
|
if (!fs.existsSync(fstabPath)) return;
|
|
|
|
const existing = fs.readFileSync(fstabPath, 'utf-8');
|
|
const updated = existing.replace(
|
|
/^\/var\/cache\/pkg\s+.*?\/var\/cache\/pkg\s+nullfs\s+(?:ro|rw)\s+0\s+0\s*$/gmu,
|
|
'',
|
|
);
|
|
|
|
if (updated !== existing) {
|
|
fs.writeFileSync(fstabPath, updated.replace(/\n{3,}/g, '\n\n'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load and deduplicate all package lists (host + all jail roles).
|
|
* This is the authoritative set for prefetching to the shared pkg cache.
|
|
*/
|
|
export function loadAllPackageLists(): string[] {
|
|
const envValues = readEnvFile(['FEATURE_TAILSCALE']);
|
|
const tailscaleEnabled = /^(YES|yes|true|TRUE|1)$/u.test(
|
|
process.env.FEATURE_TAILSCALE || envValues.FEATURE_TAILSCALE || '',
|
|
);
|
|
const all = (
|
|
[
|
|
'host-baseline.txt',
|
|
'worker-jail.txt',
|
|
'agent-worker-jail.txt',
|
|
'git-jail.txt',
|
|
'forgejo-jail.txt',
|
|
'cms-jail.txt',
|
|
'db-jail.txt',
|
|
'browser-jail.txt',
|
|
'ollama-jail.txt',
|
|
'llama-cpp-jail.txt',
|
|
'management-jail.txt',
|
|
'cnc-jail.txt',
|
|
] as PackageListName[]
|
|
).flatMap(loadPackageList);
|
|
if (tailscaleEnabled) {
|
|
all.push('tailscale');
|
|
}
|
|
return [...new Set(all)];
|
|
}
|