diff --git a/selfprivacy_api/backup/__init__.py b/selfprivacy_api/backup/__init__.py index 56150db..3db2547 100644 --- a/selfprivacy_api/backup/__init__.py +++ b/selfprivacy_api/backup/__init__.py @@ -1,12 +1,15 @@ from datetime import datetime, timedelta -from operator import add -from os import statvfs, path, walk +from os import statvfs from typing import List, Optional from selfprivacy_api.utils import ReadUserData, WriteUserData from selfprivacy_api.services import get_service_by_id -from selfprivacy_api.services.service import Service, ServiceStatus, StoppedService +from selfprivacy_api.services.service import ( + Service, + ServiceStatus, + StoppedService, +) from selfprivacy_api.jobs import Jobs, JobStatus, Job @@ -41,16 +44,17 @@ class NotDeadError(AssertionError): def __str__(self): return f""" - Service {self.service_name} should be either stopped or dead from an error before we back up. - Normally, this error is unreachable because we do try ensure this. - Apparently, not this time. - """ + Service {self.service_name} should be either stopped or dead from + an error before we back up. + Normally, this error is unreachable because we do try ensure this. + Apparently, not this time. + """ class Backups: """A stateless controller class for backups""" - ### Providers + # Providers @staticmethod def provider(): @@ -172,7 +176,7 @@ class Backups: user_data["backup"] = DEFAULT_JSON_PROVIDER - ### Init + # Init @staticmethod def init_repo(): @@ -191,7 +195,7 @@ class Backups: return False - ### Backup + # Backup @staticmethod def back_up(service: Service): @@ -221,7 +225,8 @@ class Backups: Jobs.update(job, status=JobStatus.FINISHED) return snapshot - ### Restoring + # Restoring + @staticmethod def _ensure_queued_restore_job(service, snapshot) -> Job: job = get_restore_job(service) @@ -237,12 +242,17 @@ class Backups: Jobs.update(job, status=JobStatus.RUNNING) try: - Backups._restore_service_from_snapshot(service, snapshot.id, verify=False) + Backups._restore_service_from_snapshot( + service, + snapshot.id, + verify=False, + ) except Exception as e: Backups._restore_service_from_snapshot( service, failsafe_snapshot.id, verify=False ) raise e + # TODO: Do we really have to forget this snapshot? — Inex Backups.forget_snapshot(failsafe_snapshot) @staticmethod @@ -295,8 +305,9 @@ class Backups: else: raise NotImplementedError( """ - We do not know if there is enough space for restoration because there is some novel restore strategy used! - This is a developer's fault, open a issue please + We do not know if there is enough space for restoration because + there is some novel restore strategy used! + This is a developer's fault, open an issue please """ ) available_space = Backups.space_usable_for_service(service) @@ -307,15 +318,20 @@ class Backups: ) @staticmethod - def _restore_service_from_snapshot(service: Service, snapshot_id: str, verify=True): + def _restore_service_from_snapshot( + service: Service, + snapshot_id: str, + verify=True, + ): folders = service.get_folders() Backups.provider().backupper.restore_from_backup( snapshot_id, folders, + verify=verify, ) - ### Snapshots + # Snapshots @staticmethod def get_snapshots(service: Service) -> List[Snapshot]: @@ -377,7 +393,7 @@ class Backups: # expiring cache entry Storage.cache_snapshot(snapshot) - ### Autobackup + # Autobackup @staticmethod def is_autobackup_enabled(service: Service) -> bool: @@ -472,7 +488,7 @@ class Backups: ) ] - ### Helpers + # Helpers @staticmethod def space_usable_for_service(service: Service) -> int: @@ -501,5 +517,8 @@ class Backups: # if we backup the service that is failing to restore it to the # previous snapshot, its status can be FAILED # And obviously restoring a failed service is the moun route - if service.get_status() not in [ServiceStatus.INACTIVE, ServiceStatus.FAILED]: + if service.get_status() not in [ + ServiceStatus.INACTIVE, + ServiceStatus.FAILED, + ]: raise NotDeadError(service) diff --git a/selfprivacy_api/backup/backuppers/__init__.py b/selfprivacy_api/backup/backuppers/__init__.py index 24eb108..05adede 100644 --- a/selfprivacy_api/backup/backuppers/__init__.py +++ b/selfprivacy_api/backup/backuppers/__init__.py @@ -30,7 +30,12 @@ class AbstractBackupper(ABC): raise NotImplementedError @abstractmethod - def restore_from_backup(self, snapshot_id: str, folders: List[str], verify=True): + def restore_from_backup( + self, + snapshot_id: str, + folders: List[str], + verify=True, + ): """Restore a target folder using a snapshot""" raise NotImplementedError diff --git a/selfprivacy_api/backup/backuppers/restic_backupper.py b/selfprivacy_api/backup/backuppers/restic_backupper.py index 565a084..e04eaaf 100644 --- a/selfprivacy_api/backup/backuppers/restic_backupper.py +++ b/selfprivacy_api/backup/backuppers/restic_backupper.py @@ -81,7 +81,7 @@ class ResticBackupper(AbstractBackupper): mount_command.insert(0, "nohup") handle = subprocess.Popen(mount_command, stdout=subprocess.DEVNULL, shell=False) sleep(2) - if not "ids" in listdir(dir): + if "ids" not in listdir(dir): raise IOError("failed to mount dir ", dir) return handle @@ -211,7 +211,12 @@ class ResticBackupper(AbstractBackupper): except ValueError as e: raise ValueError("cannot restore a snapshot: " + output) from e - def restore_from_backup(self, snapshot_id, folders: List[str], verify=True): + def restore_from_backup( + self, + snapshot_id, + folders: List[str], + verify=True, + ): """ Restore from backup with restic """ @@ -236,6 +241,9 @@ class ResticBackupper(AbstractBackupper): dst = folder sync(src, dst) + if not verify: + self.unmount_repo(dir) + def do_restore(self, snapshot_id, target="/", verify=False): """barebones restic restore""" restore_command = self.restic_command(