From 7de5d26a81ccf05cb7e92794f5eddb40730d4540 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 26 Jul 2024 10:12:02 +0000 Subject: [PATCH] feature(backup): full restore task --- selfprivacy_api/backup/tasks.py | 83 ++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/backup/tasks.py b/selfprivacy_api/backup/tasks.py index 5ee5e61..4c8c638 100644 --- a/selfprivacy_api/backup/tasks.py +++ b/selfprivacy_api/backup/tasks.py @@ -15,8 +15,10 @@ from huey import crontab from selfprivacy_api.services import ServiceManager from selfprivacy_api.backup import Backups -from selfprivacy_api.backup.jobs import add_autobackup_job +from selfprivacy_api.backup.jobs import add_autobackup_job, add_total_restore_job from selfprivacy_api.jobs import Jobs, JobStatus, Job +from selfprivacy_api.jobs.upgrade_system import rebuild_system +from selfprivacy_api.actions.system import add_rebuild_job SNAPSHOT_CACHE_TTL_HOURS = 6 @@ -105,6 +107,85 @@ def do_autobackup() -> None: Jobs.update(job, JobStatus.FINISHED) +def eligible_for_full_restoration(snap: Snapshot): + service = ServiceManager.get_service_by_id(Snapshot.service_name) + if service is None: + return False + if service.is_enabled() is False: + return False + return True + + +def do_full_restore(job: Job) -> None: + """ + Body full restore task, a part of server migration. + Broken out to test it independently from task infra + """ + + Jobs.update( + job, + JobStatus.RUNNING, + status_text=f"Finding the last autobackup session", + progress=0, + ) + autoslice = Backups.last_autobackup_slice() + + api_snapshot = None + for snap in autoslice: + if snap.service_name == "api": + api_snapshot = snap + autoslice.remove(snap) + if api_snapshot is None: + raise ValueError( + "Cannot restore, no configuration snapshot found. This particular error should be unreachable" + ) + + snapshots_to_restore = [ + snap for snap in autoslice if eligible_for_full_restoration(snap) + ] + + progress_per_service = 99 // len(snapshots_to_restore) + progress = 0 + Jobs.update(job, JobStatus.RUNNING, progress=progress) + + # API should be restored in the very end of the list because it requires rebuild right afterwards + snapshots_to_restore.append(api_snapshot) + + for snap in snapshots_to_restore: + try: + Backups.restore_snapshot(snap) + except Exception as error: + report_job_error(error, job) + progress = progress + progress_per_service + Jobs.update( + job, + JobStatus.RUNNING, + status_text=f"restoring {snap.service_name}", + progress=progress, + ) + + Jobs.update(job, JobStatus.RUNNING, status_text="rebuilding system", progress=99) + + # Adding a separate job to not confuse the user with jumping progress bar + rebuild_job = add_rebuild_job() + rebuild_system(rebuild_job) + Jobs.update(job, JobStatus.FINISHED) + + +def report_job_error(error: Exception, job: Job): + Jobs.update( + job, + status=JobStatus.ERROR, + error=type(error).__name__ + ": " + str(error), + ) + + +@huey.task() +def full_restore(job: Job) -> bool: + do_full_restore(job) + return True + + @huey.periodic_task(validate_datetime=validate_datetime) def automatic_backup() -> None: """