mirror of
https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git
synced 2024-11-18 16:39:13 +00:00
style: linting of backups module
This commit is contained in:
parent
3067d353d8
commit
5253780cc8
|
@ -5,7 +5,7 @@ from selfprivacy_api.models.backup.snapshot import Snapshot
|
||||||
|
|
||||||
|
|
||||||
class AbstractBackupper(ABC):
|
class AbstractBackupper(ABC):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -13,11 +13,11 @@ class AbstractBackupper(ABC):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def set_creds(self, account: str, key: str, repo: str):
|
def set_creds(self, account: str, key: str, repo: str) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def start_backup(self, folders: List[str], repo_name: str):
|
def start_backup(self, folders: List[str], repo_name: str) -> Snapshot:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -26,7 +26,7 @@ class AbstractBackupper(ABC):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def init(self):
|
def init(self) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -35,7 +35,7 @@ class AbstractBackupper(ABC):
|
||||||
snapshot_id: str,
|
snapshot_id: str,
|
||||||
folders: List[str],
|
folders: List[str],
|
||||||
verify=True,
|
verify=True,
|
||||||
):
|
) -> None:
|
||||||
"""Restore a target folder using a snapshot"""
|
"""Restore a target folder using a snapshot"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@ -44,5 +44,5 @@ class AbstractBackupper(ABC):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def forget_snapshot(self, snapshot_id):
|
def forget_snapshot(self, snapshot_id) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -21,7 +21,7 @@ from selfprivacy_api.backup.local_secret import LocalBackupSecret
|
||||||
|
|
||||||
|
|
||||||
class ResticBackupper(AbstractBackupper):
|
class ResticBackupper(AbstractBackupper):
|
||||||
def __init__(self, login_flag: str, key_flag: str, type: str):
|
def __init__(self, login_flag: str, key_flag: str, type: str) -> None:
|
||||||
self.login_flag = login_flag
|
self.login_flag = login_flag
|
||||||
self.key_flag = key_flag
|
self.key_flag = key_flag
|
||||||
self.type = type
|
self.type = type
|
||||||
|
@ -29,7 +29,7 @@ class ResticBackupper(AbstractBackupper):
|
||||||
self.key = ""
|
self.key = ""
|
||||||
self.repo = ""
|
self.repo = ""
|
||||||
|
|
||||||
def set_creds(self, account: str, key: str, repo: str):
|
def set_creds(self, account: str, key: str, repo: str) -> None:
|
||||||
self.account = account
|
self.account = account
|
||||||
self.key = key
|
self.key = key
|
||||||
self.repo = repo
|
self.repo = repo
|
||||||
|
@ -79,7 +79,11 @@ class ResticBackupper(AbstractBackupper):
|
||||||
def mount_repo(self, dir):
|
def mount_repo(self, dir):
|
||||||
mount_command = self.restic_command("mount", dir)
|
mount_command = self.restic_command("mount", dir)
|
||||||
mount_command.insert(0, "nohup")
|
mount_command.insert(0, "nohup")
|
||||||
handle = subprocess.Popen(mount_command, stdout=subprocess.DEVNULL, shell=False)
|
handle = subprocess.Popen(
|
||||||
|
mount_command,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
shell=False,
|
||||||
|
)
|
||||||
sleep(2)
|
sleep(2)
|
||||||
if "ids" not in listdir(dir):
|
if "ids" not in listdir(dir):
|
||||||
raise IOError("failed to mount dir ", dir)
|
raise IOError("failed to mount dir ", dir)
|
||||||
|
@ -109,12 +113,13 @@ class ResticBackupper(AbstractBackupper):
|
||||||
result.append(item)
|
result.append(item)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def start_backup(self, folders: List[str], tag: str):
|
def start_backup(self, folders: List[str], tag: str) -> Snapshot:
|
||||||
"""
|
"""
|
||||||
Start backup with restic
|
Start backup with restic
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# but maybe it is ok to accept a union of a string and an array of strings
|
# but maybe it is ok to accept a union
|
||||||
|
# of a string and an array of strings
|
||||||
assert not isinstance(folders, str)
|
assert not isinstance(folders, str)
|
||||||
|
|
||||||
backup_command = self.restic_command(
|
backup_command = self.restic_command(
|
||||||
|
@ -125,20 +130,34 @@ class ResticBackupper(AbstractBackupper):
|
||||||
)
|
)
|
||||||
|
|
||||||
messages = []
|
messages = []
|
||||||
job = get_backup_job(get_service_by_id(tag))
|
|
||||||
|
service = get_service_by_id(tag)
|
||||||
|
if service is None:
|
||||||
|
raise ValueError("No service with id ", tag)
|
||||||
|
|
||||||
|
job = get_backup_job(service)
|
||||||
try:
|
try:
|
||||||
for raw_message in output_yielder(backup_command):
|
for raw_message in output_yielder(backup_command):
|
||||||
message = self.parse_message(raw_message, job)
|
message = self.parse_message(
|
||||||
|
raw_message,
|
||||||
|
job,
|
||||||
|
)
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
return ResticBackupper._snapshot_from_backup_messages(messages, tag)
|
return ResticBackupper._snapshot_from_backup_messages(
|
||||||
|
messages,
|
||||||
|
tag,
|
||||||
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ValueError("could not create a snapshot: ", messages) from e
|
raise ValueError("Could not create a snapshot: ", messages) from e
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _snapshot_from_backup_messages(messages, repo_name) -> Snapshot:
|
def _snapshot_from_backup_messages(messages, repo_name) -> Snapshot:
|
||||||
for message in messages:
|
for message in messages:
|
||||||
if message["message_type"] == "summary":
|
if message["message_type"] == "summary":
|
||||||
return ResticBackupper._snapshot_from_fresh_summary(message, repo_name)
|
return ResticBackupper._snapshot_from_fresh_summary(
|
||||||
|
message,
|
||||||
|
repo_name,
|
||||||
|
)
|
||||||
raise ValueError("no summary message in restic json output")
|
raise ValueError("no summary message in restic json output")
|
||||||
|
|
||||||
def parse_message(self, raw_message_line: str, job=None) -> dict:
|
def parse_message(self, raw_message_line: str, job=None) -> dict:
|
||||||
|
@ -162,7 +181,7 @@ class ResticBackupper(AbstractBackupper):
|
||||||
service_name=repo_name,
|
service_name=repo_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
def init(self):
|
def init(self) -> None:
|
||||||
init_command = self.restic_command(
|
init_command = self.restic_command(
|
||||||
"init",
|
"init",
|
||||||
)
|
)
|
||||||
|
@ -173,7 +192,7 @@ class ResticBackupper(AbstractBackupper):
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
) as process_handle:
|
) as process_handle:
|
||||||
output = process_handle.communicate()[0].decode("utf-8")
|
output = process_handle.communicate()[0].decode("utf-8")
|
||||||
if not "created restic repository" in output:
|
if "created restic repository" not in output:
|
||||||
raise ValueError("cannot init a repo: " + output)
|
raise ValueError("cannot init a repo: " + output)
|
||||||
|
|
||||||
def is_initted(self) -> bool:
|
def is_initted(self) -> bool:
|
||||||
|
@ -182,7 +201,11 @@ class ResticBackupper(AbstractBackupper):
|
||||||
"--json",
|
"--json",
|
||||||
)
|
)
|
||||||
|
|
||||||
with subprocess.Popen(command, stdout=subprocess.PIPE, shell=False) as handle:
|
with subprocess.Popen(
|
||||||
|
command,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
shell=False,
|
||||||
|
) as handle:
|
||||||
output = handle.communicate()[0].decode("utf-8")
|
output = handle.communicate()[0].decode("utf-8")
|
||||||
if not ResticBackupper.has_json(output):
|
if not ResticBackupper.has_json(output):
|
||||||
return False
|
return False
|
||||||
|
@ -216,7 +239,7 @@ class ResticBackupper(AbstractBackupper):
|
||||||
snapshot_id,
|
snapshot_id,
|
||||||
folders: List[str],
|
folders: List[str],
|
||||||
verify=True,
|
verify=True,
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Restore from backup with restic
|
Restore from backup with restic
|
||||||
"""
|
"""
|
||||||
|
@ -235,9 +258,7 @@ class ResticBackupper(AbstractBackupper):
|
||||||
for folder in folders:
|
for folder in folders:
|
||||||
src = join(snapshot_root, folder.strip("/"))
|
src = join(snapshot_root, folder.strip("/"))
|
||||||
if not exists(src):
|
if not exists(src):
|
||||||
raise ValueError(
|
raise ValueError(f"No such path: {src}. We tried to find {folder}")
|
||||||
f"there is no such path: {src}. We tried to find {folder}"
|
|
||||||
)
|
|
||||||
dst = folder
|
dst = folder
|
||||||
sync(src, dst)
|
sync(src, dst)
|
||||||
|
|
||||||
|
@ -254,7 +275,8 @@ class ResticBackupper(AbstractBackupper):
|
||||||
restore_command, stdout=subprocess.PIPE, shell=False
|
restore_command, stdout=subprocess.PIPE, shell=False
|
||||||
) as handle:
|
) as handle:
|
||||||
|
|
||||||
# for some reason restore does not support nice reporting of progress via json
|
# for some reason restore does not support
|
||||||
|
# nice reporting of progress via json
|
||||||
output = handle.communicate()[0].decode("utf-8")
|
output = handle.communicate()[0].decode("utf-8")
|
||||||
if "restoring" not in output:
|
if "restoring" not in output:
|
||||||
raise ValueError("cannot restore a snapshot: " + output)
|
raise ValueError("cannot restore a snapshot: " + output)
|
||||||
|
@ -264,21 +286,36 @@ class ResticBackupper(AbstractBackupper):
|
||||||
) # none should be impossible after communicate
|
) # none should be impossible after communicate
|
||||||
if handle.returncode != 0:
|
if handle.returncode != 0:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"restore exited with errorcode", returncode, ":", output
|
"restore exited with errorcode",
|
||||||
|
handle.returncode,
|
||||||
|
":",
|
||||||
|
output,
|
||||||
)
|
)
|
||||||
|
|
||||||
def forget_snapshot(self, snapshot_id):
|
def forget_snapshot(self, snapshot_id) -> None:
|
||||||
"""either removes snapshot or marks it for deletion later depending on server settings"""
|
"""
|
||||||
|
Either removes snapshot or marks it for deletion later,
|
||||||
|
depending on server settings
|
||||||
|
"""
|
||||||
forget_command = self.restic_command(
|
forget_command = self.restic_command(
|
||||||
"forget",
|
"forget",
|
||||||
snapshot_id,
|
snapshot_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
with subprocess.Popen(
|
with subprocess.Popen(
|
||||||
forget_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False
|
forget_command,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
shell=False,
|
||||||
) as handle:
|
) as handle:
|
||||||
# for some reason restore does not support nice reporting of progress via json
|
# for some reason restore does not support
|
||||||
output, err = [string.decode("utf-8") for string in handle.communicate()]
|
# nice reporting of progress via json
|
||||||
|
output, err = [
|
||||||
|
string.decode(
|
||||||
|
"utf-8",
|
||||||
|
)
|
||||||
|
for string in handle.communicate()
|
||||||
|
]
|
||||||
|
|
||||||
if "no matching ID found" in err:
|
if "no matching ID found" in err:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
@ -290,7 +327,10 @@ class ResticBackupper(AbstractBackupper):
|
||||||
) # none should be impossible after communicate
|
) # none should be impossible after communicate
|
||||||
if handle.returncode != 0:
|
if handle.returncode != 0:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"forget exited with errorcode", returncode, ":", output
|
"forget exited with errorcode",
|
||||||
|
handle.returncode,
|
||||||
|
":",
|
||||||
|
output,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _load_snapshots(self) -> object:
|
def _load_snapshots(self) -> object:
|
||||||
|
@ -336,7 +376,7 @@ class ResticBackupper(AbstractBackupper):
|
||||||
starting_index = ResticBackupper.json_start(output)
|
starting_index = ResticBackupper.json_start(output)
|
||||||
|
|
||||||
if starting_index == -1:
|
if starting_index == -1:
|
||||||
raise ValueError("There is no json in the restic output : " + output)
|
raise ValueError("There is no json in the restic output: " + output)
|
||||||
|
|
||||||
truncated_output = output[starting_index:]
|
truncated_output = output[starting_index:]
|
||||||
json_messages = truncated_output.splitlines()
|
json_messages = truncated_output.splitlines()
|
||||||
|
|
|
@ -74,11 +74,17 @@ class Storage:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
snapshot = hash_as_model(redis, key, Snapshot)
|
snapshot = hash_as_model(redis, key, Snapshot)
|
||||||
|
if not snapshot:
|
||||||
|
return None
|
||||||
return snapshot.created_at
|
return snapshot.created_at
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def store_last_timestamp(service_id: str, snapshot: Snapshot):
|
def store_last_timestamp(service_id: str, snapshot: Snapshot):
|
||||||
store_model_as_hash(redis, Storage.__last_backup_key(service_id), snapshot)
|
store_model_as_hash(
|
||||||
|
redis,
|
||||||
|
Storage.__last_backup_key(service_id),
|
||||||
|
snapshot,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cache_snapshot(snapshot: Snapshot):
|
def cache_snapshot(snapshot: Snapshot):
|
||||||
|
|
Loading…
Reference in a new issue