Fix backups Audit/Details for k8s-format archives + broken local access

Two separate bugs found while testing the Audit/Details UI against a
real k8s-format backup (myapps-k8s-backup-*):

1. Format-hardcoded checks. audit_backup()'s Internal Structure and
   Volume Count checks assumed the legacy Docker-era archive layout
   (volumes/*.tar.gz, compose-files/) and would fail every k8s-format
   backup (per-app dirs with manifests.yaml/db-dump.sql.gz/pvc-data.tar.gz)
   even when it's perfectly healthy - same root cause as the
   myapps-backup-*/myapps-k8s-backup-* prefix bug fixed earlier, just in
   the audit checks instead of the listing/delete glob. cloud_backup.py's
   r2_audit_backup() had the identical unpatched filename regex, and
   app.py's /api/backups/details route had its own, which outright
   400'd any k8s-format filename before even looking at it.

2. A bigger, separate bug this surfaced: get_local_backups(),
   get_vm_backups(), and _resolve_archive_path() all branch on
   RUNNING_ON_MAIN_SERVER to decide between direct filesystem access and
   SSH-to-self - and from inside the management-platform pod that flag's
   hostname check can be wrong for the pod's own ephemeral hostname
   depending on how it's evaluated, sending these down the SSH branch
   using a topology (separate warm-standby-on-the-VM SSH path) that
   doesn't apply to a pod that already has /root hostPath-mounted in.
   Fixed by trying direct filesystem access first wherever the archive
   would already be locally reachable, falling back to the existing SSH
   paths otherwise - this preserves the original warm-standby-on-VM
   failover design (a real second deployment of this platform on the VM
   host, kept reachable via SSH when the main server is down) completely
   unchanged; it only adds the fast local path for the case where the
   caller already has direct filesystem access.

Verified against both a real k8s-format backup (odoo, single-app) and a
real legacy-format backup on disk - both audit correctly now, with
format-appropriate checks and labels.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017avLHFqkiti3g62Anq9sVA
This commit is contained in:
root
2026-08-21 02:00:07 +02:00
parent a8f996422c
commit de0b1ee4db
3 changed files with 166 additions and 63 deletions

View File

@@ -345,10 +345,12 @@ def r2_audit_backup(key: str) -> dict:
else:
report["checks"].append(f"✅ File size looks healthy ({_human_size(size)})")
# Check 5: Filename format
# Check 5: Filename format — accepts both the legacy Docker-era
# lineage (myapps-backup-*) and the k8s-native one
# (myapps-k8s-backup-*, from backup-k8s-apps.sh).
import re
filename = key.replace("backups/", "")
if re.match(r"myapps-backup-\d{8}_\d{6}\.tar\.gz", filename):
if re.match(r"myapps-(k8s-)?backup-\d{8}_\d{6}\.tar\.gz", filename):
report["checks"].append("✅ Filename format is valid")
else:
report["warnings"].append(f"⚠️ Unexpected filename format: {filename}")