fix: make the reboot plan trustworthy and report unreadable sysctl files [P2]
Three diagnostic defects from the follow-up audit. 1. The reboot plan was entirely best-effort. persist_plan discarded the directory creation, the write and every error. The plan file is the only thing on the host that says when it is going to reboot, so a failed write produced a scheduler sitting on a timer that nothing could report. It now creates the directory, writes through a temp file with sync_all and rename, and returns Result. The plan also outlived its meaning in two places. With reboot.enabled = false the scheduler exited without touching a plan written when it was still enabled, so status kept advertising a reboot that would never happen. And after systemd accepted the reboot request the plan stayed behind, leaving a past timestamp looking pending. Both paths now clear it; an absent plan is the desired end state, not an error. 2. sysctl status turned a read failure into "file absent". inspect_sysctl_tuning used fs::read_to_string(..).ok() for both managed files, so an unreadable file was reported as missing - a diagnostic stating the opposite of the truth, in the module whose whole job is to report drift accurately. Reads now go through ManagedFile::read, which distinguishes absent from unreadable, and an unreadable file produces its own drift reason naming the path and the error. 3. README described the removed ExecStartPre. It still said the service runs ensure-swap before starting the daemon. The daemon performs it as the first step of its own startup sequence. Docs also state explicitly what survives swap.enabled = false, which was implied but never written: the swapfile, its activation and the /etc/fstab entry all remain, so swap comes back after a reboot. Only the vm.swappiness persistence is removed. Acceptance checklist gained seven checks covering the priority-only path not recreating the file, swap remaining active throughout a resize, foreign-swap rollback, the unreadable status, and both reboot-plan lifecycle cases. cargo test --workspace: 179 + 4 passed. clippy -D warnings clean.
This commit is contained in:
@@ -123,7 +123,9 @@ awg2-harness validate-runtime
|
||||
|
||||
Модуль `swap` раньше писал `vm.swappiness` в `/etc/sysctl.d/99-awg2-harness.conf` с другим маркером, без проверки владения перед перезаписью и без удаления при отключении. Файл переехал на `90-`, как и остальные. Старый путь удаляется автоматически, но **только** если он всё ещё несёт прежний маркер `# Managed by awg2-harness`; файл администратора по этому пути не трогается, и это не считается ошибкой миграции.
|
||||
|
||||
При `swap.enabled = false` управляемый файл удаляется. Runtime-значение `vm.swappiness` при этом намеренно не меняется: назначать произвольное значение означало бы выдумать политику, о которой оператор не просил. После перезагрузки действует kernel default. Активный swap при отключении модуля тоже не выключается — деструктивное действие не должно запускаться самим фактом отключения функции.
|
||||
Если управляемый файл существует, но не читается, `status` показывает отдельную причину `managed sysctl file is unreadable`, а не «файл отсутствует». Оператору важна разница между файлом, который пакет ещё не писал, и файлом, который он не может прочитать.
|
||||
|
||||
При `swap.enabled = false` управляемый файл удаляется. **Остаются на месте**: сам swapfile, его активность в ядре и запись в `/etc/fstab` — то есть после перезагрузки swap снова включится. Удаляется только persistence `vm.swappiness`. Runtime-значение `vm.swappiness` при этом намеренно не меняется: назначать произвольное значение означало бы выдумать политику, о которой оператор не просил. После перезагрузки действует kernel default. Активный swap при отключении модуля тоже не выключается — деструктивное действие не должно запускаться самим фактом отключения функции.
|
||||
|
||||
`net.ipv4.tcp_congestion_control` и `net.core.default_qdisc` попадают в целевое состояние только при активном BBR.
|
||||
|
||||
@@ -572,8 +574,9 @@ Production-путь перезагрузки — внешний бинарь/с
|
||||
- окно берётся из конфига, а не из констант;
|
||||
- случайная точка внутри окна, включая окна через полночь;
|
||||
- вызов остаётся `systemctl --no-block reboot`;
|
||||
- при `reboot.enabled = false` планирование отключено;
|
||||
- запланированная отметка времени сохраняется в `/run/awg2-harness/reboot.plan`.
|
||||
- при `reboot.enabled = false` планирование отключено, а оставшийся от прежней конфигурации `/run/awg2-harness/reboot.plan` удаляется — иначе `status` продолжал бы показывать перезагрузку, которая никогда не наступит;
|
||||
- запланированная отметка времени сохраняется в `/run/awg2-harness/reboot.plan` атомарно (временный файл, `sync_all`, `rename`), и ошибка записи прекращает работу. Это единственный источник информации о том, когда хост перезагрузится; прежняя best-effort запись позволяла планировщику молча уйти в ожидание момента, о котором в системе не было никаких данных;
|
||||
- после принятия запроса на перезагрузку план удаляется, чтобы прошедшая отметка времени не выглядела как ожидающая.
|
||||
|
||||
## 17. Модуль swap
|
||||
|
||||
|
||||
Reference in New Issue
Block a user