浏览代码

fix: ne pas faire échouer le job custom_dir sur un transfert rsync partiel (code 23/24)

Les montages rclone webdav dans un répertoire sauvegardé provoquent des
readlink_stat en échec (Permission denied), ce que rsync remonte en code
23/24 sans que le reste du transfert soit affecté. On traite désormais ces
codes comme un avertissement (run.status = warning) au lieu d'avorter tout
le job en erreur fatale, aussi bien au backup qu'à la restauration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cedric Hansen 1 天之前
父节点
当前提交
5e6e499b6c
共有 2 个文件被更改,包括 20 次插入6 次删除
  1. 17 4
      sources/jobs/custom_dir.py
  2. 3 2
      sources/jobs/ynh_backup.py

+ 17 - 4
sources/jobs/custom_dir.py

@@ -47,7 +47,14 @@ def backup_custom_dir(job, instance, backup_dir):
 
         result = subprocess.run(rsync_cmd, capture_output=True, text=True, timeout=7200)
         log = (result.stdout + result.stderr).strip()
-        if result.returncode != 0:
+        # Codes 23/24 : transfert partiel (ex. fichiers illisibles comme des
+        # montages rclone) — on continue avec ce qui a pu être copié plutôt
+        # que de faire échouer tout le job.
+        warning = False
+        if result.returncode in (23, 24):
+            warning = True
+            log = f"⚠ rsync : transfert partiel (code {result.returncode}) :\n{log}"
+        elif result.returncode != 0:
             raise RuntimeError(f"rsync a échoué (code {result.returncode}) :\n{log}")
 
         # backup.csv (requis YunoHost)
@@ -110,7 +117,7 @@ def backup_custom_dir(job, instance, backup_dir):
     finally:
         subprocess.run(["sudo", "rm", "-rf", tmpdir], check=False)
 
-    return archive_name, log or "rsync terminé sans sortie."
+    return archive_name, log or "rsync terminé sans sortie.", warning
 
 
 # ---------------------------------------------------------------------------
@@ -155,9 +162,15 @@ def restore_custom_dir(archive_name, backup_dir):
              extracted_data + "/", source_path + "/"],
             capture_output=True, text=True, timeout=7200,
         )
-        if result.returncode != 0:
+        if result.returncode in (23, 24):
+            log_lines.append(
+                f"⚠ Fichiers restaurés vers {source_path} avec transfert partiel "
+                f"(code {result.returncode}) : {result.stderr.strip()}"
+            )
+        elif result.returncode != 0:
             raise RuntimeError(f"rsync restore a échoué : {result.stderr.strip()}")
-        log_lines.append(f"Fichiers restaurés vers {source_path}.")
+        else:
+            log_lines.append(f"Fichiers restaurés vers {source_path}.")
 
         # User système
         user_cfg = restore_cfg.get("system_user", {})

+ 3 - 2
sources/jobs/ynh_backup.py

@@ -23,6 +23,7 @@ def execute_job(job_id):
     db.session.add(run)
     db.session.commit()
 
+    backup_warning = False
     try:
         if job.type == "ynh_app":
             archive_name, log = _run_ynh_app(job, instance, backup_dir)
@@ -33,7 +34,7 @@ def execute_job(job_id):
             archive_name, log = run_db_dump(job, instance, backup_dir)
         elif job.type == "custom_dir":
             from jobs.custom_dir import backup_custom_dir
-            archive_name, log = backup_custom_dir(job, instance, backup_dir)
+            archive_name, log, backup_warning = backup_custom_dir(job, instance, backup_dir)
         else:
             raise ValueError(f"Type de job non géré : {job.type}")
 
@@ -95,7 +96,7 @@ def execute_job(job_id):
                     transfer_errors += 1
                 db.session.commit()
 
-        run.status = "warning" if transfer_errors else "success"
+        run.status = "warning" if (transfer_errors or backup_warning) else "success"
 
     except Exception as exc:
         run.status = "error"