backups: Separate repository loading from instantiation

The constructor of the Repository object is being used for two distinct
purposes. One is to load the object from database and other to instantiate it
with parameter such that it can be saved to database. Separating the two usages
to different methods simplifies code and parameter passing for consumers.

Also turn some class specific constants from globals to class constants.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2019-08-28 16:24:05 -07:00 committed by James Valleroy
parent 5b01689a92
commit 7467fa4553
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
5 changed files with 43 additions and 58 deletions

View File

@ -30,7 +30,6 @@ from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth.utils import format_lazy
from . import api
@ -49,10 +48,6 @@ description = [
manual_page = 'Backups'
MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/'
ROOT_REPOSITORY = '/var/lib/freedombox/borgbackup'
ROOT_REPOSITORY_NAME = format_lazy(_('{box_name} storage'),
box_name=cfg.box_name)
ROOT_REPOSITORY_UUID = 'root'
# session variable name that stores when a backup file should be deleted
SESSION_PATH_VARIABLE = 'fbx-backups-upload-path'
@ -85,8 +80,9 @@ def init():
def setup(helper, old_version=None):
"""Install and configure the module."""
helper.install(managed_packages)
from . import repository
helper.call('post', actions.superuser_run, 'backups',
['setup', '--path', ROOT_REPOSITORY])
['setup', '--path', repository.RootBorgRepository.PATH])
helper.call('post', app.enable)

View File

