diff --git a/NEWS b/NEWS index 364e82e6..74aa862e 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,9 @@ 1.9.2.dev0 * #932: Fix missing build backend setting in pyproject.toml to allow Fedora builds. + * #934: Update the logic that probes for the borgmatic streaming database dump, bootstrap + metadata, and check state directories to support more platforms and use cases. + * #934: Add the "RuntimeDirectory" and "StateDirectory" options to the sample systemd service + file to support the new runtime and state directory logic. 1.9.1 * #928: Fix the user runtime directory location on macOS (and possibly Cygwin). diff --git a/borgmatic/actions/check.py b/borgmatic/actions/check.py index d5f7fbe2..48cd0e88 100644 --- a/borgmatic/actions/check.py +++ b/borgmatic/actions/check.py @@ -403,16 +403,22 @@ BORG_DIRECTORY_FILE_TYPE = 'd' def collect_spot_check_archive_paths( - repository, archive, config, local_borg_version, global_arguments, local_path, remote_path + repository, + archive, + config, + local_borg_version, + global_arguments, + local_path, + remote_path, + borgmatic_runtime_directory, ): ''' Given a repository configuration dict, the name of the latest archive, a configuration dict, the - local Borg version, global arguments as an argparse.Namespace instance, the local Borg path, and - the remote Borg path, collect the paths from the given archive (but only include files and - symlinks and exclude borgmatic runtime directories). + local Borg version, global arguments as an argparse.Namespace instance, the local Borg path, the + remote Borg path, and the borgmatic runtime directory, collect the paths from the given archive + (but only include files and symlinks and exclude borgmatic runtime directories). ''' borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config) - borgmatic_runtime_directory = borgmatic.config.paths.get_borgmatic_runtime_directory(config) return tuple( path @@ -546,11 +552,12 @@ def spot_check( global_arguments, local_path, remote_path, + borgmatic_runtime_directory, ): ''' Given a repository dict, a loaded configuration dict, the local Borg version, global arguments - as an argparse.Namespace instance, the local Borg path, and the remote Borg path, perform a spot - check for the latest archive in the given repository. + as an argparse.Namespace instance, the local Borg path, the remote Borg path, and the borgmatic + runtime directory, perform a spot check for the latest archive in the given repository. A spot check compares file counts and also the hashes for a random sampling of source files on disk to those stored in the latest archive. If any differences are beyond configured tolerances, @@ -600,6 +607,7 @@ def spot_check( global_arguments, local_path, remote_path, + borgmatic_runtime_directory, ) logger.debug(f'{log_label}: {len(archive_paths)} total archive paths for spot check') @@ -730,14 +738,16 @@ def run_check( write_check_time(make_check_time_path(config, repository_id, 'extract')) if 'spot' in checks: - spot_check( - repository, - config, - local_borg_version, - global_arguments, - local_path, - remote_path, - ) + with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory: + spot_check( + repository, + config, + local_borg_version, + global_arguments, + local_path, + remote_path, + borgmatic_runtime_directory, + ) write_check_time(make_check_time_path(config, repository_id, 'spot')) borgmatic.hooks.command.execute_hook( diff --git a/borgmatic/actions/config/bootstrap.py b/borgmatic/actions/config/bootstrap.py index bd1c6bde..87095bcb 100644 --- a/borgmatic/actions/config/bootstrap.py +++ b/borgmatic/actions/config/bootstrap.py @@ -38,37 +38,41 @@ def get_config_paths(archive_name, bootstrap_arguments, global_arguments, local_ borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory( {'borgmatic_source_directory': bootstrap_arguments.borgmatic_source_directory} ) - borgmatic_runtime_directory = borgmatic.config.paths.get_borgmatic_runtime_directory( - {'user_runtime_directory': bootstrap_arguments.user_runtime_directory} - ) config = make_bootstrap_config(bootstrap_arguments) # Probe for the manifest file in multiple locations, as the default location has moved to the # borgmatic runtime directory (which get stored as just "/borgmatic" with Borg 1.4+). But we # still want to support reading the manifest from previously created archives as well. - for base_directory in ('borgmatic', borgmatic_runtime_directory, borgmatic_source_directory): - borgmatic_manifest_path = os.path.join(base_directory, 'bootstrap', 'manifest.json') + with borgmatic.config.paths.Runtime_directory( + {'user_runtime_directory': bootstrap_arguments.user_runtime_directory} + ) as borgmatic_runtime_directory: + for base_directory in ( + 'borgmatic', + borgmatic_runtime_directory, + borgmatic_source_directory, + ): + borgmatic_manifest_path = os.path.join(base_directory, 'bootstrap', 'manifest.json') - extract_process = borgmatic.borg.extract.extract_archive( - global_arguments.dry_run, - bootstrap_arguments.repository, - archive_name, - [borgmatic_manifest_path], - config, - local_borg_version, - global_arguments, - local_path=bootstrap_arguments.local_path, - remote_path=bootstrap_arguments.remote_path, - extract_to_stdout=True, - ) - manifest_json = extract_process.stdout.read() + extract_process = borgmatic.borg.extract.extract_archive( + global_arguments.dry_run, + bootstrap_arguments.repository, + archive_name, + [borgmatic_manifest_path], + config, + local_borg_version, + global_arguments, + local_path=bootstrap_arguments.local_path, + remote_path=bootstrap_arguments.remote_path, + extract_to_stdout=True, + ) + manifest_json = extract_process.stdout.read() - if manifest_json: - break - else: - raise ValueError( - 'Cannot read configuration paths from archive due to missing bootstrap manifest' - ) + if manifest_json: + break + else: + raise ValueError( + 'Cannot read configuration paths from archive due to missing bootstrap manifest' + ) try: manifest_data = json.loads(manifest_json) diff --git a/borgmatic/actions/create.py b/borgmatic/actions/create.py index aa8d6bc6..51168650 100644 --- a/borgmatic/actions/create.py +++ b/borgmatic/actions/create.py @@ -14,15 +14,15 @@ import borgmatic.hooks.dump logger = logging.getLogger(__name__) -def create_borgmatic_manifest(config, config_paths, dry_run): +def create_borgmatic_manifest(config, config_paths, borgmatic_runtime_directory, dry_run): ''' - Create a borgmatic manifest file to store the paths to the configuration files used to create - the archive. + Given a configuration dict, a sequence of config file paths, the borgmatic runtime directory, + and whether this is a dry run, create a borgmatic manifest file to store the paths to the + configuration files used to create the archive. ''' if dry_run: return - borgmatic_runtime_directory = borgmatic.config.paths.get_borgmatic_runtime_directory(config) borgmatic_manifest_path = os.path.join( borgmatic_runtime_directory, 'bootstrap', 'manifest.json' ) @@ -72,53 +72,63 @@ def run_create( **hook_context, ) logger.info(f'{repository.get("label", repository["path"])}: Creating archive{dry_run_label}') - borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured( - 'remove_data_source_dumps', - config, - repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, - global_arguments.dry_run, - ) - active_dumps = borgmatic.hooks.dispatch.call_hooks( - 'dump_data_sources', - config, - repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, - global_arguments.dry_run, - ) - if config.get('store_config_files', True): - create_borgmatic_manifest( + + with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory: + borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured( + 'remove_data_source_dumps', config, - config_paths, + repository['path'], + borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic_runtime_directory, global_arguments.dry_run, ) - stream_processes = [process for processes in active_dumps.values() for process in processes] + active_dumps = borgmatic.hooks.dispatch.call_hooks( + 'dump_data_sources', + config, + repository['path'], + borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic_runtime_directory, + global_arguments.dry_run, + ) + stream_processes = [process for processes in active_dumps.values() for process in processes] - json_output = borgmatic.borg.create.create_archive( - global_arguments.dry_run, - repository['path'], - config, - config_paths, - local_borg_version, - global_arguments, - local_path=local_path, - remote_path=remote_path, - progress=create_arguments.progress, - stats=create_arguments.stats, - json=create_arguments.json, - list_files=create_arguments.list_files, - stream_processes=stream_processes, - ) - if json_output: - yield borgmatic.actions.json.parse_json(json_output, repository.get('label')) + if config.get('store_config_files', True): + create_borgmatic_manifest( + config, + config_paths, + borgmatic_runtime_directory, + global_arguments.dry_run, + ) + + json_output = borgmatic.borg.create.create_archive( + global_arguments.dry_run, + repository['path'], + config, + config_paths, + local_borg_version, + global_arguments, + borgmatic_runtime_directory, + local_path=local_path, + remote_path=remote_path, + progress=create_arguments.progress, + stats=create_arguments.stats, + json=create_arguments.json, + list_files=create_arguments.list_files, + stream_processes=stream_processes, + ) + + if json_output: + yield borgmatic.actions.json.parse_json(json_output, repository.get('label')) + + borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured( + 'remove_data_source_dumps', + config, + config_filename, + borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic_runtime_directory, + global_arguments.dry_run, + ) - borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured( - 'remove_data_source_dumps', - config, - config_filename, - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, - global_arguments.dry_run, - ) borgmatic.hooks.command.execute_hook( config.get('after_backup'), config.get('umask'), diff --git a/borgmatic/actions/restore.py b/borgmatic/actions/restore.py index 3441002c..0fc4e93f 100644 --- a/borgmatic/actions/restore.py +++ b/borgmatic/actions/restore.py @@ -108,6 +108,7 @@ def restore_single_data_source( hook_name, data_source, connection_params, + borgmatic_runtime_directory, ): ''' Given (among other things) an archive name, a data source hook name, the hostname, port, @@ -123,9 +124,9 @@ def restore_single_data_source( config, repository['path'], borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic_runtime_directory, data_source['name'], )[hook_name] - borgmatic_runtime_directory = borgmatic.config.paths.get_borgmatic_runtime_directory(config) destination_path = ( tempfile.mkdtemp(dir=borgmatic_runtime_directory) @@ -135,7 +136,7 @@ def restore_single_data_source( try: # Kick off a single data source extract. If using a directory format, extract to a temporary - # directory. Otheriwes extract the single dump file to stdout. + # directory. Otherwise extract the single dump file to stdout. extract_process = borgmatic.borg.extract.extract_archive( dry_run=global_arguments.dry_run, repository=repository['path'], @@ -181,17 +182,17 @@ def collect_archive_data_source_names( global_arguments, local_path, remote_path, + borgmatic_runtime_directory, ): ''' Given a local or remote repository path, a resolved archive name, a configuration dict, the - local Borg version, global_arguments an argparse.Namespace, and local and remote Borg paths, - query the archive for the names of data sources it contains as dumps and return them as a dict - from hook name to a sequence of data source names. + local Borg version, global_arguments an argparse.Namespace, local and remote Borg paths, and the + borgmatic runtime directory, query the archive for the names of data sources it contains as + dumps and return them as a dict from hook name to a sequence of data source names. ''' borgmatic_source_directory = str( pathlib.Path(borgmatic.config.paths.get_borgmatic_source_directory(config)) ) - borgmatic_runtime_directory = borgmatic.config.paths.get_borgmatic_runtime_directory(config) # Probe for the data source dumps in multiple locations, as the default location has moved to # the borgmatic runtime directory (which get stored as just "/borgmatic" with Borg 1.4+). But we @@ -330,6 +331,7 @@ def run_restore( global_arguments, local_path, remote_path, + borgmatic_runtime_directory, ): ''' Run the "restore" action for the given repository, but only if the repository matches the @@ -346,105 +348,110 @@ def run_restore( f'{repository.get("label", repository["path"])}: Restoring data sources from archive {restore_arguments.archive}' ) - borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured( - 'remove_data_source_dumps', - config, - repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, - global_arguments.dry_run, - ) + with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory: + borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured( + 'remove_data_source_dumps', + config, + repository['path'], + borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic_runtime_directory, + global_arguments.dry_run, + ) - archive_name = borgmatic.borg.repo_list.resolve_archive_name( - repository['path'], - restore_arguments.archive, - config, - local_borg_version, - global_arguments, - local_path, - remote_path, - ) - archive_data_source_names = collect_archive_data_source_names( - repository['path'], - archive_name, - config, - local_borg_version, - global_arguments, - local_path, - remote_path, - ) - restore_names = find_data_sources_to_restore( - restore_arguments.data_sources, archive_data_source_names - ) - found_names = set() - remaining_restore_names = {} - connection_params = { - 'hostname': restore_arguments.hostname, - 'port': restore_arguments.port, - 'username': restore_arguments.username, - 'password': restore_arguments.password, - 'restore_path': restore_arguments.restore_path, - } + archive_name = borgmatic.borg.repo_list.resolve_archive_name( + repository['path'], + restore_arguments.archive, + config, + local_borg_version, + global_arguments, + local_path, + remote_path, + ) + archive_data_source_names = collect_archive_data_source_names( + repository['path'], + archive_name, + config, + local_borg_version, + global_arguments, + local_path, + remote_path, + ) + restore_names = find_data_sources_to_restore( + restore_arguments.data_sources, archive_data_source_names + ) + found_names = set() + remaining_restore_names = {} + connection_params = { + 'hostname': restore_arguments.hostname, + 'port': restore_arguments.port, + 'username': restore_arguments.username, + 'password': restore_arguments.password, + 'restore_path': restore_arguments.restore_path, + } - for hook_name, data_source_names in restore_names.items(): - for data_source_name in data_source_names: - found_hook_name, found_data_source = get_configured_data_source( - config, archive_data_source_names, hook_name, data_source_name - ) - - if not found_data_source: - remaining_restore_names.setdefault(found_hook_name or hook_name, []).append( - data_source_name + for hook_name, data_source_names in restore_names.items(): + for data_source_name in data_source_names: + found_hook_name, found_data_source = get_configured_data_source( + config, archive_data_source_names, hook_name, data_source_name ) - continue - found_names.add(data_source_name) - restore_single_data_source( - repository, - config, - local_borg_version, - global_arguments, - local_path, - remote_path, - archive_name, - found_hook_name or hook_name, - dict(found_data_source, **{'schemas': restore_arguments.schemas}), - connection_params, - ) + if not found_data_source: + remaining_restore_names.setdefault(found_hook_name or hook_name, []).append( + data_source_name + ) + continue - # For any data sources that weren't found via exact matches in the configuration, try to - # fallback to "all" entries. - for hook_name, data_source_names in remaining_restore_names.items(): - for data_source_name in data_source_names: - found_hook_name, found_data_source = get_configured_data_source( - config, archive_data_source_names, hook_name, data_source_name, 'all' - ) + found_names.add(data_source_name) + restore_single_data_source( + repository, + config, + local_borg_version, + global_arguments, + local_path, + remote_path, + archive_name, + found_hook_name or hook_name, + dict(found_data_source, **{'schemas': restore_arguments.schemas}), + connection_params, + borgmatic_runtime_directory, + ) - if not found_data_source: - continue + # For any data sources that weren't found via exact matches in the configuration, try to + # fallback to "all" entries. + for hook_name, data_source_names in remaining_restore_names.items(): + for data_source_name in data_source_names: + found_hook_name, found_data_source = get_configured_data_source( + config, archive_data_source_names, hook_name, data_source_name, 'all' + ) - found_names.add(data_source_name) - data_source = copy.copy(found_data_source) - data_source['name'] = data_source_name + if not found_data_source: + continue - restore_single_data_source( - repository, - config, - local_borg_version, - global_arguments, - local_path, - remote_path, - archive_name, - found_hook_name or hook_name, - dict(data_source, **{'schemas': restore_arguments.schemas}), - connection_params, - ) + found_names.add(data_source_name) + data_source = copy.copy(found_data_source) + data_source['name'] = data_source_name - borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured( - 'remove_data_source_dumps', - config, - repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, - global_arguments.dry_run, - ) + restore_single_data_source( + repository, + config, + local_borg_version, + global_arguments, + local_path, + remote_path, + archive_name, + found_hook_name or hook_name, + dict(data_source, **{'schemas': restore_arguments.schemas}), + connection_params, + borgmatic_runtime_directory, + ) + + borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured( + 'remove_data_source_dumps', + config, + repository['path'], + borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic_runtime_directory, + global_arguments.dry_run, + ) ensure_data_sources_found(restore_names, remaining_restore_names, found_names) diff --git a/borgmatic/borg/create.py b/borgmatic/borg/create.py index c34f7dfd..9eacf4d2 100644 --- a/borgmatic/borg/create.py +++ b/borgmatic/borg/create.py @@ -504,6 +504,7 @@ def create_archive( config_paths, local_borg_version, global_arguments, + borgmatic_runtime_directory, local_path='borg', remote_path=None, progress=False, @@ -524,9 +525,7 @@ def create_archive( working_directory = borgmatic.config.paths.get_working_directory(config) borgmatic_runtime_directories = expand_directories( - collect_borgmatic_runtime_directories( - borgmatic.config.paths.get_borgmatic_runtime_directory(config) - ), + collect_borgmatic_runtime_directories(borgmatic_runtime_directory), working_directory=working_directory, ) diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 1952703a..83dbbfd1 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -275,8 +275,8 @@ def run_actions( ''' Given parsed command-line arguments as an argparse.ArgumentParser instance, the configuration filename, a configuration dict, a sequence of loaded configuration paths, local and remote paths - to Borg, a local Borg version string, and a repository name, run all actions from the - command-line arguments on the given repository. + to Borg, a local Borg version string, a repository name, and the borgmatic runtime directory, + run all actions from the command-line arguments on the given repository. Yield JSON output strings from executing any actions that produce JSON. diff --git a/borgmatic/config/paths.py b/borgmatic/config/paths.py index ad318d6d..144222f1 100644 --- a/borgmatic/config/paths.py +++ b/borgmatic/config/paths.py @@ -1,4 +1,8 @@ +import logging import os +import tempfile + +logger = logging.getLogger(__name__) def expand_user_in_path(path): @@ -26,28 +30,70 @@ def get_borgmatic_source_directory(config): return expand_user_in_path(config.get('borgmatic_source_directory') or '~/.borgmatic') -def get_borgmatic_runtime_directory(config): +class Runtime_directory: ''' - Given a configuration dict, get the borgmatic runtime directory used for storing temporary - runtime data like streaming database dumps and bootstrap metadata. Defaults to - $XDG_RUNTIME_DIR/./borgmatic or $TMPDIR/./borgmatic or $TEMP/./borgmatic or - /run/user/$UID/./borgmatic. + A Python context manager for creating and cleaning up the borgmatic runtime directory used for + storing temporary runtime data like streaming database dumps and bootstrap metadata. - The "/./" is taking advantage of a Borg feature such that the part of the path before the "/./" - does not get stored in the file path within an archive. That way, the path of the runtime - directory can change without leaving database dumps within an archive inaccessible. + Example use as a context manager: + + with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory: + do_something_with(borgmatic_runtime_directory) + + For the scope of that "with" statement, the runtime directory is available. Afterwards, it + automatically gets cleaned up as necessary. ''' - return expand_user_in_path( - os.path.join( + + def __init__(self, config): + ''' + Given a configuration dict, determine the borgmatic runtime directory, creating a secure, + temporary directory within it if necessary. Defaults to $XDG_RUNTIME_DIR/./borgmatic or + $RUNTIME_DIRECTORY/./borgmatic or $TMPDIR/borgmatic-[random]/./borgmatic or + $TEMP/borgmatic-[random]/./borgmatic or /tmp/borgmatic-[random]/./borgmatic where "[random]" + is a randomly generated string intended to avoid path collisions. + + If XDG_RUNTIME_DIR or RUNTIME_DIRECTORY is set and already ends in "/borgmatic", then don't + tack on a second "/borgmatic" path component. + + The "/./" is taking advantage of a Borg feature such that the part of the path before the "/./" + does not get stored in the file path within an archive. That way, the path of the runtime + directory can change without leaving database dumps within an archive inaccessible. + ''' + + runtime_directory = ( config.get('user_runtime_directory') - or os.environ.get('XDG_RUNTIME_DIR') - or os.environ.get('TMPDIR') - or os.environ.get('TEMP') - or f'/run/user/{os.getuid()}', - '.', - 'borgmatic', + or os.environ.get('XDG_RUNTIME_DIR') # Set by PAM on Linux. + or os.environ.get('RUNTIME_DIRECTORY') # Set by systemd if configured. ) - ) + + if runtime_directory: + self.temporary_directory = None + else: + self.temporary_directory = tempfile.TemporaryDirectory( + prefix='borgmatic', dir=os.environ.get('TMPDIR') or os.environ.get('TEMP') or '/tmp' + ) + runtime_directory = self.temporary_directory.name + + (base_path, final_directory) = os.path.split(runtime_directory.rstrip(os.path.sep)) + + self.runtime_path = expand_user_in_path( + os.path.join( + base_path if final_directory == 'borgmatic' else runtime_directory, '.', 'borgmatic' + ) + ) + + def __enter__(self): + ''' + Return the borgmatic runtime path as a string. + ''' + return self.runtime_path + + def __exit__(self, exception, value, traceback): + ''' + Delete any temporary directory that was created as part of initialization. + ''' + if self.temporary_directory: + self.temporary_directory.cleanup() def get_borgmatic_state_directory(config): @@ -59,10 +105,9 @@ def get_borgmatic_state_directory(config): return expand_user_in_path( os.path.join( config.get('user_state_directory') - or os.environ.get( - 'XDG_STATE_HOME', - '~/.local/state', - ), + or os.environ.get('XDG_STATE_HOME') + or os.environ.get('STATE_DIRECTORY') # Set by systemd if configured. + or '~/.local/state', 'borgmatic', ) ) diff --git a/borgmatic/hooks/mariadb.py b/borgmatic/hooks/mariadb.py index dcbd9d4b..f15d0cd1 100644 --- a/borgmatic/hooks/mariadb.py +++ b/borgmatic/hooks/mariadb.py @@ -14,15 +14,11 @@ from borgmatic.hooks import dump logger = logging.getLogger(__name__) -def make_dump_path(config, base_directory=None): # pragma: no cover +def make_dump_path(base_directory): # pragma: no cover ''' - Given a configuration dict and an optional base directory, make the corresponding dump path. If - a base directory isn't provided, use the borgmatic runtime directory. + Given a base directory, make the corresponding dump path. ''' - return dump.make_data_source_dump_path( - base_directory or borgmatic.config.paths.get_borgmatic_runtime_directory(config), - 'mariadb_databases', - ) + return dump.make_data_source_dump_path(base_directory, 'mariadb_databases') SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys') @@ -126,12 +122,12 @@ def use_streaming(databases, config, log_prefix): return any(databases) -def dump_data_sources(databases, config, log_prefix, dry_run): +def dump_data_sources(databases, config, log_prefix, borgmatic_runtime_directory, dry_run): ''' Dump the given MariaDB databases to a named pipe. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the given - configuration dict to construct the destination path and the given log prefix in any log - entries. + borgmatic runtime directory to construct the destination path and the given log prefix in any + log entries. Return a sequence of subprocess.Popen instances for the dump processes ready to spew to a named pipe. But if this is a dry run, then don't actually dump anything and return an empty sequence. @@ -142,7 +138,7 @@ def dump_data_sources(databases, config, log_prefix, dry_run): logger.info(f'{log_prefix}: Dumping MariaDB databases{dry_run_label}') for database in databases: - dump_path = make_dump_path(config) + dump_path = make_dump_path(borgmatic_runtime_directory) extra_environment = {'MYSQL_PWD': database['password']} if 'password' in database else None dump_database_names = database_names_to_dump( database, extra_environment, log_prefix, dry_run @@ -185,30 +181,36 @@ def dump_data_sources(databases, config, log_prefix, dry_run): return [process for process in processes if process] -def remove_data_source_dumps(databases, config, log_prefix, dry_run): # pragma: no cover +def remove_data_source_dumps( + databases, config, log_prefix, borgmatic_runtime_directory, dry_run +): # pragma: no cover ''' - Remove all database dump files for this hook regardless of the given databases. Use the given - configuration dict to construct the destination path and the log prefix in any log entries. If - this is a dry run, then don't actually remove anything. + Remove all database dump files for this hook regardless of the given databases. Use the + borgmatic_runtime_directory to construct the destination path and the log prefix in any log + entries. If this is a dry run, then don't actually remove anything. ''' - dump.remove_data_source_dumps(make_dump_path(config), 'MariaDB', log_prefix, dry_run) + dump.remove_data_source_dumps( + make_dump_path(borgmatic_runtime_directory), 'MariaDB', log_prefix, dry_run + ) -def make_data_source_dump_patterns(databases, config, log_prefix, name=None): # pragma: no cover +def make_data_source_dump_patterns( + databases, config, log_prefix, borgmatic_runtime_directory, name=None +): # pragma: no cover ''' - Given a sequence of configurations dicts, a configuration dict, a prefix to log with, and a - database name to match, return the corresponding glob patterns to match the database dump in an - archive. + Given a sequence of configurations dicts, a configuration dict, a prefix to log with, the + borgmatic runtime directory, and a database name to match, return the corresponding glob + patterns to match the database dump in an archive. ''' borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config) return ( + dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, 'borgmatic'), name, hostname='*' + make_dump_path(borgmatic_runtime_directory), name, hostname='*' ), - dump.make_data_source_dump_filename(make_dump_path(config), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, borgmatic_source_directory), name, hostname='*' + make_dump_path(borgmatic_source_directory), name, hostname='*' ), ) diff --git a/borgmatic/hooks/mongodb.py b/borgmatic/hooks/mongodb.py index 498b0638..7ad0259f 100644 --- a/borgmatic/hooks/mongodb.py +++ b/borgmatic/hooks/mongodb.py @@ -8,15 +8,11 @@ from borgmatic.hooks import dump logger = logging.getLogger(__name__) -def make_dump_path(config, base_directory=None): # pragma: no cover +def make_dump_path(base_directory): # pragma: no cover ''' - Given a configuration dict and an optional base directory, make the corresponding dump path. If - a base directory isn't provided, use the borgmatic runtime directory. + Given a base directory, make the corresponding dump path. ''' - return dump.make_data_source_dump_path( - base_directory or borgmatic.config.paths.get_borgmatic_runtime_directory(config), - 'mongodb_databases', - ) + return dump.make_data_source_dump_path(base_directory, 'mongodb_databases') def use_streaming(databases, config, log_prefix): @@ -27,11 +23,11 @@ def use_streaming(databases, config, log_prefix): return any(database.get('format') != 'directory' for database in databases) -def dump_data_sources(databases, config, log_prefix, dry_run): +def dump_data_sources(databases, config, log_prefix, borgmatic_runtime_directory, dry_run): ''' Dump the given MongoDB databases to a named pipe. The databases are supplied as a sequence of - dicts, one dict describing each database as per the configuration schema. Use the configuration - dict to construct the destination path and the given log prefix in any log entries. + dicts, one dict describing each database as per the configuration schema. Use the borgmatic + runtime directory to construct the destination path and the given log prefix in any log entries. Return a sequence of subprocess.Popen instances for the dump processes ready to spew to a named pipe. But if this is a dry run, then don't actually dump anything and return an empty sequence. @@ -44,7 +40,7 @@ def dump_data_sources(databases, config, log_prefix, dry_run): for database in databases: name = database['name'] dump_filename = dump.make_data_source_dump_filename( - make_dump_path(config), name, database.get('hostname') + make_dump_path(borgmatic_runtime_directory), name, database.get('hostname') ) dump_format = database.get('format', 'archive') @@ -94,30 +90,36 @@ def build_dump_command(database, dump_filename, dump_format): ) -def remove_data_source_dumps(databases, config, log_prefix, dry_run): # pragma: no cover +def remove_data_source_dumps( + databases, config, log_prefix, borgmatic_runtime_directory, dry_run +): # pragma: no cover ''' - Remove all database dump files for this hook regardless of the given databases. Use the log - prefix in any log entries. Use the given configuration dict to construct the destination path. - If this is a dry run, then don't actually remove anything. + Remove all database dump files for this hook regardless of the given databases. Use the + borgmatic_runtime_directory to construct the destination path and the log prefix in any log + entries. If this is a dry run, then don't actually remove anything. ''' - dump.remove_data_source_dumps(make_dump_path(config), 'MongoDB', log_prefix, dry_run) + dump.remove_data_source_dumps( + make_dump_path(borgmatic_runtime_directory), 'MongoDB', log_prefix, dry_run + ) -def make_data_source_dump_patterns(databases, config, log_prefix, name=None): # pragma: no cover +def make_data_source_dump_patterns( + databases, config, log_prefix, borgmatic_runtime_directory, name=None +): # pragma: no cover ''' - Given a sequence of database configurations dicts, a configuration dict, a prefix to log with, - and a database name to match, return the corresponding glob patterns to match the database dump - in an archive. + Given a sequence of configurations dicts, a configuration dict, a prefix to log with, the + borgmatic runtime directory, and a database name to match, return the corresponding glob + patterns to match the database dump in an archive. ''' borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config) return ( + dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, 'borgmatic'), name, hostname='*' + make_dump_path(borgmatic_runtime_directory), name, hostname='*' ), - dump.make_data_source_dump_filename(make_dump_path(config), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, borgmatic_source_directory), name, hostname='*' + make_dump_path(borgmatic_source_directory), name, hostname='*' ), ) diff --git a/borgmatic/hooks/mysql.py b/borgmatic/hooks/mysql.py index fb72da0f..83b8c6e6 100644 --- a/borgmatic/hooks/mysql.py +++ b/borgmatic/hooks/mysql.py @@ -14,15 +14,11 @@ from borgmatic.hooks import dump logger = logging.getLogger(__name__) -def make_dump_path(config, base_directory=None): # pragma: no cover +def make_dump_path(base_directory): # pragma: no cover ''' - Given a configuration dict and an optional base directory, make the corresponding dump path. If - a base directory isn't provided, use the borgmatic runtime directory. + Given a base directory, make the corresponding dump path. ''' - return dump.make_data_source_dump_path( - base_directory or borgmatic.config.paths.get_borgmatic_runtime_directory(config), - 'mysql_databases', - ) + return dump.make_data_source_dump_path(base_directory, 'mysql_databases') SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys') @@ -125,11 +121,12 @@ def use_streaming(databases, config, log_prefix): return any(databases) -def dump_data_sources(databases, config, log_prefix, dry_run): +def dump_data_sources(databases, config, log_prefix, borgmatic_runtime_directory, dry_run): ''' Dump the given MySQL/MariaDB databases to a named pipe. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the given - configuration dict to construct the destination path and the given log prefix in any log entries. + borgmatic runtime directory to construct the destination path and the given log prefix in any + log entries. Return a sequence of subprocess.Popen instances for the dump processes ready to spew to a named pipe. But if this is a dry run, then don't actually dump anything and return an empty sequence. @@ -140,7 +137,7 @@ def dump_data_sources(databases, config, log_prefix, dry_run): logger.info(f'{log_prefix}: Dumping MySQL databases{dry_run_label}') for database in databases: - dump_path = make_dump_path(config) + dump_path = make_dump_path(borgmatic_runtime_directory) extra_environment = {'MYSQL_PWD': database['password']} if 'password' in database else None dump_database_names = database_names_to_dump( database, extra_environment, log_prefix, dry_run @@ -183,30 +180,36 @@ def dump_data_sources(databases, config, log_prefix, dry_run): return [process for process in processes if process] -def remove_data_source_dumps(databases, config, log_prefix, dry_run): # pragma: no cover +def remove_data_source_dumps( + databases, config, log_prefix, borgmatic_runtime_directory, dry_run +): # pragma: no cover ''' - Remove all database dump files for this hook regardless of the given databases. Use the given - configuration dict to construct the destination path and the log prefix in any log entries. If - this is a dry run, then don't actually remove anything. + Remove all database dump files for this hook regardless of the given databases. Use the + borgmatic runtime directory to construct the destination path and the log prefix in any log + entries. If this is a dry run, then don't actually remove anything. ''' - dump.remove_data_source_dumps(make_dump_path(config), 'MySQL', log_prefix, dry_run) + dump.remove_data_source_dumps( + make_dump_path(borgmatic_runtime_directory), 'MySQL', log_prefix, dry_run + ) -def make_data_source_dump_patterns(databases, config, log_prefix, name=None): # pragma: no cover +def make_data_source_dump_patterns( + databases, config, log_prefix, borgmatic_runtime_directory, name=None +): # pragma: no cover ''' - Given a sequence of configurations dicts, a configuration dict, a prefix to log with, and a - database name to match, return the corresponding glob patterns to match the database dump in an - archive. + Given a sequence of configurations dicts, a configuration dict, a prefix to log with, the + borgmatic runtime directory, and a database name to match, return the corresponding glob + patterns to match the database dump in an archive. ''' borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config) return ( + dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, 'borgmatic'), name, hostname='*' + make_dump_path(borgmatic_runtime_directory), name, hostname='*' ), - dump.make_data_source_dump_filename(make_dump_path(config), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, borgmatic_source_directory), name, hostname='*' + make_dump_path(borgmatic_source_directory), name, hostname='*' ), ) diff --git a/borgmatic/hooks/postgresql.py b/borgmatic/hooks/postgresql.py index 1b510148..8fda4baf 100644 --- a/borgmatic/hooks/postgresql.py +++ b/borgmatic/hooks/postgresql.py @@ -16,15 +16,11 @@ from borgmatic.hooks import dump logger = logging.getLogger(__name__) -def make_dump_path(config, base_directory=None): # pragma: no cover +def make_dump_path(base_directory): # pragma: no cover ''' - Given a configuration dict and an optional base directory, make the corresponding dump path. If - a base directory isn't provided, use the borgmatic runtime directory. + Given a base directory, make the corresponding dump path. ''' - return dump.make_data_source_dump_path( - base_directory or borgmatic.config.paths.get_borgmatic_runtime_directory(config), - 'postgresql_databases', - ) + return dump.make_data_source_dump_path(base_directory, 'postgresql_databases') def make_extra_environment(database, restore_connection_params=None): @@ -108,12 +104,12 @@ def use_streaming(databases, config, log_prefix): return any(database.get('format') != 'directory' for database in databases) -def dump_data_sources(databases, config, log_prefix, dry_run): +def dump_data_sources(databases, config, log_prefix, borgmatic_runtime_directory, dry_run): ''' Dump the given PostgreSQL databases to a named pipe. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the given - configuration dict to construct the destination path and the given log prefix in any log - entries. + borgmatic runtime directory to construct the destination path and the given log prefix in any + log entries. Return a sequence of subprocess.Popen instances for the dump processes ready to spew to a named pipe. But if this is a dry run, then don't actually dump anything and return an empty sequence. @@ -127,7 +123,7 @@ def dump_data_sources(databases, config, log_prefix, dry_run): for database in databases: extra_environment = make_extra_environment(database) - dump_path = make_dump_path(config) + dump_path = make_dump_path(borgmatic_runtime_directory) dump_database_names = database_names_to_dump( database, extra_environment, log_prefix, dry_run ) @@ -210,30 +206,36 @@ def dump_data_sources(databases, config, log_prefix, dry_run): return processes -def remove_data_source_dumps(databases, config, log_prefix, dry_run): # pragma: no cover +def remove_data_source_dumps( + databases, config, log_prefix, borgmatic_runtime_directory, dry_run +): # pragma: no cover ''' - Remove all database dump files for this hook regardless of the given databases. Use the given - configuration dict to construct the destination path and the log prefix in any log entries. If - this is a dry run, then don't actually remove anything. + Remove all database dump files for this hook regardless of the given databases. Use the + borgmatic runtime directory to construct the destination path and the log prefix in any log + entries. If this is a dry run, then don't actually remove anything. ''' - dump.remove_data_source_dumps(make_dump_path(config), 'PostgreSQL', log_prefix, dry_run) + dump.remove_data_source_dumps( + make_dump_path(borgmatic_runtime_directory), 'PostgreSQL', log_prefix, dry_run + ) -def make_data_source_dump_patterns(databases, config, log_prefix, name=None): # pragma: no cover +def make_data_source_dump_patterns( + databases, config, log_prefix, borgmatic_runtime_directory, name=None +): # pragma: no cover ''' - Given a sequence of configurations dicts, a configuration dict, a prefix to log with, and a - database name to match, return the corresponding glob patterns to match the database dump in an - archive. + Given a sequence of configurations dicts, a configuration dict, a prefix to log with, the + borgmatic runtime directory, and a database name to match, return the corresponding glob + patterns to match the database dump in an archive. ''' borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config) return ( + dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, 'borgmatic'), name, hostname='*' + make_dump_path(borgmatic_runtime_directory), name, hostname='*' ), - dump.make_data_source_dump_filename(make_dump_path(config), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, borgmatic_source_directory), name, hostname='*' + make_dump_path(borgmatic_source_directory), name, hostname='*' ), ) diff --git a/borgmatic/hooks/sqlite.py b/borgmatic/hooks/sqlite.py index 778a6226..a7acb56f 100644 --- a/borgmatic/hooks/sqlite.py +++ b/borgmatic/hooks/sqlite.py @@ -9,15 +9,11 @@ from borgmatic.hooks import dump logger = logging.getLogger(__name__) -def make_dump_path(config, base_directory=None): # pragma: no cover +def make_dump_path(base_directory): # pragma: no cover ''' - Given a configuration dict and an optional base directory, make the corresponding dump path. If - a base directory isn't provided, use the borgmatic runtime directory. + Given a base directory, make the corresponding dump path. ''' - return dump.make_data_source_dump_path( - base_directory or borgmatic.config.paths.get_borgmatic_runtime_directory(config), - 'sqlite_databases', - ) + return dump.make_data_source_dump_path(base_directory, 'sqlite_databases') def use_streaming(databases, config, log_prefix): @@ -28,11 +24,11 @@ def use_streaming(databases, config, log_prefix): return any(databases) -def dump_data_sources(databases, config, log_prefix, dry_run): +def dump_data_sources(databases, config, log_prefix, borgmatic_runtime_directory, dry_run): ''' Dump the given SQLite databases to a named pipe. The databases are supplied as a sequence of - configuration dicts, as per the configuration schema. Use the given configuration dict to - construct the destination path and the given log prefix in any log entries. + configuration dicts, as per the configuration schema. Use the given borgmatic runtime directory + to construct the destination path and the given log prefix in any log entries. Return a sequence of subprocess.Popen instances for the dump processes ready to spew to a named pipe. But if this is a dry run, then don't actually dump anything and return an empty sequence. @@ -52,7 +48,7 @@ def dump_data_sources(databases, config, log_prefix, dry_run): f'{log_prefix}: No SQLite database at {database_path}; an empty database will be created and dumped' ) - dump_path = make_dump_path(config) + dump_path = make_dump_path(borgmatic_runtime_directory) dump_filename = dump.make_data_source_dump_filename(dump_path, database['name']) if os.path.exists(dump_filename): @@ -80,30 +76,36 @@ def dump_data_sources(databases, config, log_prefix, dry_run): return processes -def remove_data_source_dumps(databases, config, log_prefix, dry_run): # pragma: no cover +def remove_data_source_dumps( + databases, config, log_prefix, borgmatic_runtime_directory, dry_run +): # pragma: no cover ''' - Remove the given SQLite database dumps from the filesystem. The databases are supplied as a - sequence of configuration dicts, as per the configuration schema. Use the given configuration - dict to construct the destination path and the given log prefix in any log entries. If this is a - dry run, then don't actually remove anything. + Remove all database dump files for this hook regardless of the given databases. Use the + borgmatic runtime directory to construct the destination path and the log prefix in any log + entries. If this is a dry run, then don't actually remove anything. ''' - dump.remove_data_source_dumps(make_dump_path(config), 'SQLite', log_prefix, dry_run) + dump.remove_data_source_dumps( + make_dump_path(borgmatic_runtime_directory), 'SQLite', log_prefix, dry_run + ) -def make_data_source_dump_patterns(databases, config, log_prefix, name=None): # pragma: no cover +def make_data_source_dump_patterns( + databases, config, log_prefix, borgmatic_runtime_directory, name=None +): # pragma: no cover ''' - Make a pattern that matches the given SQLite databases. The databases are supplied as a sequence - of configuration dicts, as per the configuration schema. + Given a sequence of configurations dicts, a configuration dict, a prefix to log with, the + borgmatic runtime directory, and a database name to match, return the corresponding glob + patterns to match the database dump in an archive. ''' borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config) return ( + dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, 'borgmatic'), name, hostname='*' + make_dump_path(borgmatic_runtime_directory), name, hostname='*' ), - dump.make_data_source_dump_filename(make_dump_path(config), name, hostname='*'), dump.make_data_source_dump_filename( - make_dump_path(config, borgmatic_source_directory), name, hostname='*' + make_dump_path(borgmatic_source_directory), name, hostname='*' ), ) diff --git a/sample/systemd/borgmatic.service b/sample/systemd/borgmatic.service index 7a96b834..564cbe99 100644 --- a/sample/systemd/borgmatic.service +++ b/sample/systemd/borgmatic.service @@ -9,6 +9,8 @@ Documentation=https://torsion.org/borgmatic/ [Service] Type=oneshot +RuntimeDirectory=borgmatic +StateDirectory=borgmatic # Load single encrypted credential. LoadCredentialEncrypted=borgmatic.pw diff --git a/tests/unit/actions/config/test_bootstrap.py b/tests/unit/actions/config/test_bootstrap.py index dceabe19..39e759f0 100644 --- a/tests/unit/actions/config/test_bootstrap.py +++ b/tests/unit/actions/config/test_bootstrap.py @@ -16,9 +16,6 @@ def test_get_config_paths_returns_list_of_config_paths(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/source') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/runtime') flexmock(module).should_receive('make_bootstrap_config').and_return({}) bootstrap_arguments = flexmock( repository='repo', @@ -33,6 +30,9 @@ def test_get_config_paths_returns_list_of_config_paths(): dry_run=False, ) local_borg_version = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) extract_process = flexmock( stdout=flexmock( read=lambda: '{"config_paths": ["/borgmatic/config.yaml"]}', @@ -47,13 +47,58 @@ def test_get_config_paths_returns_list_of_config_paths(): ) == ['/borgmatic/config.yaml'] +def test_get_config_paths_probes_for_manifest(): + flexmock(module.borgmatic.config.paths).should_receive( + 'get_borgmatic_source_directory' + ).and_return('/source') + flexmock(module).should_receive('make_bootstrap_config').and_return({}) + bootstrap_arguments = flexmock( + repository='repo', + archive='archive', + ssh_command=None, + local_path='borg7', + remote_path='borg8', + borgmatic_source_directory=None, + user_runtime_directory=None, + ) + global_arguments = flexmock( + dry_run=False, + ) + local_borg_version = flexmock() + borgmatic_runtime_directory = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + borgmatic_runtime_directory, + ) + flexmock(module.os.path).should_receive('join').with_args( + 'borgmatic', 'bootstrap', 'manifest.json' + ).and_return(flexmock()).once() + flexmock(module.os.path).should_receive('join').with_args( + borgmatic_runtime_directory, 'bootstrap', 'manifest.json' + ).and_return(flexmock()).once() + flexmock(module.os.path).should_receive('join').with_args( + '/source', 'bootstrap', 'manifest.json' + ).and_return(flexmock()).once() + manifest_missing_extract_process = flexmock( + stdout=flexmock(read=lambda: None), + ) + manifest_found_extract_process = flexmock( + stdout=flexmock( + read=lambda: '{"config_paths": ["/borgmatic/config.yaml"]}', + ), + ) + flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return( + manifest_missing_extract_process + ).and_return(manifest_missing_extract_process).and_return(manifest_found_extract_process) + + assert module.get_config_paths( + 'archive', bootstrap_arguments, global_arguments, local_borg_version + ) == ['/borgmatic/config.yaml'] + + def test_get_config_paths_translates_ssh_command_argument_to_config(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/source') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/runtime') config = flexmock() flexmock(module).should_receive('make_bootstrap_config').and_return(config) bootstrap_arguments = flexmock( @@ -69,6 +114,9 @@ def test_get_config_paths_translates_ssh_command_argument_to_config(): dry_run=False, ) local_borg_version = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) extract_process = flexmock( stdout=flexmock( read=lambda: '{"config_paths": ["/borgmatic/config.yaml"]}', @@ -96,9 +144,6 @@ def test_get_config_paths_with_missing_manifest_raises_value_error(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/source') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/runtime') flexmock(module).should_receive('make_bootstrap_config').and_return({}) bootstrap_arguments = flexmock( repository='repo', @@ -113,6 +158,10 @@ def test_get_config_paths_with_missing_manifest_raises_value_error(): dry_run=False, ) local_borg_version = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) + flexmock(module.os.path).should_receive('join').and_return(flexmock()) extract_process = flexmock(stdout=flexmock(read=lambda: '')) flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return( extract_process @@ -128,9 +177,6 @@ def test_get_config_paths_with_broken_json_raises_value_error(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/source') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/runtime') flexmock(module).should_receive('make_bootstrap_config').and_return({}) bootstrap_arguments = flexmock( repository='repo', @@ -145,6 +191,9 @@ def test_get_config_paths_with_broken_json_raises_value_error(): dry_run=False, ) local_borg_version = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) extract_process = flexmock( stdout=flexmock(read=lambda: '{"config_paths": ["/oops'), ) @@ -162,9 +211,6 @@ def test_get_config_paths_with_json_missing_key_raises_value_error(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/source') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/runtime') flexmock(module).should_receive('make_bootstrap_config').and_return({}) bootstrap_arguments = flexmock( repository='repo', @@ -179,6 +225,9 @@ def test_get_config_paths_with_json_missing_key_raises_value_error(): dry_run=False, ) local_borg_version = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) extract_process = flexmock( stdout=flexmock(read=lambda: '{}'), ) @@ -210,6 +259,9 @@ def test_run_bootstrap_does_not_raise(): dry_run=False, ) local_borg_version = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) extract_process = flexmock( stdout=flexmock( read=lambda: '{"config_paths": ["borgmatic/config.yaml"]}', @@ -244,6 +296,9 @@ def test_run_bootstrap_translates_ssh_command_argument_to_config(): dry_run=False, ) local_borg_version = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) extract_process = flexmock( stdout=flexmock( read=lambda: '{"config_paths": ["borgmatic/config.yaml"]}', diff --git a/tests/unit/actions/test_check.py b/tests/unit/actions/test_check.py index f8987733..917d8a4a 100644 --- a/tests/unit/actions/test_check.py +++ b/tests/unit/actions/test_check.py @@ -713,9 +713,6 @@ def test_collect_spot_check_archive_paths_excludes_directories(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/home/user/.borgmatic') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/1001/borgmatic') flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return( ( 'f /etc/path', @@ -732,6 +729,7 @@ def test_collect_spot_check_archive_paths_excludes_directories(): global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/user/1001/borgmatic', ) == ('/etc/path', '/etc/other') @@ -739,9 +737,6 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_runtime_dir flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return( ( 'f /etc/path', @@ -757,6 +752,7 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_runtime_dir global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/user/0/borgmatic', ) == ('/etc/path',) @@ -764,9 +760,6 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_source_dire flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return( ( 'f /etc/path', @@ -782,6 +775,7 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_source_dire global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/user/0/borgmatic', ) == ('/etc/path',) @@ -789,9 +783,6 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_runtime_dir flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root.borgmatic') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return( ( 'f /etc/path', @@ -807,6 +798,7 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_runtime_dir global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/user/0/borgmatic', ) == ('/etc/path',) @@ -1149,6 +1141,7 @@ def test_spot_check_without_spot_configuration_errors(): global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1161,6 +1154,7 @@ def test_spot_check_without_any_configuration_errors(): global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1181,6 +1175,7 @@ def test_spot_check_data_tolerance_percenatge_greater_than_data_sample_percentag global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1212,6 +1207,7 @@ def test_spot_check_with_count_delta_greater_than_count_tolerance_percentage_err global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1244,6 +1240,7 @@ def test_spot_check_with_failing_percentage_greater_than_data_tolerance_percenta global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1275,6 +1272,7 @@ def test_spot_check_with_high_enough_tolerances_does_not_raise(): global_arguments=flexmock(), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1362,6 +1360,9 @@ def test_run_check_runs_configured_spot_check(): flexmock(module).should_receive('make_archives_check_id').and_return(None) flexmock(module).should_receive('filter_checks_on_frequency').and_return({'spot'}) flexmock(module.borgmatic.borg.check).should_receive('check_archives').never() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) flexmock(module.borgmatic.actions.check).should_receive('spot_check').once() flexmock(module).should_receive('make_check_time_path') flexmock(module).should_receive('write_check_time') diff --git a/tests/unit/actions/test_create.py b/tests/unit/actions/test_create.py index 8582abae..c8450897 100644 --- a/tests/unit/actions/test_create.py +++ b/tests/unit/actions/test_create.py @@ -8,6 +8,9 @@ from borgmatic.actions import create as module def test_run_create_executes_and_calls_hooks_for_configured_repository(): flexmock(module.logger).answer = lambda message: None flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) flexmock(module.borgmatic.borg.create).should_receive('create_archive').once() flexmock(module).should_receive('create_borgmatic_manifest').once() flexmock(module.borgmatic.hooks.command).should_receive('execute_hook').times(2) @@ -44,6 +47,9 @@ def test_run_create_executes_and_calls_hooks_for_configured_repository(): def test_run_create_with_store_config_files_false_does_not_create_borgmatic_manifest(): flexmock(module.logger).answer = lambda message: None flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) flexmock(module.borgmatic.borg.create).should_receive('create_archive').once() flexmock(module).should_receive('create_borgmatic_manifest').never() flexmock(module.borgmatic.hooks.command).should_receive('execute_hook').times(2) @@ -82,6 +88,9 @@ def test_run_create_runs_with_selected_repository(): flexmock(module.borgmatic.config.validate).should_receive( 'repositories_match' ).once().and_return(True) + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) flexmock(module.borgmatic.borg.create).should_receive('create_archive').once() flexmock(module).should_receive('create_borgmatic_manifest').once() flexmock(module.borgmatic.hooks.command).should_receive('execute_hook').times(2) @@ -120,6 +129,7 @@ def test_run_create_bails_if_repository_does_not_match(): flexmock(module.borgmatic.config.validate).should_receive( 'repositories_match' ).once().and_return(False) + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').never() flexmock(module.borgmatic.borg.create).should_receive('create_archive').never() flexmock(module).should_receive('create_borgmatic_manifest').never() create_arguments = flexmock( @@ -153,6 +163,9 @@ def test_run_create_produces_json(): flexmock(module.borgmatic.config.validate).should_receive( 'repositories_match' ).once().and_return(True) + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) flexmock(module.borgmatic.borg.create).should_receive('create_archive').once().and_return( flexmock() ) @@ -191,18 +204,15 @@ def test_run_create_produces_json(): def test_create_borgmatic_manifest_creates_manifest_file(): - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.os.path).should_receive('join').with_args( - '/run/user/0/borgmatic', 'bootstrap', 'manifest.json' - ).and_return('/run/user/0/borgmatic/bootstrap/manifest.json') + '/run/borgmatic', 'bootstrap', 'manifest.json' + ).and_return('/run/borgmatic/bootstrap/manifest.json') flexmock(module.os.path).should_receive('exists').and_return(False) flexmock(module.os).should_receive('makedirs').and_return(True) flexmock(module.importlib.metadata).should_receive('version').and_return('1.0.0') flexmock(sys.modules['builtins']).should_receive('open').with_args( - '/run/user/0/borgmatic/bootstrap/manifest.json', 'w' + '/run/borgmatic/bootstrap/manifest.json', 'w' ).and_return( flexmock( __enter__=lambda *args: flexmock(write=lambda *args: None, close=lambda *args: None), @@ -211,22 +221,19 @@ def test_create_borgmatic_manifest_creates_manifest_file(): ) flexmock(module.json).should_receive('dump').and_return(True).once() - module.create_borgmatic_manifest({}, 'test.yaml', False) + module.create_borgmatic_manifest({}, 'test.yaml', '/run/borgmatic', False) def test_create_borgmatic_manifest_creates_manifest_file_with_custom_borgmatic_runtime_directory(): - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/borgmatic') flexmock(module.os.path).should_receive('join').with_args( - '/borgmatic', 'bootstrap', 'manifest.json' - ).and_return('/borgmatic/bootstrap/manifest.json') + '/run/borgmatic', 'bootstrap', 'manifest.json' + ).and_return('/run/borgmatic/bootstrap/manifest.json') flexmock(module.os.path).should_receive('exists').and_return(False) flexmock(module.os).should_receive('makedirs').and_return(True) flexmock(module.importlib.metadata).should_receive('version').and_return('1.0.0') flexmock(sys.modules['builtins']).should_receive('open').with_args( - '/borgmatic/bootstrap/manifest.json', 'w' + '/run/borgmatic/bootstrap/manifest.json', 'w' ).and_return( flexmock( __enter__=lambda *args: flexmock(write=lambda *args: None, close=lambda *args: None), @@ -236,9 +243,11 @@ def test_create_borgmatic_manifest_creates_manifest_file_with_custom_borgmatic_r flexmock(module.json).should_receive('dump').and_return(True).once() module.create_borgmatic_manifest( - {'borgmatic_runtime_directory': '/borgmatic'}, 'test.yaml', False + {'borgmatic_runtime_directory': '/borgmatic'}, 'test.yaml', '/run/borgmatic', False ) def test_create_borgmatic_manifest_does_not_create_manifest_file_on_dry_run(): - module.create_borgmatic_manifest({}, 'test.yaml', True) + flexmock(module.json).should_receive('dump').never() + + module.create_borgmatic_manifest({}, 'test.yaml', '/run/borgmatic', True) diff --git a/tests/unit/actions/test_restore.py b/tests/unit/actions/test_restore.py index d9e3d9d6..b06d6fb1 100644 --- a/tests/unit/actions/test_restore.py +++ b/tests/unit/actions/test_restore.py @@ -87,11 +87,8 @@ def test_strip_path_prefix_from_extracted_dump_destination_renames_first_matchin def test_restore_single_data_source_extracts_and_restores_single_file_dump(): flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( - 'make_data_source_dump_patterns', object, object, object, object + 'make_data_source_dump_patterns', object, object, object, object, object ).and_return({'postgresql': flexmock()}) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.tempfile).should_receive('mkdtemp').never() flexmock(module.borgmatic.hooks.dump).should_receive( 'convert_glob_patterns_to_borg_pattern' @@ -123,16 +120,14 @@ def test_restore_single_data_source_extracts_and_restores_single_file_dump(): hook_name='postgresql', data_source={'name': 'test', 'format': 'plain'}, connection_params=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) def test_restore_single_data_source_extracts_and_restores_directory_dump(): flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( - 'make_data_source_dump_patterns', object, object, object, object + 'make_data_source_dump_patterns', object, object, object, object, object ).and_return({'postgresql': flexmock()}) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.tempfile).should_receive('mkdtemp').once().and_return( '/run/user/0/borgmatic/tmp1234' ) @@ -166,16 +161,14 @@ def test_restore_single_data_source_extracts_and_restores_directory_dump(): hook_name='postgresql', data_source={'name': 'test', 'format': 'directory'}, connection_params=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) def test_restore_single_data_source_with_directory_dump_error_cleans_up_temporary_directory(): flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( - 'make_data_source_dump_patterns', object, object, object, object + 'make_data_source_dump_patterns', object, object, object, object, object ).and_return({'postgresql': flexmock()}) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.tempfile).should_receive('mkdtemp').once().and_return( '/run/user/0/borgmatic/tmp1234' ) @@ -210,16 +203,14 @@ def test_restore_single_data_source_with_directory_dump_error_cleans_up_temporar hook_name='postgresql', data_source={'name': 'test', 'format': 'directory'}, connection_params=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) def test_restore_single_data_source_with_directory_dump_and_dry_run_skips_directory_move_and_cleanup(): flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( - 'make_data_source_dump_patterns', object, object, object, object + 'make_data_source_dump_patterns', object, object, object, object, object ).and_return({'postgresql': flexmock()}) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.tempfile).should_receive('mkdtemp').once().and_return( '/run/user/0/borgmatic/tmp1234' ) @@ -253,6 +244,7 @@ def test_restore_single_data_source_with_directory_dump_and_dry_run_skips_direct hook_name='postgresql', data_source={'name': 'test', 'format': 'directory'}, connection_params=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -260,9 +252,6 @@ def test_collect_archive_data_source_names_parses_archive_paths(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.borgmatic.hooks.dump).should_receive('make_data_source_dump_path').and_return( '' ) @@ -282,6 +271,7 @@ def test_collect_archive_data_source_names_parses_archive_paths(): global_arguments=flexmock(log_json=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) assert archive_data_source_names == { @@ -294,9 +284,6 @@ def test_collect_archive_data_source_names_parses_archive_paths_with_different_b flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.borgmatic.hooks.dump).should_receive('make_data_source_dump_path').and_return( '' ) @@ -317,6 +304,7 @@ def test_collect_archive_data_source_names_parses_archive_paths_with_different_b global_arguments=flexmock(log_json=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) assert archive_data_source_names == { @@ -329,9 +317,6 @@ def test_collect_archive_data_source_names_parses_directory_format_archive_paths flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.borgmatic.hooks.dump).should_receive('make_data_source_dump_path').and_return( '' ) @@ -350,6 +335,7 @@ def test_collect_archive_data_source_names_parses_directory_format_archive_paths global_arguments=flexmock(log_json=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) assert archive_data_source_names == { @@ -361,9 +347,6 @@ def test_collect_archive_data_source_names_skips_bad_archive_paths(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/run/user/0/borgmatic') flexmock(module.borgmatic.hooks.dump).should_receive('make_data_source_dump_path').and_return( '' ) @@ -384,6 +367,7 @@ def test_collect_archive_data_source_names_skips_bad_archive_paths(): global_arguments=flexmock(log_json=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) assert archive_data_source_names == { @@ -481,6 +465,10 @@ def test_run_restore_restores_each_data_source(): } flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True) + borgmatic_runtime_directory = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + borgmatic_runtime_directory + ) flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured') flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( flexmock() @@ -501,6 +489,7 @@ def test_run_restore_restores_each_data_source(): hook_name='postgresql_databases', data_source={'name': 'foo', 'schemas': None}, connection_params=object, + borgmatic_runtime_directory=borgmatic_runtime_directory, ).once() flexmock(module).should_receive('restore_single_data_source').with_args( repository=object, @@ -513,6 +502,7 @@ def test_run_restore_restores_each_data_source(): hook_name='postgresql_databases', data_source={'name': 'bar', 'schemas': None}, connection_params=object, + borgmatic_runtime_directory=borgmatic_runtime_directory, ).once() flexmock(module).should_receive('ensure_data_sources_found') @@ -534,6 +524,7 @@ def test_run_restore_restores_each_data_source(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -541,6 +532,9 @@ def test_run_restore_bails_for_non_matching_repository(): flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return( False ) + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + flexmock() + ) flexmock(module.borgmatic.hooks.dispatch).should_receive( 'call_hooks_even_if_unconfigured' ).never() @@ -554,6 +548,7 @@ def test_run_restore_bails_for_non_matching_repository(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -563,6 +558,10 @@ def test_run_restore_restores_data_source_configured_with_all_name(): } flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True) + borgmatic_runtime_directory = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + borgmatic_runtime_directory + ) flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured') flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( flexmock() @@ -599,6 +598,7 @@ def test_run_restore_restores_data_source_configured_with_all_name(): hook_name='postgresql_databases', data_source={'name': 'foo', 'schemas': None}, connection_params=object, + borgmatic_runtime_directory=borgmatic_runtime_directory, ).once() flexmock(module).should_receive('restore_single_data_source').with_args( repository=object, @@ -611,6 +611,7 @@ def test_run_restore_restores_data_source_configured_with_all_name(): hook_name='postgresql_databases', data_source={'name': 'bar', 'schemas': None}, connection_params=object, + borgmatic_runtime_directory=borgmatic_runtime_directory, ).once() flexmock(module).should_receive('ensure_data_sources_found') @@ -632,6 +633,7 @@ def test_run_restore_restores_data_source_configured_with_all_name(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -641,6 +643,10 @@ def test_run_restore_skips_missing_data_source(): } flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True) + borgmatic_runtime_directory = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + borgmatic_runtime_directory + ) flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured') flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( flexmock() @@ -677,6 +683,7 @@ def test_run_restore_skips_missing_data_source(): hook_name='postgresql_databases', data_source={'name': 'foo', 'schemas': None}, connection_params=object, + borgmatic_runtime_directory=borgmatic_runtime_directory, ).once() flexmock(module).should_receive('restore_single_data_source').with_args( repository=object, @@ -689,6 +696,7 @@ def test_run_restore_skips_missing_data_source(): hook_name='postgresql_databases', data_source={'name': 'bar', 'schemas': None}, connection_params=object, + borgmatic_runtime_directory=borgmatic_runtime_directory, ).never() flexmock(module).should_receive('ensure_data_sources_found') @@ -710,6 +718,7 @@ def test_run_restore_skips_missing_data_source(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) @@ -720,6 +729,10 @@ def test_run_restore_restores_data_sources_from_different_hooks(): } flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True) + borgmatic_runtime_directory = flexmock() + flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return( + borgmatic_runtime_directory + ) flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured') flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( flexmock() @@ -749,6 +762,7 @@ def test_run_restore_restores_data_sources_from_different_hooks(): hook_name='postgresql_databases', data_source={'name': 'foo', 'schemas': None}, connection_params=object, + borgmatic_runtime_directory=borgmatic_runtime_directory, ).once() flexmock(module).should_receive('restore_single_data_source').with_args( repository=object, @@ -761,6 +775,7 @@ def test_run_restore_restores_data_sources_from_different_hooks(): hook_name='mysql_databases', data_source={'name': 'bar', 'schemas': None}, connection_params=object, + borgmatic_runtime_directory=borgmatic_runtime_directory, ).once() flexmock(module).should_receive('ensure_data_sources_found') @@ -782,4 +797,5 @@ def test_run_restore_restores_data_sources_from_different_hooks(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), + borgmatic_runtime_directory='/run/borgmatic', ) diff --git a/tests/unit/borg/test_create.py b/tests/unit/borg/test_create.py index f24707a5..b1f8b509 100644 --- a/tests/unit/borg/test_create.py +++ b/tests/unit/borg/test_create.py @@ -1453,9 +1453,6 @@ def test_create_archive_calls_borg_with_parameters(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1483,6 +1480,7 @@ def test_create_archive_calls_borg_with_parameters(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', ) @@ -1490,9 +1488,6 @@ def test_create_archive_calls_borg_with_environment(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1521,6 +1516,7 @@ def test_create_archive_calls_borg_with_environment(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', ) @@ -1528,9 +1524,6 @@ def test_create_archive_with_log_info_calls_borg_with_info_parameter(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1559,6 +1552,7 @@ def test_create_archive_with_log_info_calls_borg_with_info_parameter(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', ) @@ -1566,9 +1560,6 @@ def test_create_archive_with_log_info_and_json_suppresses_most_borg_output(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1595,6 +1586,7 @@ def test_create_archive_with_log_info_and_json_suppresses_most_borg_output(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', json=True, ) @@ -1603,9 +1595,6 @@ def test_create_archive_with_log_debug_calls_borg_with_debug_parameter(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1634,6 +1623,7 @@ def test_create_archive_with_log_debug_calls_borg_with_debug_parameter(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', ) @@ -1641,9 +1631,6 @@ def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1670,6 +1657,7 @@ def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', json=True, ) @@ -1680,9 +1668,6 @@ def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create', '--dry-run'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1711,6 +1696,7 @@ def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', stats=True, ) @@ -1719,9 +1705,6 @@ def test_create_archive_with_working_directory_calls_borg_with_working_directory flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1752,6 +1735,7 @@ def test_create_archive_with_working_directory_calls_borg_with_working_directory config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', ) @@ -1759,9 +1743,6 @@ def test_create_archive_with_exit_codes_calls_borg_using_them(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1791,6 +1772,7 @@ def test_create_archive_with_exit_codes_calls_borg_using_them(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', ) @@ -1798,9 +1780,6 @@ def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_answer_ou flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1828,6 +1807,7 @@ def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_answer_ou config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', stats=True, ) @@ -1836,9 +1816,6 @@ def test_create_archive_with_files_calls_borg_with_answer_output_log_level(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( ( @@ -1871,6 +1848,7 @@ def test_create_archive_with_files_calls_borg_with_answer_output_log_level(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', list_files=True, ) @@ -1879,9 +1857,6 @@ def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_para flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1910,6 +1885,7 @@ def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_para config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', progress=True, ) @@ -1918,9 +1894,6 @@ def test_create_archive_with_progress_calls_borg_with_progress_parameter(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -1948,6 +1921,7 @@ def test_create_archive_with_progress_calls_borg_with_progress_parameter(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', progress=True, ) @@ -1957,9 +1931,6 @@ def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progr flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER processes = flexmock() flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( ( @@ -2009,6 +1980,7 @@ def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progr config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', progress=True, stream_processes=processes, ) @@ -2018,9 +1990,6 @@ def test_create_archive_with_json_calls_borg_with_json_flag(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -2046,6 +2015,7 @@ def test_create_archive_with_json_calls_borg_with_json_flag(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', json=True, ) @@ -2056,9 +2026,6 @@ def test_create_archive_with_stats_and_json_calls_borg_without_stats_flag(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -2084,6 +2051,7 @@ def test_create_archive_with_stats_and_json_calls_borg_without_stats_flag(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', json=True, stats=True, ) @@ -2095,9 +2063,6 @@ def test_create_archive_calls_borg_with_working_directory(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module).should_receive('expand_directories').and_return(()) - flexmock(module.borgmatic.config.paths).should_receive( - 'get_borgmatic_runtime_directory' - ).and_return('/var/run/0/borgmatic') flexmock(module).should_receive('collect_borgmatic_runtime_directories').and_return([]) flexmock(module).should_receive('make_base_create_command').and_return( (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock()) @@ -2128,6 +2093,7 @@ def test_create_archive_calls_borg_with_working_directory(): config_paths=['/tmp/test.yaml'], local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + borgmatic_runtime_directory='/borgmatic/run', ) diff --git a/tests/unit/config/test_paths.py b/tests/unit/config/test_paths.py index deeac41b..a6b6892c 100644 --- a/tests/unit/config/test_paths.py +++ b/tests/unit/config/test_paths.py @@ -33,53 +33,120 @@ def test_get_borgmatic_source_directory_without_config_option_uses_default(): assert module.get_borgmatic_source_directory({}) == '~/.borgmatic' -def test_get_borgmatic_runtime_directory_uses_config_option(): +def test_runtime_directory_uses_config_option(): flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) + config = {'user_runtime_directory': '/run', 'borgmatic_source_directory': '/nope'} - assert ( - module.get_borgmatic_runtime_directory( - {'user_runtime_directory': '/tmp', 'borgmatic_source_directory': '/nope'} - ) - == '/tmp/./borgmatic' - ) + with module.Runtime_directory(config) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/run/./borgmatic' -def test_get_borgmatic_runtime_directory_falls_back_to_linux_environment_variable(): +def test_runtime_directory_uses_config_option_without_adding_duplicate_borgmatic_subdirectory(): + flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) + config = {'user_runtime_directory': '/run/borgmatic', 'borgmatic_source_directory': '/nope'} + + with module.Runtime_directory(config) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/run/./borgmatic' + + +def test_runtime_directory_falls_back_to_xdg_runtime_dir(): flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return( - '/tmp' + '/run' ) - assert module.get_borgmatic_runtime_directory({}) == '/tmp/./borgmatic' + with module.Runtime_directory({}) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/run/./borgmatic' -def test_get_borgmatic_runtime_directory_falls_back_to_macos_environment_variable(): +def test_runtime_directory_falls_back_to_xdg_runtime_dir_without_adding_duplicate_borgmatic_subdirectory(): + flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) + flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return( + '/run/borgmatic' + ) + + with module.Runtime_directory({}) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/run/./borgmatic' + + +def test_runtime_directory_falls_back_to_runtime_directory(): flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return(None) - flexmock(module.os.environ).should_receive('get').with_args('TMPDIR').and_return('/tmp') + flexmock(module.os.environ).should_receive('get').with_args('RUNTIME_DIRECTORY').and_return( + '/run' + ) - assert module.get_borgmatic_runtime_directory({}) == '/tmp/./borgmatic' + with module.Runtime_directory({}) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/run/./borgmatic' -def test_get_borgmatic_runtime_directory_falls_back_to_other_environment_variable(): +def test_runtime_directory_falls_back_to_runtime_directory_without_adding_duplicate_borgmatic_subdirectory(): flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return(None) + flexmock(module.os.environ).should_receive('get').with_args('RUNTIME_DIRECTORY').and_return( + '/run/borgmatic' + ) + + with module.Runtime_directory({}) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/run/./borgmatic' + + +def test_runtime_directory_falls_back_to_tmpdir_and_adds_temporary_subdirectory_that_get_cleaned_up(): + flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) + flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return(None) + flexmock(module.os.environ).should_receive('get').with_args('RUNTIME_DIRECTORY').and_return( + None + ) + flexmock(module.os.environ).should_receive('get').with_args('TMPDIR').and_return('/run') + temporary_directory = flexmock(name='/run/borgmatic-1234') + temporary_directory.should_receive('cleanup').once() + flexmock(module.tempfile).should_receive('TemporaryDirectory').with_args( + prefix='borgmatic', dir='/run' + ).and_return(temporary_directory) + + with module.Runtime_directory({}) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/run/borgmatic-1234/./borgmatic' + + +def test_runtime_directory_falls_back_to_temp_and_adds_temporary_subdirectory_that_get_cleaned_up(): + flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) + flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return(None) + flexmock(module.os.environ).should_receive('get').with_args('RUNTIME_DIRECTORY').and_return( + None + ) flexmock(module.os.environ).should_receive('get').with_args('TMPDIR').and_return(None) - flexmock(module.os.environ).should_receive('get').with_args('TEMP').and_return('/tmp') + flexmock(module.os.environ).should_receive('get').with_args('TEMP').and_return('/run') + temporary_directory = flexmock(name='/run/borgmatic-1234') + temporary_directory.should_receive('cleanup').once() + flexmock(module.tempfile).should_receive('TemporaryDirectory').with_args( + prefix='borgmatic', dir='/run' + ).and_return(temporary_directory) - assert module.get_borgmatic_runtime_directory({}) == '/tmp/./borgmatic' + with module.Runtime_directory({}) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/run/borgmatic-1234/./borgmatic' -def test_get_borgmatic_runtime_directory_defaults_to_hard_coded_path(): +def test_runtime_directory_falls_back_to_hard_coded_tmp_path_and_adds_temporary_subdirectory_that_get_cleaned_up(): flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) - flexmock(module.os.environ).should_receive('get').and_return('/run/user/0') - flexmock(module.os).should_receive('getuid').and_return(0) + flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return(None) + flexmock(module.os.environ).should_receive('get').with_args('RUNTIME_DIRECTORY').and_return( + None + ) + flexmock(module.os.environ).should_receive('get').with_args('TMPDIR').and_return(None) + flexmock(module.os.environ).should_receive('get').with_args('TEMP').and_return(None) + temporary_directory = flexmock(name='/tmp/borgmatic-1234') + temporary_directory.should_receive('cleanup').once() + flexmock(module.tempfile).should_receive('TemporaryDirectory').with_args( + prefix='borgmatic', dir='/tmp' + ).and_return(temporary_directory) - assert module.get_borgmatic_runtime_directory({}) == '/run/user/0/./borgmatic' + with module.Runtime_directory({}) as borgmatic_runtime_directory: + assert borgmatic_runtime_directory == '/tmp/borgmatic-1234/./borgmatic' def test_get_borgmatic_state_directory_uses_config_option(): flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) + flexmock(module.os.environ).should_receive('get').never() assert ( module.get_borgmatic_state_directory( @@ -89,16 +156,24 @@ def test_get_borgmatic_state_directory_uses_config_option(): ) -def test_get_borgmatic_state_directory_falls_back_to_environment_variable(): +def test_get_borgmatic_state_directory_falls_back_to_xdg_state_home(): flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) - flexmock(module.os.environ).should_receive('get').with_args( - 'XDG_STATE_HOME', object - ).and_return('/tmp') + flexmock(module.os.environ).should_receive('get').with_args('XDG_STATE_HOME').and_return('/tmp') + + assert module.get_borgmatic_state_directory({}) == '/tmp/borgmatic' + + +def test_get_borgmatic_state_directory_falls_back_to_state_directory(): + flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) + flexmock(module.os.environ).should_receive('get').with_args('XDG_STATE_HOME').and_return(None) + flexmock(module.os.environ).should_receive('get').with_args('STATE_DIRECTORY').and_return( + '/tmp' + ) assert module.get_borgmatic_state_directory({}) == '/tmp/borgmatic' def test_get_borgmatic_state_directory_defaults_to_hard_coded_path(): flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path) - flexmock(module.os.environ).should_receive('get').and_return('/root/.local/state') - assert module.get_borgmatic_state_directory({}) == '/root/.local/state/borgmatic' + flexmock(module.os.environ).should_receive('get').and_return(None) + assert module.get_borgmatic_state_directory({}) == '~/.local/state/borgmatic' diff --git a/tests/unit/hooks/test_mariadb.py b/tests/unit/hooks/test_mariadb.py index 8034afed..fd039f74 100644 --- a/tests/unit/hooks/test_mariadb.py +++ b/tests/unit/hooks/test_mariadb.py @@ -73,7 +73,12 @@ def test_dump_data_sources_dumps_each_database(): dry_run_label=object, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_dump_data_sources_dumps_with_password(): @@ -94,7 +99,9 @@ def test_dump_data_sources_dumps_with_password(): dry_run_label=object, ).and_return(process).once() - assert module.dump_data_sources([database], {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + [database], {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_dumps_all_databases_at_once(): @@ -112,7 +119,9 @@ def test_dump_data_sources_dumps_all_databases_at_once(): dry_run_label=object, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_dumps_all_databases_separately_when_format_configured(): @@ -132,7 +141,12 @@ def test_dump_data_sources_dumps_all_databases_separately_when_format_configured dry_run_label=object, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_database_names_to_dump_runs_mariadb_with_list_options(): @@ -434,7 +448,9 @@ def test_dump_data_sources_errors_for_missing_all_databases(): flexmock(module).should_receive('database_names_to_dump').and_return(()) with pytest.raises(ValueError): - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run(): @@ -445,7 +461,12 @@ def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run ) flexmock(module).should_receive('database_names_to_dump').and_return(()) - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=True) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=True + ) + == [] + ) def test_restore_data_source_dump_runs_mariadb_to_restore(): diff --git a/tests/unit/hooks/test_mongodb.py b/tests/unit/hooks/test_mongodb.py index 86ac7b7e..8b56c9e1 100644 --- a/tests/unit/hooks/test_mongodb.py +++ b/tests/unit/hooks/test_mongodb.py @@ -41,7 +41,12 @@ def test_dump_data_sources_runs_mongodump_for_each_database(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_dump_data_sources_with_dry_run_skips_mongodump(): @@ -53,7 +58,12 @@ def test_dump_data_sources_with_dry_run_skips_mongodump(): flexmock(module.dump).should_receive('create_named_pipe_for_dump').never() flexmock(module).should_receive('execute_command').never() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=True) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=True + ) + == [] + ) def test_dump_data_sources_runs_mongodump_with_hostname_and_port(): @@ -82,7 +92,9 @@ def test_dump_data_sources_runs_mongodump_with_hostname_and_port(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_runs_mongodump_with_username_and_password(): @@ -120,7 +132,9 @@ def test_dump_data_sources_runs_mongodump_with_username_and_password(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_runs_mongodump_with_directory_format(): @@ -137,7 +151,12 @@ def test_dump_data_sources_runs_mongodump_with_directory_format(): shell=True, ).and_return(flexmock()).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == [] + ) def test_dump_data_sources_runs_mongodump_with_options(): @@ -163,7 +182,9 @@ def test_dump_data_sources_runs_mongodump_with_options(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_runs_mongodumpall_for_all_databases(): @@ -181,7 +202,9 @@ def test_dump_data_sources_runs_mongodumpall_for_all_databases(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_build_dump_command_with_username_injection_attack_gets_escaped(): diff --git a/tests/unit/hooks/test_mysql.py b/tests/unit/hooks/test_mysql.py index fa6145ac..4d608bf6 100644 --- a/tests/unit/hooks/test_mysql.py +++ b/tests/unit/hooks/test_mysql.py @@ -73,7 +73,12 @@ def test_dump_data_sources_dumps_each_database(): dry_run_label=object, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_dump_data_sources_dumps_with_password(): @@ -94,7 +99,9 @@ def test_dump_data_sources_dumps_with_password(): dry_run_label=object, ).and_return(process).once() - assert module.dump_data_sources([database], {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + [database], {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_dumps_all_databases_at_once(): @@ -112,7 +119,9 @@ def test_dump_data_sources_dumps_all_databases_at_once(): dry_run_label=object, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_dumps_all_databases_separately_when_format_configured(): @@ -132,7 +141,12 @@ def test_dump_data_sources_dumps_all_databases_separately_when_format_configured dry_run_label=object, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_database_names_to_dump_runs_mysql_with_list_options(): @@ -432,7 +446,9 @@ def test_dump_data_sources_errors_for_missing_all_databases(): flexmock(module).should_receive('database_names_to_dump').and_return(()) with pytest.raises(ValueError): - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run(): @@ -443,7 +459,12 @@ def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run ) flexmock(module).should_receive('database_names_to_dump').and_return(()) - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=True) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=True + ) + == [] + ) def test_restore_data_source_dump_runs_mysql_to_restore(): diff --git a/tests/unit/hooks/test_postgresql.py b/tests/unit/hooks/test_postgresql.py index 87f5dc0f..f0585258 100644 --- a/tests/unit/hooks/test_postgresql.py +++ b/tests/unit/hooks/test_postgresql.py @@ -251,7 +251,12 @@ def test_dump_data_sources_runs_pg_dump_for_each_database(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_dump_data_sources_raises_when_no_database_names_to_dump(): @@ -261,7 +266,9 @@ def test_dump_data_sources_raises_when_no_database_names_to_dump(): flexmock(module).should_receive('database_names_to_dump').and_return(()) with pytest.raises(ValueError): - module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) def test_dump_data_sources_does_not_raise_when_no_database_names_to_dump(): @@ -270,7 +277,9 @@ def test_dump_data_sources_does_not_raise_when_no_database_names_to_dump(): flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module).should_receive('database_names_to_dump').and_return(()) - module.dump_data_sources(databases, {}, 'test.yaml', dry_run=True) == [] + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=True + ) == [] def test_dump_data_sources_with_duplicate_dump_skips_pg_dump(): @@ -287,7 +296,12 @@ def test_dump_data_sources_with_duplicate_dump_skips_pg_dump(): flexmock(module.dump).should_receive('create_named_pipe_for_dump').never() flexmock(module).should_receive('execute_command').never() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == [] + ) def test_dump_data_sources_with_dry_run_skips_pg_dump(): @@ -304,7 +318,12 @@ def test_dump_data_sources_with_dry_run_skips_pg_dump(): flexmock(module.dump).should_receive('create_named_pipe_for_dump').never() flexmock(module).should_receive('execute_command').never() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=True) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=True + ) + == [] + ) def test_dump_data_sources_runs_pg_dump_with_hostname_and_port(): @@ -340,7 +359,9 @@ def test_dump_data_sources_runs_pg_dump_with_hostname_and_port(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_runs_pg_dump_with_username_and_password(): @@ -376,7 +397,9 @@ def test_dump_data_sources_runs_pg_dump_with_username_and_password(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_with_username_injection_attack_gets_escaped(): @@ -412,7 +435,9 @@ def test_dump_data_sources_with_username_injection_attack_gets_escaped(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_runs_pg_dump_with_directory_format(): @@ -443,7 +468,12 @@ def test_dump_data_sources_runs_pg_dump_with_directory_format(): extra_environment={'PGSSLMODE': 'disable'}, ).and_return(flexmock()).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == [] + ) def test_dump_data_sources_runs_pg_dump_with_options(): @@ -476,7 +506,9 @@ def test_dump_data_sources_runs_pg_dump_with_options(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_runs_pg_dumpall_for_all_databases(): @@ -498,7 +530,9 @@ def test_dump_data_sources_runs_pg_dumpall_for_all_databases(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_dump_data_sources_runs_non_default_pg_dump(): @@ -532,7 +566,9 @@ def test_dump_data_sources_runs_non_default_pg_dump(): run_to_completion=False, ).and_return(process).once() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [process] + assert module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) == [process] def test_restore_data_source_dump_runs_pg_restore(): diff --git a/tests/unit/hooks/test_sqlite.py b/tests/unit/hooks/test_sqlite.py index ea560e30..27a4aa31 100644 --- a/tests/unit/hooks/test_sqlite.py +++ b/tests/unit/hooks/test_sqlite.py @@ -18,15 +18,20 @@ def test_use_streaming_false_for_no_databases(): def test_dump_data_sources_logs_and_skips_if_dump_already_exists(): databases = [{'path': '/path/to/database', 'name': 'database'}] - flexmock(module).should_receive('make_dump_path').and_return('/path/to/dump') + flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic') flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return( - '/path/to/dump/database' + '/run/borgmatic/database' ) flexmock(module.os.path).should_receive('exists').and_return(True) flexmock(module.dump).should_receive('create_named_pipe_for_dump').never() flexmock(module).should_receive('execute_command').never() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == [] + ) def test_dump_data_sources_dumps_each_database(): @@ -36,9 +41,9 @@ def test_dump_data_sources_dumps_each_database(): ] processes = [flexmock(), flexmock()] - flexmock(module).should_receive('make_dump_path').and_return('/path/to/dump') + flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic') flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return( - '/path/to/dump/database' + '/run/borgmatic/database' ) flexmock(module.os.path).should_receive('exists').and_return(False) flexmock(module.dump).should_receive('create_named_pipe_for_dump') @@ -46,7 +51,12 @@ def test_dump_data_sources_dumps_each_database(): processes[1] ) - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_dump_data_sources_with_path_injection_attack_gets_escaped(): @@ -55,9 +65,9 @@ def test_dump_data_sources_with_path_injection_attack_gets_escaped(): ] processes = [flexmock()] - flexmock(module).should_receive('make_dump_path').and_return('/path/to/dump') + flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic') flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return( - '/path/to/dump/database' + '/run/borgmatic/database' ) flexmock(module.os.path).should_receive('exists').and_return(False) flexmock(module.dump).should_receive('create_named_pipe_for_dump') @@ -67,13 +77,18 @@ def test_dump_data_sources_with_path_injection_attack_gets_escaped(): "'/path/to/database1; naughty-command'", '.dump', '>', - '/path/to/dump/database', + '/run/borgmatic/database', ), shell=True, run_to_completion=False, ).and_return(processes[0]) - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_dump_data_sources_with_non_existent_path_warns_and_dumps_database(): @@ -82,16 +97,21 @@ def test_dump_data_sources_with_non_existent_path_warns_and_dumps_database(): ] processes = [flexmock()] - flexmock(module).should_receive('make_dump_path').and_return('/path/to/dump') + flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic') flexmock(module.logger).should_receive('warning').once() flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return( - '/path/to/dump/database' + '/run/borgmatic' ) flexmock(module.os.path).should_receive('exists').and_return(False) flexmock(module.dump).should_receive('create_named_pipe_for_dump') flexmock(module).should_receive('execute_command').and_return(processes[0]) - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_dump_data_sources_with_name_all_warns_and_dumps_all_databases(): @@ -100,32 +120,42 @@ def test_dump_data_sources_with_name_all_warns_and_dumps_all_databases(): ] processes = [flexmock()] - flexmock(module).should_receive('make_dump_path').and_return('/path/to/dump') + flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic') flexmock(module.logger).should_receive( 'warning' ).twice() # once for the name=all, once for the non-existent path flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return( - '/path/to/dump/database' + '/run/borgmatic/database' ) flexmock(module.os.path).should_receive('exists').and_return(False) flexmock(module.dump).should_receive('create_named_pipe_for_dump') flexmock(module).should_receive('execute_command').and_return(processes[0]) - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=False) == processes + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=False + ) + == processes + ) def test_dump_data_sources_does_not_dump_if_dry_run(): databases = [{'path': '/path/to/database', 'name': 'database'}] - flexmock(module).should_receive('make_dump_path').and_return('/path/to/dump') + flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic') flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return( - '/path/to/dump/database' + '/run/borgmatic' ) flexmock(module.os.path).should_receive('exists').and_return(False) flexmock(module.dump).should_receive('create_named_pipe_for_dump').never() flexmock(module).should_receive('execute_command').never() - assert module.dump_data_sources(databases, {}, 'test.yaml', dry_run=True) == [] + assert ( + module.dump_data_sources( + databases, {}, 'test.yaml', borgmatic_runtime_directory='/run/borgmatic', dry_run=True + ) + == [] + ) def test_restore_data_source_dump_restores_database():