Wire management-platform's backup/restore routes to the k8s scripts
api_backup_run and restore_start were still hardcoded to the Docker-era backup-myapps.sh/restore-myapps.sh, which target Docker volumes that no longer exist for the 5 apps now running in k3s (n8n, odoo, mautic, nextcloud, frappe/erpnext) — every backup/restore triggered from the UI has been silently hollow for these apps since the migration. - api_backup_run: /root/backup-myapps.sh -> /root/CloudOps/backup/backup-k8s-apps.sh - restore_start: switched from the old image-relative path (which resolved to /app/restore-myapps.sh, baked into the Docker image from platform/) to an absolute /root/CloudOps/backup/restore-k8s-apps.sh path — the new backup/ folder is a sibling of platform/, not part of the Docker build context, so it's only reachable via the pod's existing /root hostPath mount, the same way api_backup_run already reaches its script. --apps flag shape and the job-log streaming contract are unchanged, so no frontend/template changes needed for the core flow. modules/backups.py's three hardcoded myapps-backup-* references (get_local_backups, get_vm_backups x2, delete_backup's filename regex) now also recognize the new myapps-k8s-backup-* prefix, so both backup lineages stay visible/manageable in the UI. Depends on the previous commit (scoped ServiceAccount + k3s kubectl access for the pod) to actually function when triggered from the UI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -56,8 +56,12 @@ def _ssh_main(remote_cmd, timeout=30):
|
||||
# ────────────────────────────────────────────────────────────────
|
||||
|
||||
def get_local_backups():
|
||||
# myapps-backup-* is the legacy Docker-era lineage; myapps-k8s-backup-*
|
||||
# is the k8s-native one (backup-k8s-apps.sh). Listed together, sorted
|
||||
# by mtime, so history from both stays visible in the UI.
|
||||
stdout, _ = _ssh_main(
|
||||
"ls -t /root/backups/myapps-backup-*.tar.gz 2>/dev/null | head -20"
|
||||
"ls -t /root/backups/myapps-backup-*.tar.gz "
|
||||
"/root/backups/myapps-k8s-backup-*.tar.gz 2>/dev/null | head -20"
|
||||
)
|
||||
files = []
|
||||
if stdout:
|
||||
@@ -77,7 +81,8 @@ def get_vm_backups():
|
||||
f"-o StrictHostKeyChecking=no -o ConnectTimeout=10 "
|
||||
f"-o BatchMode=yes "
|
||||
f"{VM_USER}@{VM_HOST} "
|
||||
f"'ls -t /backups/cloudproject/myapps-backup-*.tar.gz 2>/dev/null | head -20'"
|
||||
f"'ls -t /backups/cloudproject/myapps-backup-*.tar.gz "
|
||||
f"/backups/cloudproject/myapps-k8s-backup-*.tar.gz 2>/dev/null | head -20'"
|
||||
)
|
||||
stdout, _ = _run(cmd, timeout=25)
|
||||
if stdout:
|
||||
@@ -90,7 +95,10 @@ def get_vm_backups():
|
||||
else:
|
||||
backup_dir = '/backups/cloudproject'
|
||||
if os.path.exists(backup_dir):
|
||||
files = glob.glob(f'{backup_dir}/myapps-backup-*.tar.gz')
|
||||
files = (
|
||||
glob.glob(f'{backup_dir}/myapps-backup-*.tar.gz')
|
||||
+ glob.glob(f'{backup_dir}/myapps-k8s-backup-*.tar.gz')
|
||||
)
|
||||
files.sort(key=os.path.getmtime, reverse=True)
|
||||
vm_backups = [os.path.basename(f) for f in files[:20]]
|
||||
return vm_backups
|
||||
@@ -408,7 +416,11 @@ def _sha256_file(path):
|
||||
# ────────────────────────────────────────────────────────────────
|
||||
|
||||
def delete_backup(backup_file, source='local'):
|
||||
if not re.match(r'^myapps-backup-\d{8}_\d{6}\.tar\.gz$', backup_file):
|
||||
# Accepts both the legacy Docker-era lineage (myapps-backup-*) and the
|
||||
# k8s-native one (myapps-k8s-backup-*, from backup-k8s-apps.sh). Still a
|
||||
# tight anchor match — this guards against path traversal in a filename
|
||||
# that ends up interpolated into a shell/filesystem path below.
|
||||
if not re.match(r'^myapps-(k8s-)?backup-\d{8}_\d{6}\.tar\.gz$', backup_file):
|
||||
return False, f'Invalid backup filename: {backup_file}'
|
||||
|
||||
if source == 'local':
|
||||
|
||||
Reference in New Issue
Block a user