Files
pulse/scripts
rcourtman dcdbca28f8 Stage server update archives on the install filesystem, not /tmp (#3006)
## Summary

Unattended updates on small Proxmox LXCs were being OOM-killed. The
server installer staged the release archive (~280 MB for v6.5.0) and its
extracted tree (~750 MB) under `/tmp`. On Debian 13 and many LXC
templates `/tmp` is tmpfs, and tmpfs pages count against the container's
memory cgroup. The reporter's host log shows the `/lxc/118` cgroup at
its 1 GiB limit with ~820 MB of shmem, the kernel OOM-killing the
container's `systemd` (PID 1), and Proxmox then stopping the container.

`install.sh` now downloads and extracts into owner-only hidden
directories under the install directory:
- The install directory is always writable from the hardened
`pulse-update.service` sandbox.
- The staged binary already lives there.
- The disk headroom preflight now measures the filesystem the update
actually stages on.

The Go updater already prefers its data directory over `/tmp` for the
same reason.

Two follow-ups that come with staging on disk:
- **Stale staging sweep.** An interrupted run's staging (~1 GB) now
survives reboots. Staging setup removes stale `.pulse-extract-XXXXXX`
and `.pulse-<release>-linux-<arch>-XXXXXX` directories under the install
directory. It only removes real directories with those exact names,
never symlinks or files, and only when their ctime is more than 6 hours
old. `install.sh` has no update lock, so age is what proves no live run
still owns them.
- **Ownership.** Every recursive `chown -R pulse:pulse "$INSTALL_DIR"`
(archive install, source build, `setup_directories`) now goes through
`chown_install_dir_for_pulse`, which skips staging. The payload that
root still reads is never handed to the service account.

The `Failed to print table: Broken pipe` line in the report came from
`systemctl list-unit-files | grep -q` in v6.5.0's service discovery.
`main` already reads that inventory in full, and it was not the cause of
the failure.

## Tests

- New Go tests `TestRootInstallScriptSweepsOnlyStaleServerStaging`
(fresh staging kept, stale staging removed; look-alike names, files,
symlinks and their targets kept) and
`TestRootInstallScriptPulseOwnershipExcludesServerStaging` (staging
skipped, no raw recursive install-dir chown left).
- New `test_archive_extraction_stages_on_the_install_filesystem` (server
installer stop harness) and
`test_download_stages_archive_on_the_install_filesystem` (temporary
inputs harness) fail against the current installer and pass with this
change.
- `scripts/tests/test_server_installer_stop.py`,
`test_server_installer_temporary_inputs.py`,
`test_server_service_discovery.py`, and the `TestRootInstall*` tests in
`scripts/installtests` pass (run on Linux).

Refs #2785
2026-10-17 02:10:35 +01:00
..
…
…
…
2026-10-16 20:44:47 +01:00