From a5b52c8f7584d944642b61d5199cf4cf8993327e Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 29 Jul 2024 12:16:33 +0000 Subject: [PATCH] feature(backup): endpoint to force autobackup --- selfprivacy_api/backup/tasks.py | 15 +++++++ .../graphql/mutations/backup_mutations.py | 17 +++++++ tests/test_graphql/test_api_backup.py | 44 ++++++++++++++++++- 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/backup/tasks.py b/selfprivacy_api/backup/tasks.py index 1d775fc..515c56a 100644 --- a/selfprivacy_api/backup/tasks.py +++ b/selfprivacy_api/backup/tasks.py @@ -82,6 +82,12 @@ def do_autobackup() -> None: inside tests """ time = datetime.now(timezone.utc) + backups_were_disabled = Backups.autobackup_period_minutes() is None + + if backups_were_disabled: + # Temporarily enable autobackup + Backups.set_autobackup_period_minutes(24 * 60) # 1 day + services_to_back_up = Backups.services_to_back_up(time) if not services_to_back_up: return @@ -104,7 +110,11 @@ def do_autobackup() -> None: progress = progress + progress_per_service Jobs.update(job, JobStatus.RUNNING, progress=progress) + if backups_were_disabled: + Backups.set_autobackup_period_minutes(0) Jobs.update(job, JobStatus.FINISHED) + # there is no point of returning the job + # this code is called with a delay def eligible_for_full_restoration(snap: Snapshot): @@ -194,7 +204,12 @@ def automatic_backup() -> None: """ The worker periodic task that starts the automatic backup process. """ + + +@huey.task() +def trigger_autobackup() -> bool: do_autobackup() + return True @huey.periodic_task(crontab(hour="*/" + str(SNAPSHOT_CACHE_TTL_HOURS))) diff --git a/selfprivacy_api/graphql/mutations/backup_mutations.py b/selfprivacy_api/graphql/mutations/backup_mutations.py index 0f81c29..ce27ac0 100644 --- a/selfprivacy_api/graphql/mutations/backup_mutations.py +++ b/selfprivacy_api/graphql/mutations/backup_mutations.py @@ -25,6 +25,7 @@ from selfprivacy_api.backup.tasks import ( restore_snapshot, prune_autobackup_snapshots, full_restore, + trigger_autobackup, ) from selfprivacy_api.backup.jobs import ( add_backup_job, @@ -171,6 +172,22 @@ class BackupMutations: job=job_to_api_job(job), ) + @strawberry.mutation(permission_classes=[IsAuthenticated]) + def manual_autobackup(self) -> GenericMutationReturn: + """Induce autobackup to back up all the services at once + Useful when migrating but a bit of a hack + """ + + # This cannot give us a job, unfortunately + # TODO: We need to pass it + trigger_autobackup() + + return GenericMutationReturn( + success=True, + code=200, + message="Backup task queued", + ) + @strawberry.mutation(permission_classes=[IsAuthenticated]) def restore_all(self) -> GenericJobMutationReturn: """ diff --git a/tests/test_graphql/test_api_backup.py b/tests/test_graphql/test_api_backup.py index 6365ce4..f5cc061 100644 --- a/tests/test_graphql/test_api_backup.py +++ b/tests/test_graphql/test_api_backup.py @@ -14,6 +14,13 @@ from selfprivacy_api.jobs import Jobs, JobStatus from selfprivacy_api.backup.storage import Storage from selfprivacy_api.backup.local_secret import LocalBackupSecret +from tests.test_graphql.test_services import ( + only_dummy_service_and_api, + only_dummy_service, + dkim_file, +) + + API_RELOAD_SNAPSHOTS = """ mutation TestSnapshotsReload { backup { @@ -26,6 +33,18 @@ mutation TestSnapshotsReload { } """ +API_MANUAL_AUTOBACKUP = """ +mutation TestForcedAutobackup { + backup { + manualAutobackup{ + success + message + code + } + } +} +""" + API_SET_AUTOBACKUP_PERIOD_MUTATION = """ mutation TestAutobackupPeriod($period: Int) { backup { @@ -259,6 +278,17 @@ def api_reload_snapshots(authorized_client): return response +def api_manual_autobackup(authorized_client): + response = authorized_client.post( + "/graphql", + json={ + "query": API_MANUAL_AUTOBACKUP, + "variables": {}, + }, + ) + return response + + def api_init( authorized_client, kind, @@ -403,7 +433,7 @@ def test_reinit(authorized_client, dummy_service, tmpdir, backups): assert Jobs.get_job(job["uid"]).status == JobStatus.FINISHED -def test_migrate(authorized_client, dummy_service, tmpdir, backups): +def test_migrate_backup_repo(authorized_client, dummy_service, tmpdir, backups): """ Simulate the workflow of migrating to a new server """ @@ -554,6 +584,18 @@ def test_reload_snapshots_bare_bare_bare(authorized_client, dummy_service, backu assert snaps == [] +def test_induce_autobackup(authorized_client, only_dummy_service_and_api, backups): + dummy_service = only_dummy_service_and_api + + response = api_manual_autobackup(authorized_client) + # raise ValueError(get_data(response)) + data = get_data(response)["backup"]["manualAutobackup"] + assert_ok(data) + + snaps = api_snapshots(authorized_client) + assert len(snaps) == 2 + + def test_reload_snapshots(authorized_client, dummy_service, backups): response = api_backup(authorized_client, dummy_service) data = get_data(response)["backup"]["startBackup"]