@ -27,11 +27,11 @@ from uuid import uuid1
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth import actions, cfg
from plinth.errors import ActionError
from plinth.utils import format_lazy
from . import (ROOT_REPOSITORY, ROOT_REPOSITORY_NAME, ROOT_REPOSITORY_UUID,
_backup_handler, api, get_known_hosts_path,
from . import (_backup_handler, api, get_known_hosts_path,
restore_archive_handler, split_path, store)
from .errors import BorgError, BorgRepositoryDoesNotExistError, SshfsError
@ -83,27 +83,27 @@ class BaseBorgRepository(abc.ABC):
flags = {}
is_mounted = True
def __init__(self, uuid=None, path=None, credentials=None, **kwargs):
"""
Instantiate a new repository.
def __init__(self, path, credentials=None, uuid=None, **kwargs):
"""Instantiate a new repository.
If only a uuid is given, load the values from kvstore.
"""
self._path = path
self.credentials = credentials or {}
self.uuid = uuid or str(uuid1())
self.kwargs = kwargs
if not uuid:
uuid = str(uuid1())
self.uuid = uuid
@classmethod
def load(cls, uuid):
"""Create instance by loading from database."""
storage = dict(store.get(uuid))
path = storage.pop('path')
storage.pop('uuid')
credentials = storage.setdefault('credentials', {})
storage.pop('credentials')
if path and credentials:
self._path = path
self.credentials = credentials
else:
# Either a uuid, or both a path and credentials must be given
if not bool(uuid):
raise ValueError('Invalid arguments.')
else:
self._load_from_kvstore()
return cls(path, credentials, uuid, **storage)
@property
def name(self):
@ -139,14 +139,6 @@ class BaseBorgRepository(abc.ABC):
return {}
def _load_from_kvstore(self):
storage = store.get(self.uuid)
try:
self.credentials = storage['credentials']
except KeyError:
self.credentials = {}
self._path = storage['path']
def get_info(self):
"""Return Borg information about a repository."""
output = self.run(['info', '--path', self.borg_path])
@ -309,20 +301,18 @@ class BaseBorgRepository(abc.ABC):
class RootBorgRepository(BaseBorgRepository):
"""Borg repository on the root filesystem."""
UUID = 'root'
PATH = '/var/lib/freedombox/borgbackup'
storage_type = 'root'
name = ROOT_REPOSITORY_NAME
borg_path = ROOT_REPOSITORY
name = format_lazy(_('{box_name} storage'), box_name=cfg.box_name)
borg_path = PATH
sort_order = 10
is_mounted = True
def __init__(self, path, credentials=None):
def __init__(self, credentials=None):
"""Initialize the repository object."""
self.uuid = ROOT_REPOSITORY_UUID
if credentials is None:
credentials = {}
self._path = path
self.credentials = credentials
super().__init__(self.PATH, credentials, self.UUID)
def run(self, arguments):
return self._run('backups', arguments)
@ -439,7 +429,7 @@ class SshBorgRepository(BaseBorgRepository):
def get_repositories():
"""Get all repositories of a given storage type."""
repositories = [get_instance(ROOT_REPOSITORY_UUID)]
repositories = [get_instance(RootBorgRepository.UUID)]
for uuid in store.get_storages():
repositories.append(get_instance(uuid))
@ -448,11 +438,11 @@ def get_repositories():
def get_instance(uuid):
"""Create a local or SSH repository object instance."""
if uuid == ROOT_REPOSITORY_UUID:
return RootBorgRepository(path=ROOT_REPOSITORY)
if uuid == RootBorgRepository.UUID:
return RootBorgRepository()
storage = store.get(uuid)
if storage['storage_type'] == 'ssh':
return SshBorgRepository(uuid=uuid)
return SshBorgRepository.load(uuid)
return BorgRepository(uuid=uuid)
return BorgRepository.load(uuid)

View File

@ -23,7 +23,7 @@ from unittest.mock import MagicMock, call, patch
import pytest
from django.core.files.uploadedfile import SimpleUploadedFile
from .. import ROOT_REPOSITORY, api, forms
from .. import api, forms, repository
# pylint: disable=protected-access
@ -99,7 +99,8 @@ class TestBackupProcesses:
def test_backup_apps():
"""Test that backup_handler is called."""
backup_handler = MagicMock()
api.backup_apps(backup_handler, path=ROOT_REPOSITORY)
api.backup_apps(backup_handler,
path=repository.RootBorgRepository.PATH)
backup_handler.assert_called_once()
@staticmethod

View File

@ -160,7 +160,7 @@ def test_sshfs_mount_password():
credentials = _get_credentials()
ssh_path = test_config.backups_ssh_path
repository = SshBorgRepository(path=ssh_path, credentials=credentials)
repository = SshBorgRepository(ssh_path, credentials)
repository.mount()
assert repository.is_mounted
repository.umount()
@ -173,7 +173,7 @@ def test_sshfs_mount_keyfile():
credentials = _get_credentials()
ssh_path = test_config.backups_ssh_path
repository = SshBorgRepository(path=ssh_path, credentials=credentials)
repository = SshBorgRepository(ssh_path, credentials)
repository.mount()
assert repository.is_mounted
repository.umount()
@ -183,8 +183,7 @@ def test_sshfs_mount_keyfile():
def test_access_nonexisting_url():
"""Test accessing a non-existent URL."""
repo_url = "user@%s.com.au:~/repo" % str(uuid.uuid1())
repository = SshBorgRepository(path=repo_url,
credentials=_dummy_credentials)
repository = SshBorgRepository(repo_url, _dummy_credentials)
with pytest.raises(backups.errors.BorgRepositoryDoesNotExistError):
repository.get_info()
@ -192,8 +191,7 @@ def test_access_nonexisting_url():
def test_inaccessible_repo_url():
"""Test accessing an existing URL with wrong credentials."""
repo_url = 'user@heise.de:~/repo'
repository = SshBorgRepository(path=repo_url,
credentials=_dummy_credentials)
repository = SshBorgRepository(repo_url, _dummy_credentials)
with pytest.raises(backups.errors.BorgError):
repository.get_info()

View File

@ -264,7 +264,7 @@ class AddRepositoryView(SuccessMessageMixin, FormView):
encryption_passphrase = None
credentials = {'encryption_passphrase': encryption_passphrase}
repository = BorgRepository(path=path, credentials=credentials)
repository = BorgRepository(path, credentials)
try:
repository.get_info()
except BorgRepositoryDoesNotExistError:
@ -298,7 +298,7 @@ class AddRemoteRepositoryView(SuccessMessageMixin, FormView):
'ssh_password': form.cleaned_data.get('ssh_password'),
'encryption_passphrase': encryption_passphrase
}
repository = SshBorgRepository(path=path, credentials=credentials)
repository = SshBorgRepository(path, credentials)
repository.save(verified=False)
messages.success(self.request, _('Added new remote SSH repository.'))
@ -471,7 +471,7 @@ class RemoveRepositoryView(SuccessMessageMixin, TemplateView):
def umount_repository(request, uuid):
"""View to unmount a remote SSH repository."""
repository = SshBorgRepository(uuid=uuid)
repository = SshBorgRepository.load(uuid)
repository.umount()
if repository.is_mounted:
messages.error(request, _('Unmounting failed!'))
@ -485,7 +485,7 @@ def mount_repository(request, uuid):
if not get_instance(uuid).is_usable():
return redirect('backups:verify-ssh-hostkey', uuid=uuid)
repository = SshBorgRepository(uuid=uuid)
repository = SshBorgRepository.load(uuid)
try:
repository.mount()
except Exception as err: