Files
mesh-catalog/modules/zfs/cmd/zfs-tools/main.go
T
jochen bd0182785c
mesh/merge-gate pass: builds zfs, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
Run the scrub schedule as a scheduled process, never pause a scrub, and keep the pool's name the only setting in a unit
The second review of #147 found three blockers before the home server.

1. ${setting:} was not filled in a unit's name, so the distribution timers would have reached the
   machine as "zfs-scrub-weekly@${setting:scrub-pool}.timer". mesh-controller #171 fills it and refuses
   any value that is not a unit-name part. The module's own schedule needs no setting in a name now: the
   node-engine's timer for the scheduled process zfs-mesh-scrub fires `zfs-tools scrub-run` every quarter
   of an hour, and the run reads the pool, cadence, day and time from the configuration file.
2. The run does what upstream's zfs-scrub@.service does: if a scrub is running it waits for it with
   zpool wait -t scrub, otherwise it starts one and waits. It never pauses or stops a scrub, and the unit
   has no ExecStop. A resilver defers the scrub. The served slot is kept in /var/lib/zfs-tools.
3. Unassigning left the pool with no schedule. mesh-host #66 (hq ADR 0269, proposed) gives the
   distribution timer back running and enabled. The README says not to unassign before it is merged.

Also: the run comes before the distribution timers in the declaration; the day and time are checked
against fixed sets and reach no unit file (an invalid value runs nothing); a due scrub not started an
hour after its time is a warning; the error counts outlive a restart in a small state file.
2026-10-09 01:40:02 +02:00

57 lines
1.9 KiB
Go

// The zfs module's Go bundle: the mesh's view of ZFS on a machine — its pools' health, layout, space,
// scrubs, I/O, cache and events, every dataset with its space and snapshots — and one act, taking a named
// snapshot of one dataset.
//
// One binary, four ways to run. With no argument it is the bundle the node's tool runner launches and
// speaks MCP to over stdio (novox/hq ADR 0188, ADR 0193), as the operator account. With `watch` and
// `events` it is one of the module's two long-running processes, run by the node-engine: the watch, which
// the health check zfs_health stands on (watch.go), and the event follower, as root, judged by its unit
// (follow.go). With `scrub-run` it is the scheduled scrub, run as root by the node-engine's timer
// (schedule.go).
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
stdio "git.novox.be/novox/mesh-sdk/go"
)
func main() {
if len(os.Args) > 1 {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt)
defer stop()
switch os.Args[1] {
case "watch":
(&Watch{M: Here(), Out: os.Stdout}).Loop(ctx)
case "scrub-run":
said, err := Here().ScrubRun(ctx, ServedFile)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(said)
case "events":
if err := Follower(ctx, os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
default:
fmt.Fprintf(os.Stderr, "zfs-tools: %q is not a way to run; with no argument it serves its tools, with watch it "+
"watches the pools, with events it follows ZFS's events, with scrub-run it runs the scheduled scrub\n", os.Args[1])
os.Exit(2)
}
return
}
m := Here()
m.Emit = func(eventType string, body any) error { return stdio.Emit(eventType, body) }
// An empty name serves as the module the runtime names (MESH_SERVED_MODULE): zfs.
if err := stdio.Serve("", Tools(m)); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}