Pulse/CLEANUP_TODO.md
rcourtman c10be5cf78 Document Codex review findings and resolutions
Updated CLEANUP_TODO.md with comprehensive documentation of all 8 critical
issues identified by Codex review (conv-1763166192078-1076) and their
resolutions.

Key updates:
- Added detailed problem/fix/impact for each issue
- Updated status to 'Codex review complete, ready for deployment testing'
- Documented all commits in implementation history
- Added Codex review summary section
- Marked phases 1-6 as complete, phase 7 (testing) as pending

This provides complete audit trail of cleanup implementation work.
2025-11-15 00:36:50 +00:00

10 KiB

Node Cleanup Implementation - CODEX REVIEW COMPLETE

Status: Implementation complete, Codex review passed, ready for deployment testing.

Latest Update: Addressed all 8 critical issues found by Codex review (conv-1763166192078-1076).

Implementation History

Commit b192c60e9: Relocate binaries to /opt/pulse/sensor-proxy/

  • Binary path moved from /usr/local/bin to /opt/pulse/sensor-proxy/bin
  • All systemd ExecStart paths updated
  • Guarantees cleanup works on read-only /usr systems

Commit 6692228e0: Full cleanup script implementation

  • SSH key removal (working)
  • Service uninstallation (via isolated systemd-run transient unit)
  • API token deletion (JSON first, filtered table fallback)
  • Bind mount removal (scans all LXC configs)
  • pulse-monitor user deletion
  • flock serialization (prevents concurrent runs)
  • Immediate request file deletion (prevents loops)

Commits ed48d7555, 17d2e6876: Bug fixes during testing

  • Fixed directory creation order (create before binary download)
  • Fixed SHARE_DIR unbound variable

Commit bcd8d4e0f: Critical Codex review fixes #1-4

  • Host detection now includes hostname/FQDN (not just IP)
  • Systemd sandbox relaxed (/etc/pve and /etc/systemd/system writable)
  • Uninstaller called with --purge flag for complete removal
  • All /usr/local references migrated to /opt paths
  • UUID used for transient unit names (prevents collisions)
  • --wait and --collect flags capture uninstaller exit code

Commit fe53d6473: Remaining Codex review fixes #5-8

  • LXC bind mounts removed via pct set -delete (not sed)
  • API token parsing: three-tier fallback (pveum JSON → pvesh JSON → table)
  • Retry logic: rename to .processing, delete only on success

Issues Discovered and Resolved

1. Host Detection Failure (CRITICAL) - FIXED

Problem: Cleanup script only compared against IPs from hostname -I, missing nodes configured as https://hostname:8006. This caused localhost cleanup (API tokens, bind mounts, service removal) to be skipped entirely.

Fix: Check against hostname, hostname -f, and all IPs. Now catches all localhost variations.

Impact: Without this fix, cleanup would only remove remote SSH keys, leaving services/binaries/tokens intact.

2. Systemd Sandbox Blocked Critical Operations - FIXED

Problem: Cleanup service ran with ProtectSystem=strict and ReadWritePaths=/var/lib/pulse-sensor-proxy /root/.ssh, blocking writes to /etc/pve (Proxmox configs) and /etc/systemd/system.

Fix: Added /etc/pve and /etc/systemd/system to ReadWritePaths.

Impact: pveum token deletion and pct set -delete would fail with "read-only file system".

3. Incomplete Purging - FIXED

Problem: Uninstaller called without --purge, leaving /var/lib/pulse-sensor-proxy, service user, and SSH private keys on disk. Request file deleted before work completed, preventing retry on failure.

Fix: Added --purge flag, added --wait --collect to capture exit code, fail cleanup if uninstaller fails.

Impact: Claimed "cleanup completed successfully" even when artifacts remained.

4. Incomplete Path Migration - FIXED

Problem: After relocating binaries to /opt, three references still pointed to /usr/local:

  • Forced command in SSH authorized_keys: /usr/local/bin/pulse-sensor-wrapper.sh
  • Self-heal script: /usr/local/share/pulse/install-sensor-proxy.sh
  • Backend removal helpers: /usr/local/bin/pulse-sensor-cleanup.sh

Fix: Updated all three locations. Go backend now checks both paths (new + legacy).

Impact: New installs would lose telemetry immediately (SSH command not found). UI-triggered cleanup wouldn't find helpers.

5. Transient Unit Name Collisions - FIXED

Problem: Used date +%s for unit names. If two cleanup requests fired within same second, second would fail (unit already exists). Error suppressed, logged as "started" anyway.

Fix: Use /proc/sys/kernel/random/uuid for unique names.

Impact: Multiple concurrent cleanups could race, with silent failures.

6. Bind Mount Removal Too Broad - FIXED

Problem: Used sed -i '/pulse-sensor-proxy/d' which would delete ANY line mentioning the substring (including unrelated comments/hooks). Also couldn't run inside systemd sandbox.

Fix: Use pct set <ctid> -delete mp<N> which validates syntax and is sandbox-compatible.

Impact: Could break container configs. Sed approach would fail anyway due to sandbox.

7. API Token Parsing Fragile - FIXED

Problem: Table parser filtered only │┌└╞, failing on other Unicode borders or locales. "User not found" vs "feature unsupported" both treated as "no tokens".

