fix(nixos): smartd self-gates on the hardware — the doctor was right, smartd wasn't
Some checks failed
Check / eval (push) Has been cancelled
Some checks failed
Check / eval (push) Has been cancelled
BACKLOG #118. Bernardo booted the live ISO and the Waybar health icon was red, reporting smartd. Everything downstream turned out to be working correctly, which is the part worth recording: smartd's config is DEVICESCAN, and where no drive answers SMART it exits 17 ("Unable to monitor any SMART enabled devices"), systemd marks the unit failed, nomarchy-doctor faithfully reports a failed system unit, and Waybar paints @bad. The doctor was telling the truth. smartd was the bug. Scope was never live-only, which is why this sat in NOW rather than as a live nit: services.smartd.enable mkDefaults true on every machine and QEMU virtio exposes no SMART, so every VM install has been booting to a health warning about a daemon with nothing to do — and every V2 run had been showing it as noise. Fixed with the distro's own self-gate convention: an ExecCondition running `smartctl --scan`, which prints nothing exactly when smartd would find nothing. A failed condition leaves the unit inactive rather than failed. Deliberately NOT SuccessExitStatus = 17: that would also swallow exit 17 from a machine that does have drives, which is the entire reason the daemon ships. V2. checks.smartd-gate boots the REAL distro module rather than a restatement of it (its nixpkgs.config needs mkForce to yield to the test's pkgs) and asserts both halves, because they pull in opposite directions: a gate that never skips leaves the red icon, and a gate that always skips silently disables drive-health monitoring on real hardware — the failure nobody notices until a disk dies quietly. So the no-SMART node must go ActiveState=inactive, unfailed, and absent from `systemctl --failed` (what the doctor actually reads); and the gate's logic is driven against a scan that DOES find a device, since QEMU cannot answer SMART honestly and pretending otherwise would test nothing. The check was proved to fail by unwiring the condition. flake check, doctor, hardware-toggles, live-baseline-apps, option-docs and state-bridges pass. V3 pending: that smartd still RUNS where drives have SMART (dev box, real NVMe). Queued with an explicit fail-condition — if it skips there, revert the gate rather than tune it. Also swept: #120's size table said the duplicate chromium was gone; #121 was reverted, so it is back and the table says so. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
72
flake.nix
72
flake.nix
@@ -560,10 +560,6 @@
|
||||
touch $out
|
||||
'';
|
||||
|
||||
# nomarchy-doctor's contract: an induced failed unit flips the
|
||||
# sheet to ✖/exit-1 and names the unit; with the failure
|
||||
# cleared it reports healthy/exit-0. Minimal node (just the
|
||||
# package) — the disk/flake/snapper checks self-skip in a VM.
|
||||
# Live-ISO baseline apps (ROADMAP § live-ISO baseline apps, #103).
|
||||
# The live session shipped no
|
||||
# browser and no office for as long as it existed — the thing a user
|
||||
@@ -654,6 +650,74 @@
|
||||
touch $out
|
||||
'';
|
||||
|
||||
# smartd self-gate (BACKLOG #118). Where no drive answers SMART,
|
||||
# smartd exits 17 and systemd marks the unit FAILED — so the doctor
|
||||
# honestly reported a failed unit and Waybar went red on every QEMU
|
||||
# guest and most live USBs. Both halves matter and they pull in
|
||||
# opposite directions, which is why both are asserted here: a
|
||||
# gate that never skips leaves the red icon, and a gate that always
|
||||
# skips silently disables drive-health monitoring on real hardware —
|
||||
# the failure nobody would notice until a disk died quietly.
|
||||
smartd-gate = pkgs.testers.runNixOSTest {
|
||||
name = "nomarchy-smartd-gate";
|
||||
nodes = {
|
||||
# QEMU virtio: no SMART. The machine Bernardo saw. Imports the
|
||||
# REAL distro module, so this guards the shipped wiring rather
|
||||
# than a restatement of it — runNixOSTest supplies the node's
|
||||
# pkgs, so the module's own `nixpkgs.config` has to yield.
|
||||
nosmart = { lib, pkgs, ... }: {
|
||||
imports = [ ./modules/nixos/default.nix ];
|
||||
nixpkgs.config = lib.mkForce { allowUnfree = true; };
|
||||
nomarchy.system.greeter.enable = false;
|
||||
# For the test's own probing only — the gate calls smartctl by
|
||||
# store path, so this changes nothing about what is measured.
|
||||
environment.systemPackages = [ pkgs.smartmontools ];
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
nosmart.wait_for_unit("multi-user.target")
|
||||
|
||||
# The gate itself agrees there is nothing to monitor.
|
||||
cond = nosmart.succeed(
|
||||
"systemctl show -p ExecCondition --value smartd.service"
|
||||
)
|
||||
assert "smartd-any-smart-device" in cond, f"gate not wired: {cond}"
|
||||
nosmart.succeed("smartctl --scan | tee /dev/stderr | (! grep -q .)")
|
||||
|
||||
# Inactive, NOT failed — the whole point. Before #118 this was
|
||||
# 'failed' with Result=exit-code / status=17.
|
||||
state = nosmart.succeed(
|
||||
"systemctl show -p ActiveState --value smartd.service"
|
||||
).strip()
|
||||
assert state == "inactive", f"expected inactive, got {state}"
|
||||
nosmart.fail("systemctl is-failed --quiet smartd.service")
|
||||
|
||||
# And therefore the thing the user actually sees: the doctor's
|
||||
# failed-unit check is what reddened the Waybar icon.
|
||||
failed = nosmart.succeed("systemctl --failed --no-legend --plain")
|
||||
assert "smartd" not in failed, f"smartd still failed: {failed}"
|
||||
|
||||
# The other half: a machine WITH a SMART device must still run
|
||||
# smartd. QEMU cannot answer SMART, so drive the gate's logic
|
||||
# against a scan that finds one, rather than pretend otherwise.
|
||||
nosmart.succeed(
|
||||
"mkdir -p /tmp/fake && "
|
||||
"printf '#!/bin/sh\\necho \"/dev/sda -d scsi # /dev/sda, SCSI device\"\\n' "
|
||||
"> /tmp/fake/smartctl && chmod +x /tmp/fake/smartctl"
|
||||
)
|
||||
gate = cond.split("argv[]=")[1].split(" ")[0].strip("; ]")
|
||||
nosmart.succeed(
|
||||
f"sed 's|/nix/store/[^ ]*/bin/smartctl|/tmp/fake/smartctl|' {gate} > /tmp/gate2"
|
||||
)
|
||||
nosmart.succeed("chmod +x /tmp/gate2")
|
||||
nosmart.succeed("/tmp/gate2") # exit 0 => smartd would start
|
||||
'';
|
||||
};
|
||||
|
||||
# nomarchy-doctor's contract: an induced failed unit flips the
|
||||
# sheet to ✖/exit-1 and names the unit; with the failure
|
||||
# cleared it reports healthy/exit-0. Minimal node (just the
|
||||
# package) — the disk/flake/snapper checks self-skip in a VM.
|
||||
doctor = pkgs.testers.runNixOSTest {
|
||||
name = "nomarchy-doctor";
|
||||
nodes.machine = { ... }: {
|
||||
|
||||
Reference in New Issue
Block a user