Honor configured ZFS pool everywhere

Codex caught zroot hardcodes in setup/sanoid.ts and setup/db.ts; same
pattern remained in three more shipping locations:

- scripts/backup.ts: jail and shared dataset paths
- src/tenant-registry.ts: default tenant dataset list
- setup/sanoid.ts: npm-global retention candidate

Add zfsPool() helper to maintenance-snapshots.ts (where the analogous
buildHostDbDatasets reads ZFS_POOL) and use it in all three. Operators
running on non-default pools no longer get silently-wrong dataset paths
in backup, tenant provisioning, or sanoid retention.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---
Build: pass | Tests: pass — Tests  2099 passed (2099)
This commit is contained in:
Operator & Claude Code 2026-05-02 08:20:32 +02:00
parent 316a70c86c
commit 00a908306d
4 changed files with 17 additions and 9 deletions

View file

@ -44,6 +44,7 @@ import {
} from '../src/config.js'; } from '../src/config.js';
import { formatDisplayDate, formatSnapshotStamp } from '../src/display-date.js'; import { formatDisplayDate, formatSnapshotStamp } from '../src/display-date.js';
import { hostd } from '../src/hostd/client.js'; import { hostd } from '../src/hostd/client.js';
import { zfsPool } from '../src/maintenance-snapshots.js';
// ── CLI args ────────────────────────────────────────────────────────────────── // ── CLI args ──────────────────────────────────────────────────────────────────
@ -151,17 +152,18 @@ function datasetExists(datasets: ZfsDatasetInfo[], name: string): boolean {
} }
function buildSnapshotTargets(): string[] { function buildSnapshotTargets(): string[] {
const pool = zfsPool();
const targets = [ const targets = [
`zroot/${ZFS_PREFIX}/jails/${TENANT_ID}-db`, `${pool}/${ZFS_PREFIX}/jails/${TENANT_ID}-db`,
`zroot/${ZFS_PREFIX}/jails/${TENANT_ID}-git`, `${pool}/${ZFS_PREFIX}/jails/${TENANT_ID}-git`,
`zroot/${ZFS_PREFIX}/jails/${TENANT_ID}-cms`, `${pool}/${ZFS_PREFIX}/jails/${TENANT_ID}-cms`,
]; ];
const datasets = listZfsDatasets(); const datasets = listZfsDatasets();
const homeDataset = detectHomeDataset(datasets, AGENT_HOME); const homeDataset = detectHomeDataset(datasets, AGENT_HOME);
if (homeDataset) targets.push(homeDataset); if (homeDataset) targets.push(homeDataset);
const npmGlobalDataset = `zroot/${ZFS_PREFIX}/shared/npm-global`; const npmGlobalDataset = `${pool}/${ZFS_PREFIX}/shared/npm-global`;
if (datasetExists(datasets, npmGlobalDataset)) { if (datasetExists(datasets, npmGlobalDataset)) {
targets.push(npmGlobalDataset); targets.push(npmGlobalDataset);
} }

View file

@ -18,7 +18,7 @@ import {
} from '../src/config.js'; } from '../src/config.js';
import { readEnvFile } from '../src/env.js'; import { readEnvFile } from '../src/env.js';
import { logger } from '../src/logger.js'; import { logger } from '../src/logger.js';
import { buildHostDbDatasets } from '../src/maintenance-snapshots.js'; import { buildHostDbDatasets, zfsPool } from '../src/maintenance-snapshots.js';
import { commandExists, getPlatform, isRoot } from './platform.js'; import { commandExists, getPlatform, isRoot } from './platform.js';
import { emitStatus } from './status.js'; import { emitStatus } from './status.js';
@ -277,8 +277,9 @@ export async function run(_args: string[]): Promise<void> {
: homeDataset && homePolicy === 'minimal' : homeDataset && homePolicy === 'minimal'
? 'operator_home_minimal' ? 'operator_home_minimal'
: null; : null;
const npmGlobalDataset = datasetExists(datasets, `zroot/${prefix}/shared/npm-global`) const npmGlobalCandidate = `${zfsPool()}/${prefix}/shared/npm-global`;
? `zroot/${prefix}/shared/npm-global` const npmGlobalDataset = datasetExists(datasets, npmGlobalCandidate)
? npmGlobalCandidate
: null; : null;
const conf = buildSanoidConf(datasets, { homeDataset, homeTemplate, npmGlobalDataset }); const conf = buildSanoidConf(datasets, { homeDataset, homeTemplate, npmGlobalDataset });

View file

@ -26,8 +26,12 @@ export type HostdLike = (
params?: Record<string, string | number | boolean>, params?: Record<string, string | number | boolean>,
) => Promise<HostdLikeResponse>; ) => Promise<HostdLikeResponse>;
export function zfsPool(): string {
return (process.env.ZFS_POOL || 'zroot').trim() || 'zroot';
}
export function buildHostDbDatasets(): { data: string; wal: string } { export function buildHostDbDatasets(): { data: string; wal: string } {
const pool = (process.env.ZFS_POOL || 'zroot').trim() || 'zroot'; const pool = zfsPool();
const prefix = (ZFS_PREFIX || '').trim(); const prefix = (ZFS_PREFIX || '').trim();
return { return {
data: `${pool}/${prefix}/pgdata`, data: `${pool}/${prefix}/pgdata`,

View file

@ -6,6 +6,7 @@ import { Document, isMap, parse, parseDocument, stringify } from 'yaml';
import { z } from 'zod'; import { z } from 'zod';
import { normalizeDbIdentifierBase } from './db-identifiers.js'; import { normalizeDbIdentifierBase } from './db-identifiers.js';
import { zfsPool } from './maintenance-snapshots.js';
import { import {
canonicalDatasetPath, canonicalDatasetPath,
datasetsOverlap, datasetsOverlap,
@ -437,7 +438,7 @@ export function deriveTenantRecord(
tenantWorkerJailName(id, 'db'), tenantWorkerJailName(id, 'db'),
tenantWorkerJailName(id, 'git'), tenantWorkerJailName(id, 'git'),
], ],
datasets: options.datasets || [`zroot/${id}-ai`], datasets: options.datasets || [`${zfsPool()}/${id}-ai`],
}; };
} }