From 28e4fe07913ed3a78a9f88480545b35a264c1725 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 6 Jan 2025 16:41:03 -0800 Subject: [PATCH] backups: Make all generated archive names consistent - For scheduled backups and automatic backups generated when uninstalling apps, make the generated archive name similar to automatic name given no name is specified when creating an archive. Tests: - Set schedule on a local repository, uninstall an application, and create a backup archive without a name. The timestamp in generated backup archives in all three cases is similar in format. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/backups/repository.py | 5 +++++ plinth/modules/backups/schedule.py | 7 ++++--- plinth/modules/backups/tests/test_schedule.py | 11 ++++++++--- plinth/modules/backups/views.py | 4 +--- plinth/views.py | 8 +++----- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/plinth/modules/backups/repository.py b/plinth/modules/backups/repository.py index f2e8680f8..63ac75c6b 100644 --- a/plinth/modules/backups/repository.py +++ b/plinth/modules/backups/repository.py @@ -241,6 +241,11 @@ class BaseBorgRepository(abc.ABC): return None + def generate_archive_name(self): + """Return a name to create a backup archive with using time.""" + return datetime.datetime.now().astimezone().replace( + microsecond=0).isoformat() + def get_archive_apps(self, archive_name): """Get list of apps included in an archive.""" archive_path = self._get_archive_path(archive_name) diff --git a/plinth/modules/backups/schedule.py b/plinth/modules/backups/schedule.py index 1d89a0e73..42f501bc2 100644 --- a/plinth/modules/backups/schedule.py +++ b/plinth/modules/backups/schedule.py @@ -274,12 +274,14 @@ class Schedule: logger.info('Running backup for repository %s, periods %s', self.repository_uuid, periods) + repository = self._get_repository() + from . import api periods = list(periods) periods.sort() - name = 'scheduled: {periods}: {datetime}'.format( + name = 'scheduled: {periods}: {name}'.format( periods=', '.join(periods), - datetime=datetime.now().strftime('%Y-%m-%d:%H:%M')) + name=repository.generate_archive_name()) comment = self._serialize_comment({ 'type': 'scheduled', 'periods': periods @@ -290,7 +292,6 @@ class Schedule: if component.app_id not in self.unselected_apps ] - repository = self._get_repository() repository.create_archive(name, app_ids, archive_comment=comment) def _run_cleanup(self, repository): diff --git a/plinth/modules/backups/tests/test_schedule.py b/plinth/modules/backups/tests/test_schedule.py index 092a2f396..d8520f438 100644 --- a/plinth/modules/backups/tests/test_schedule.py +++ b/plinth/modules/backups/tests/test_schedule.py @@ -9,7 +9,7 @@ from unittest.mock import MagicMock, call, patch import pytest -import plinth.modules.backups.repository # noqa, pylint: disable=unused-import +import plinth.modules.backups.repository as repository_module from plinth.app import App from ..components import BackupRestore @@ -431,15 +431,19 @@ def test_run_schedule(get_instance, get_setup_state, schedule_params, repository.list_archives.side_effect = \ lambda: _get_archives_from_test_data(archives_data) get_instance.return_value = repository + repository.generate_archive_name = lambda: \ + repository_module.BaseBorgRepository.generate_archive_name(None) with patch('plinth.modules.backups.schedule.datetime') as mock_datetime, \ - patch('plinth.app.App.list') as app_list: + patch('plinth.modules.backups.repository.datetime') \ + as repo_datetime, patch('plinth.app.App.list') as app_list: app_list.return_value = [ _get_test_app('test-app1'), _get_test_app('test-app2'), _get_test_app('test-app3') ] + repo_datetime.datetime.now.return_value = test_now mock_datetime.now.return_value = test_now mock_datetime.strptime = datetime.strptime mock_datetime.min = datetime.min @@ -458,7 +462,8 @@ def test_run_schedule(get_instance, get_setup_state, schedule_params, run_periods.sort() name = 'scheduled: {periods}: {datetime}'.format( periods=', '.join(run_periods), - datetime=mock_datetime.now().strftime('%Y-%m-%d:%H:%M')) + datetime=repo_datetime.datetime.now().astimezone().replace( + microsecond=0).isoformat()) app_ids = ['test-app1', 'test-app3'] archive_comment = json.dumps({ 'type': 'scheduled', diff --git a/plinth/modules/backups/views.py b/plinth/modules/backups/views.py index 856abedff..56d8968c3 100644 --- a/plinth/modules/backups/views.py +++ b/plinth/modules/backups/views.py @@ -7,7 +7,6 @@ import contextlib import logging import os import subprocess -from datetime import datetime from urllib.parse import unquote from django.contrib import messages @@ -141,8 +140,7 @@ class CreateArchiveView(FormView): name = form.cleaned_data['name'] if not name: - name = datetime.now().astimezone().replace( - microsecond=0).isoformat() + name = repository.generate_archive_name() selected_apps = form.cleaned_data['selected_apps'] with handle_common_errors(self.request): diff --git a/plinth/views.py b/plinth/views.py index 46abdefca..cbb858689 100644 --- a/plinth/views.py +++ b/plinth/views.py @@ -3,7 +3,6 @@ Main FreedomBox views. """ -import datetime import random import time import traceback @@ -607,10 +606,9 @@ class UninstallView(FormView): if repository.flags.get('mountable'): repository.mount() - name = datetime.datetime.now().strftime( - '%Y-%m-%d:%H:%M:%S') + ' ' + str( - _('before uninstall of {app_id}')).format( - app_id=self.app.app_id) + name = repository.generate_archive_name() + ' ' + str( + _('before uninstall of {app_id}')).format( + app_id=self.app.app_id) repository.create_archive(name, [self.app.app_id]) # Uninstall