From cbf917ad8a59944db7588a4f3ec1262c950faf71 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 17 Feb 2023 15:55:19 +0000 Subject: [PATCH] refactor(backups): snapshotlist and local secret groundwork --- selfprivacy_api/backup/__init__.py | 9 +++++++ selfprivacy_api/backup/local_secret.py | 30 +++++++++++++++++++++++ selfprivacy_api/backup/restic_backuper.py | 23 +++++++++++------ tests/test_graphql/test_backup.py | 10 ++++++-- 4 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 selfprivacy_api/backup/local_secret.py diff --git a/selfprivacy_api/backup/__init__.py b/selfprivacy_api/backup/__init__.py index 5ddd378..4410809 100644 --- a/selfprivacy_api/backup/__init__.py +++ b/selfprivacy_api/backup/__init__.py @@ -1,3 +1,7 @@ +from typing import List + +from selfprivacy_api.models.backup.snapshot import Snapshot + from selfprivacy_api.utils.singleton_metaclass import SingletonMetaclass from selfprivacy_api.services.service import Service @@ -41,3 +45,8 @@ class Backups(metaclass=SingletonMetaclass): service.pre_backup() self.provider.backuper.start_backup(folder, repo_name) service.post_restore() + + def get_snapshots(self, service: Service) -> List[Snapshot]: + repo_name = service.get_id() + + return self.provider.backuper.get_snapshots(repo_name) diff --git a/selfprivacy_api/backup/local_secret.py b/selfprivacy_api/backup/local_secret.py new file mode 100644 index 0000000..f2ebf06 --- /dev/null +++ b/selfprivacy_api/backup/local_secret.py @@ -0,0 +1,30 @@ +"""Handling of local secret used for encrypted backups. +Separated out for circular dependency reasons +""" + +REDIS_KEY = "backup:local_secret" + + +class LocalBackupSecret: + @staticmethod + def get(): + """A secret string which backblaze/other clouds do not know. + Serves as encryption key. + TODO: generate and save in redis + """ + return "TEMPORARY_SECRET" + + @staticmethod + def reset(): + pass + + def exists(): + pass + + @staticmethod + def _generate(): + pass + + @staticmethod + def _store(secret: str): + pass diff --git a/selfprivacy_api/backup/restic_backuper.py b/selfprivacy_api/backup/restic_backuper.py index 2c120f1..3d02d07 100644 --- a/selfprivacy_api/backup/restic_backuper.py +++ b/selfprivacy_api/backup/restic_backuper.py @@ -6,6 +6,8 @@ from typing import List from selfprivacy_api.backup.backuper import AbstractBackuper from selfprivacy_api.models.backup.snapshot import Snapshot +from selfprivacy_api.backup.local_secret import LocalBackupSecret + class ResticBackuper(AbstractBackuper): def __init__(self, login_flag: str, key_flag: str, type: str): @@ -37,6 +39,9 @@ class ResticBackuper(AbstractBackuper): return f"{acc_arg} {key_arg}" + def _password_command(self): + return f"echo {LocalBackupSecret.get()}" + def restic_command(self, repo_name: str, *args): command = [ "restic", @@ -44,6 +49,8 @@ class ResticBackuper(AbstractBackuper): self.rclone_args(), "-r", self.restic_repo(repo_name), + "--password-command", + self._password_command(), ] if args != []: command.extend(args) @@ -87,6 +94,7 @@ class ResticBackuper(AbstractBackuper): def _load_snapshots(self, repo_name) -> object: """ Load list of snapshots from repository + raises Value Error if repo does not exist """ listing_command = self.restic_command( repo_name, @@ -102,13 +110,12 @@ class ResticBackuper(AbstractBackuper): ) as backup_listing_process_descriptor: output = backup_listing_process_descriptor.communicate()[0].decode("utf-8") + if "Is there a repository at the following location?" in output: + raise ValueError("No repository! : " + output) try: return self.parse_snapshot_output(output) - except ValueError: - if "Is there a repository at the following location?" in output: - return [] - self.error_message = output - return [] + except ValueError as e: + raise ValueError("Cannot load snapshots: ") from e def get_snapshots(self, repo_name) -> List[Snapshot]: """Get all snapshots from the repo""" @@ -119,7 +126,7 @@ class ResticBackuper(AbstractBackuper): return snapshots def parse_snapshot_output(self, output: str) -> object: + if "[" not in output: + raise ValueError("There is no json in the restic snapshot output") starting_index = output.find("[") - json.loads(output[starting_index:]) - self.snapshot_list = json.loads(output[starting_index:]) - print(output) + return json.loads(output[starting_index:]) diff --git a/tests/test_graphql/test_backup.py b/tests/test_graphql/test_backup.py index ee8ee0e..bb3b624 100644 --- a/tests/test_graphql/test_backup.py +++ b/tests/test_graphql/test_backup.py @@ -59,5 +59,11 @@ def test_backup_service(test_service, backups): backups.back_up(test_service) -def test_no_snapshots(memory_backup): - assert memory_backup.backuper.get_snapshots("") == [] +def test_no_repo(memory_backup): + with pytest.raises(ValueError): + assert memory_backup.backuper.get_snapshots("") == [] + + +# def test_one_snapshot(backups, test_service): +# backups.back_up(test_service) +# assert len(backups.get_snapshots(test_service)) == 1