mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
backups: Allow comments to be added to archives during backup
Tests performed: - Schedules are able to store and retrieve comments properly. Information about schedule backups stored in comments is extracted properly. - Unit tests run. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
d3f8e815c1
commit
01e00cdde4
@ -13,6 +13,7 @@ import sys
|
||||
import tarfile
|
||||
|
||||
from plinth.modules.backups import MANIFESTS_FOLDER
|
||||
from plinth.utils import Version
|
||||
|
||||
TIMEOUT = 30
|
||||
|
||||
@ -38,6 +39,9 @@ def parse_arguments():
|
||||
help='Create archive')
|
||||
create_archive.add_argument('--paths', help='Paths to include in archive',
|
||||
nargs='+')
|
||||
create_archive.add_argument('--comment',
|
||||
help='Comment text to add to archive',
|
||||
default='')
|
||||
|
||||
delete_archive = subparsers.add_parser('delete-archive',
|
||||
help='Delete archive')
|
||||
@ -112,13 +116,32 @@ def subcommand_info(arguments):
|
||||
|
||||
def subcommand_list_repo(arguments):
|
||||
"""List repository contents."""
|
||||
run(['borg', 'list', '--json', arguments.path], arguments)
|
||||
run(['borg', 'list', '--json', '--format="{comment}"', arguments.path],
|
||||
arguments)
|
||||
|
||||
|
||||
def _get_borg_version(arugments):
|
||||
"""Return the version of borgbackup."""
|
||||
process = run(['borg', '--version'], arugments, stdout=subprocess.PIPE)
|
||||
return process.stdout.decode().split()[1] # Example: "borg 1.1.9"
|
||||
|
||||
|
||||
def subcommand_create_archive(arguments):
|
||||
"""Create archive."""
|
||||
paths = filter(os.path.exists, arguments.paths)
|
||||
run(['borg', 'create', '--json', arguments.path] + list(paths), arguments)
|
||||
command = ['borg', 'create', '--json']
|
||||
if arguments.comment:
|
||||
comment = arguments.comment
|
||||
if Version(_get_borg_version(arguments)) < Version('1.1.10'):
|
||||
# Undo any placeholder escape sequences in comments as this version
|
||||
# of borg does not support placeholders. XXX: Drop this code when
|
||||
# support for borg < 1.1.10 is dropped.
|
||||
comment = comment.replace('{{', '{').replace('}}', '}')
|
||||
|
||||
command += ['--comment', comment]
|
||||
|
||||
command += [arguments.path] + list(paths)
|
||||
run(command, arguments)
|
||||
|
||||
|
||||
def subcommand_delete_archive(arguments):
|
||||
|
||||
@ -85,7 +85,11 @@ def _backup_handler(packet, encryption_passphrase=None):
|
||||
|
||||
paths = packet.directories + packet.files
|
||||
paths.append(manifest_path)
|
||||
arguments = ['create-archive', '--path', packet.path, '--paths'] + paths
|
||||
arguments = ['create-archive', '--path', packet.path]
|
||||
if packet.archive_comment:
|
||||
arguments += ['--comment', packet.archive_comment]
|
||||
|
||||
arguments += ['--paths'] + paths
|
||||
input_data = ''
|
||||
if encryption_passphrase:
|
||||
input_data = json.dumps(
|
||||
|
||||
@ -41,7 +41,8 @@ class BackupError:
|
||||
class Packet:
|
||||
"""Information passed to a handlers for backup/restore operations."""
|
||||
|
||||
def __init__(self, operation, scope, root, components=None, path=None):
|
||||
def __init__(self, operation, scope, root, components=None, path=None,
|
||||
archive_comment=None):
|
||||
"""Initialize the packet.
|
||||
|
||||
operation is either 'backup' or 'restore.
|
||||
@ -62,6 +63,7 @@ class Packet:
|
||||
self.root = root
|
||||
self.components = components
|
||||
self.path = path
|
||||
self.archive_comment = archive_comment
|
||||
self.errors = []
|
||||
|
||||
self.directories = []
|
||||
@ -105,8 +107,8 @@ def restore_full(restore_handler):
|
||||
_switch_to_subvolume(subvolume)
|
||||
|
||||
|
||||
def backup_apps(backup_handler, path, app_ids=None,
|
||||
encryption_passphrase=None):
|
||||
def backup_apps(backup_handler, path, app_ids=None, encryption_passphrase=None,
|
||||
archive_comment=None):
|
||||
"""Backup data belonging to a set of applications."""
|
||||
if not app_ids:
|
||||
components = get_all_components_for_backup()
|
||||
@ -123,7 +125,8 @@ def backup_apps(backup_handler, path, app_ids=None,
|
||||
backup_root = '/'
|
||||
snapshotted = False
|
||||
|
||||
packet = Packet('backup', 'apps', backup_root, components, path)
|
||||
packet = Packet('backup', 'apps', backup_root, components, path,
|
||||
archive_comment)
|
||||
_run_operation(backup_handler, packet,
|
||||
encryption_passphrase=encryption_passphrase)
|
||||
|
||||
|
||||
@ -166,12 +166,13 @@ class BaseBorgRepository(abc.ABC):
|
||||
return sorted(archives, key=lambda archive: archive['start'],
|
||||
reverse=True)
|
||||
|
||||
def create_archive(self, archive_name, app_ids):
|
||||
def create_archive(self, archive_name, app_ids, archive_comment=None):
|
||||
"""Create a new archive in this repository with given name."""
|
||||
archive_path = self._get_archive_path(archive_name)
|
||||
passphrase = self.credentials.get('encryption_passphrase', None)
|
||||
api.backup_apps(_backup_handler, path=archive_path, app_ids=app_ids,
|
||||
encryption_passphrase=passphrase)
|
||||
encryption_passphrase=passphrase,
|
||||
archive_comment=archive_comment)
|
||||
|
||||
def delete_archive(self, archive_name):
|
||||
"""Delete an archive with given name from this repository."""
|
||||
|
||||
@ -63,11 +63,21 @@ def _get_test_app(name):
|
||||
class TestBackupProcesses:
|
||||
"""Test cases for backup processes"""
|
||||
|
||||
@staticmethod
|
||||
def test_packet_init():
|
||||
"""Test that packet is initialized properly."""
|
||||
packet = api.Packet('backup', 'apps', '/', [])
|
||||
assert packet.archive_comment is None
|
||||
packet = api.Packet('backup', 'apps', '/', [],
|
||||
archive_comment='test comment')
|
||||
assert packet.archive_comment == 'test comment'
|
||||
|
||||
@staticmethod
|
||||
def test_packet_collected_files_directories():
|
||||
"""Test that directories/files are collected from manifests."""
|
||||
components = [_get_backup_component('a'), _get_backup_component('b')]
|
||||
packet = api.Packet('backup', 'apps', '/', components)
|
||||
packet = api.Packet('backup', 'apps', '/', components,
|
||||
archive_comment='test comment')
|
||||
for component in components:
|
||||
for section in ['config', 'data', 'secrets']:
|
||||
for directory in getattr(component, section)['directories']:
|
||||
|
||||
@ -88,18 +88,21 @@ def test_create_export_delete_archive(data_directory, backup_directory):
|
||||
"""
|
||||
repo_name = 'test_create_and_delete'
|
||||
archive_name = 'first_archive'
|
||||
archive_comment = 'test_archive_comment'
|
||||
path = backup_directory / repo_name
|
||||
|
||||
repository = BorgRepository(str(path))
|
||||
repository.initialize()
|
||||
archive_path = "::".join([str(path), archive_name])
|
||||
actions.superuser_run('backups', [
|
||||
'create-archive', '--path', archive_path, '--paths',
|
||||
'create-archive', '--path', archive_path, '--comment', archive_comment,
|
||||
'--paths',
|
||||
str(data_directory)
|
||||
])
|
||||
|
||||
archive = repository.list_archives()[0]
|
||||
assert archive['name'] == archive_name
|
||||
assert archive['comment'] == archive_comment
|
||||
|
||||
repository.delete_archive(archive_name)
|
||||
content = repository.list_archives()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user