Fix: Three-tier fallback:

  1. pveum --output-format json with python3 parsing
  2. pvesh get /access/users/pulse-monitor@pam/token (always JSON)
  3. Improved table parser with better filtering

Impact: Non-English locales or Proxmox versions with different table formatting would silently skip token cleanup.

8. No Retry on Failure - FIXED

Problem: Request file deleted immediately. Any crash left no way to retry.

Fix: Rename to .processing, delete only on success. Failures leave .processing file for manual investigation/retry.

Impact: Transient failures (network issues, systemd hiccups) couldn't be retried automatically.

Read-Only Filesystem

Problem: Proxmox VE can mount /usr as read-only (hardened setups, boot-from-snapshots, appliance builds).

Solution: All binaries relocated to /opt/pulse/sensor-proxy/ where we control permissions.

Process Isolation During Uninstall

Problem: Cleanup script runs as systemd service. When it calls the uninstaller which stops the proxy service, systemd kills the cleanup service with SIGTERM.

Solution: Use transient systemd unit with:

  • UUID-based unique name
  • Conflicts=pulse-sensor-proxy.service
  • --wait --collect to capture exit code
  • --purge flag for complete removal

Implementation Status

Phase 1: Relocate Binaries COMPLETE

  • Update installer to use /opt/pulse/sensor-proxy/bin/
  • Update systemd unit ExecStart path
  • Update cleanup script to use new paths
  • Update forced command in SSH authorized_keys
  • Update self-heal script paths
  • Update Go backend removal helpers (supports both new and legacy paths)

Phase 2: Fix Uninstall Orchestration COMPLETE

  • Cleanup script spawns transient systemd unit via systemd-run
  • Added Conflicts=pulse-sensor-proxy.service to transient unit
  • UUID-based unit names prevent collisions
  • Added --wait --collect to capture exit code
  • Uninstaller runs with --purge --quiet flags
  • Cleanup fails if uninstaller exits non-zero

Phase 3: Prevent Cleanup Loops COMPLETE

  • Added flock serialization via exec 200>lockfile
  • Rename request file to .processing (allows retry on failure)
  • Delete .processing only on successful completion
  • .path unit uses PathChanged/PathModified (correct semantics)
  • Comprehensive logging at info/warn/error levels

Phase 4: Improve API Token Handling COMPLETE

  • Three-tier fallback: pveum JSON → pvesh JSON → table parsing
  • Python3 JSON parsing for robustness
  • Better table filtering (handles more Unicode characters)
  • Error handling for token deletion failures (logs warnings, continues)
  • Logs each token removal attempt

Phase 5: LXC Bind Mount Removal COMPLETE

  • Use pct set -delete instead of sed (validates syntax)
  • Sandbox-compatible (works with ProtectSystem=strict + /etc/pve writable)
  • Only removes mounts containing "pulse-sensor-proxy"

Phase 6: Host Detection COMPLETE

  • Check against hostname, FQDN, and all local IPs
  • Localhost detection works for https://hostname:8006 URLs
  • Ensures full cleanup runs for all localhost variations

Phase 7: Testing & Validation ⏸️ PENDING

  • ⏸️ Deploy updated installer to test host
  • ⏸️ Test node removal via Pulse UI
  • ⏸️ Verify complete cleanup:
    • ⏸️ No systemd units
    • ⏸️ No binaries in /opt or /usr/local
    • ⏸️ No bind mounts in LXC configs
    • ⏸️ No API tokens or pulse-monitor user
    • ⏸️ No SSH keys in authorized_keys
    • ⏸️ No /var/lib/pulse-sensor-proxy directory
  • ⏸️ Test retry logic (simulate failure, verify .processing file persists)
  • ⏸️ Test on fresh Proxmox VE install
  • ⏸️ Test on hardened PVE with read-only /usr
  • ⏸️ Test cluster vs standalone scenarios

Codex Review Summary

Review Session: conv-1763166192078-1076

Findings: 8 critical issues identified:

  1. Host detection only checked IPs, skipped cleanup for hostname-based URLs
  2. Systemd sandbox blocked /etc/pve and /etc/systemd writes
  3. Uninstaller missing --purge, request file deleted too early
  4. Incomplete /usr/local → /opt migration (SSH forced command, self-heal, backend)
  5. Timestamp-based unit names caused collisions
  6. Brittle sed-based bind mount removal, Unicode table parsing
  7. API token parsing failed on locales, couldn't distinguish error types
  8. No retry mechanism for transient failures

Resolution: All 8 issues fixed in commits bcd8d4e0f and fe53d6473.

References

  • Initial commit: ed65fda74 "Extend node cleanup to fully remove Pulse footprint"
  • Binary relocation: b192c60e9 "Relocate binaries to /opt/pulse/sensor-proxy/"
  • Full implementation: 6692228e0 "Full cleanup script implementation"
  • Bug fixes: ed48d7555, 17d2e6876
  • Codex review fixes #1-4: bcd8d4e0f
  • Codex review fixes #5-8: fe53d6473
  • Codex review: conv-1763166192078-1076
  • Related files:
    • scripts/install-sensor-proxy.sh (installer + cleanup script generation)
    • internal/api/config_handlers.go (triggerPVEHostCleanup + manual removal)
    • cmd/pulse-sensor-proxy/cleanup.go (handleRequestCleanup)

Priority

High: Implementation complete and Codex-reviewed. Ready for deployment testing. All critical security and correctness issues addressed.