The previous fix closed one branch and left its neighbours open. Both remaining paths could
still end with the host having no swap, and one of them said the opposite in its own error
message.
1. The message claimed a fallback that was no longer running.
The forward path deactivates the staging swap *before* renaming it onto the managed
path. A failure at that rename, or at anything after it, therefore reached the rollback
with staging_active = false. The rollback then returned "the staging swap is left
active" - which was false: it had been switched off several steps earlier, and with the
original also unable to activate, the host had nothing.
The rollback now brings the replacement back up as the fallback when it is not running,
and the error describes what is actually active rather than assuming.
2. The replacement was deleted before the original was known to work.
With new_installed the rollback removed the file at the managed path to make room for
the restore. That discarded the only other swap area on the host before finding out
whether the original could be activated. The replacement is now moved aside to the
staging name instead, so it is still available as the fallback if the restore fails.
3. An installed replacement could be moved while the kernel was swapping to it.
ReplacementState had no way to express "installed and active", so a failure in the final
verification - a failed read of /proc/swaps after a successful swapon - left the
rollback moving a live swapfile. new_active closes that: if the replacement is active it
is deactivated first, and if that fails the rollback stops and reports that the
replacement is in place and serving, because a working swap is the outcome to keep.
Also from the same audit:
- mkswap --file creates the file itself, so a non-zero exit is not a guarantee that nothing
was left behind. Both creation paths now remove the file on failure; previously a failed
mkswap left an unowned file at the managed path, which the next run correctly called
Foreign and refused, wedging the module.
- stale_ownership_help gave advice that could not work. Removing the ownership record
leaves the file Foreign, which is refused just as firmly, so "mv previous main, delete the
record" only changed which error appeared. Both cases now give one recipe that actually
restores managed state: swapoff, remove the three files and the record, re-run
ensure-swap. Swap holds no persistent data, so a rebuild loses nothing.
- LastActionState kept a single error slot shared by all subsystems, so a successful
firewall reconcile cleared a live traffic failure and logged firewall=recovered for
something that had never failed. Errors are now tracked per subsystem; the shared field
that status reads mirrors whatever is still outstanding.
- tools/README.md still showed 0.1.6.
Five rollback tests covering the branches that exist: original unrestorable with staging
running, original unrestorable with staging already stopped (it is reactivated and never
released), an installed replacement preserved by being moved aside rather than deleted,
the success path releasing the replacement only after the original is back, and the
no-op case.
cargo fmt --check, clippy -D warnings, cargo test --locked --workspace --release: 193 + 4
passed.
Добавлен package-режим в tools/build.sh: --package, --dist-dir, --version. Реализована сборка Debian-пакета, metadata/control, postinst/postrm и SHA256SUMS. Установочные пути унифицированы под /usr/sbin и /lib/systemd/system для production-модели.