diff --git a/NEWS b/NEWS index e868bd5e..e464f264 100644 --- a/NEWS +++ b/NEWS @@ -2,8 +2,8 @@ * #1309: Add support for the "--quick-stats" flag and the "quick_statistics" option to the "prune" action. Borg >= 1.4.5 and < 2 only. * #1317: Add an "--archive-hostname" flag and corresponding "archive_hostname" option for - overriding the hostname used when creating an archive, e.g. via the "{hostname}" placeholder in - the "archive_name_format" option. + overriding the hostname used for the "{hostname}" placeholder in the "archive_name_format" + option. * #1319: Fix the ZFS hook's overzealous unmounting of snapshot paths when a source dataset is at "/". * #1322: Fix for the "restore" action sometimes failing to find a database dump that was dumped diff --git a/borgmatic/borg/borg.py b/borgmatic/borg/borg.py index 20af8e11..98329038 100644 --- a/borgmatic/borg/borg.py +++ b/borgmatic/borg/borg.py @@ -29,7 +29,6 @@ def run_arbitrary_borg( command. ''' borgmatic.logger.add_custom_log_levels() - lock_wait = config.get('lock_wait', None) try: options = options[1:] if options[0] == '--' else options @@ -52,8 +51,9 @@ def run_arbitrary_borg( + borg_command + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ()) + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ()) + + flags.make_flags('hostname', config.get('archive_hostname')) + flags.make_flags('remote-path', remote_path) - + flags.make_flags('lock-wait', lock_wait) + + flags.make_flags('lock-wait', config.get('lock_wait')) + command_options ) diff --git a/borgmatic/borg/break_lock.py b/borgmatic/borg/break_lock.py index 18eae878..cf736cc3 100644 --- a/borgmatic/borg/break_lock.py +++ b/borgmatic/borg/break_lock.py @@ -21,12 +21,14 @@ def break_lock( argparse.Namespace of global arguments, and optional local and remote Borg paths, break any repository and cache locks leftover from Borg aborting. ''' - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') extra_borg_options = config.get('extra_borg_options', {}).get('break_lock', '') full_command = ( (local_path, 'break-lock') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + ('--log-json',) diff --git a/borgmatic/borg/change_passphrase.py b/borgmatic/borg/change_passphrase.py index 99ad9d65..406d94a2 100644 --- a/borgmatic/borg/change_passphrase.py +++ b/borgmatic/borg/change_passphrase.py @@ -24,12 +24,14 @@ def change_passphrase( based on an interactive prompt. ''' borgmatic.logger.add_custom_log_levels() - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') extra_borg_options = config.get('extra_borg_options', {}).get('key_change_passphrase', '') full_command = ( (local_path, 'key', 'change-passphrase') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + (('--lock-wait', str(lock_wait)) if lock_wait else ()) diff --git a/borgmatic/borg/check.py b/borgmatic/borg/check.py index 64ea23cd..2ddecc57 100644 --- a/borgmatic/borg/check.py +++ b/borgmatic/borg/check.py @@ -20,7 +20,7 @@ def make_archive_filter_flags(local_borg_version, config, checks, check_argument flag. And if "prefix" is set in configuration and "archives" is in checks, then include a "--match-archives" flag. ''' - check_last = config.get('check_last', None) + check_last = config.get('check_last') prefix = config.get('prefix') if 'archives' in checks or 'data' in checks: @@ -152,6 +152,7 @@ def check_archives( # If not configured, elevate Borg's exit code 1 (an ostensible warning) to error, because Borg # returns exit code 1 for repository check errors! borg_exit_codes = [*config.get('borg_exit_codes', []), *[{'code': 1, 'treat_as': 'error'}]] + archive_hostname = config.get('archive_hostname') umask = config.get('umask') working_directory = borgmatic.config.paths.get_working_directory(config) @@ -176,6 +177,7 @@ def check_archives( else () ) + make_check_name_flags(checks_subset, archive_filter_flags) + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + ( diff --git a/borgmatic/borg/compact.py b/borgmatic/borg/compact.py index d3be5747..b0d036bc 100644 --- a/borgmatic/borg/compact.py +++ b/borgmatic/borg/compact.py @@ -22,13 +22,15 @@ def compact_segments( Given dry-run flag, a local or remote repository path, a configuration dict, and the local Borg version, compact the segments in a repository. ''' - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') extra_borg_options = config.get('extra_borg_options', {}).get('compact', '') threshold = config.get('compact_threshold') full_command = ( (local_path, 'compact') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + (('--log-json',) if (config.get('log_json') or not config.get('progress')) else ()) diff --git a/borgmatic/borg/create.py b/borgmatic/borg/create.py index 88f62d3a..ec01a4e8 100644 --- a/borgmatic/borg/create.py +++ b/borgmatic/borg/create.py @@ -188,14 +188,14 @@ def make_base_create_command( # noqa: PLR0912 patterns, borgmatic_runtime_directory, ) - checkpoint_interval = config.get('checkpoint_interval', None) - checkpoint_volume = config.get('checkpoint_volume', None) - chunker_params = config.get('chunker_params', None) - compression = config.get('compression', None) - upload_rate_limit = config.get('upload_rate_limit', None) - upload_buffer_size = config.get('upload_buffer_size', None) - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + checkpoint_interval = config.get('checkpoint_interval') + checkpoint_volume = config.get('checkpoint_volume') + chunker_params = config.get('chunker_params') + compression = config.get('compression') + upload_rate_limit = config.get('upload_rate_limit') + upload_buffer_size = config.get('upload_buffer_size') + umask = config.get('umask') + lock_wait = config.get('lock_wait') list_filter_flags = flags.make_list_filter_flags(local_borg_version, dry_run) files_changed = config.get('files_changed') files_cache = config.get('files_cache') diff --git a/borgmatic/borg/delete.py b/borgmatic/borg/delete.py index acfe5695..f5f1b22e 100644 --- a/borgmatic/borg/delete.py +++ b/borgmatic/borg/delete.py @@ -36,6 +36,7 @@ def make_delete_command( + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ()) + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ()) + borgmatic.borg.flags.make_flags('dry-run', global_arguments.dry_run) + + borgmatic.borg.flags.make_flags('hostname', config.get('archive_hostname')) + borgmatic.borg.flags.make_flags('remote-path', remote_path) + borgmatic.borg.flags.make_flags('umask', config.get('umask')) + ('--log-json',) diff --git a/borgmatic/borg/diff.py b/borgmatic/borg/diff.py index 8bff7429..92bcc3d5 100644 --- a/borgmatic/borg/diff.py +++ b/borgmatic/borg/diff.py @@ -31,7 +31,8 @@ def diff( ''' borgmatic.logger.add_custom_log_levels() - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + lock_wait = config.get('lock_wait') exclude_flags = flags.make_exclude_flags(config) extra_borg_options = config.get('extra_borg_options', {}).get('diff', '') @@ -53,6 +54,7 @@ def diff( diff_command = ( (local_path, 'diff') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + ('--log-json',) + (('--lock-wait', str(lock_wait)) if lock_wait is not None else ()) diff --git a/borgmatic/borg/export_key.py b/borgmatic/borg/export_key.py index ee210718..5699a3bc 100644 --- a/borgmatic/borg/export_key.py +++ b/borgmatic/borg/export_key.py @@ -29,8 +29,9 @@ def export_key( Raise FileExistsError if a path is given but it already exists on disk. ''' borgmatic.logger.add_custom_log_levels() - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') working_directory = borgmatic.config.paths.get_working_directory(config) extra_borg_options = config.get('extra_borg_options', {}).get('key_export', '') @@ -46,6 +47,7 @@ def export_key( full_command = ( (local_path, 'key', 'export') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + (('--log-json',) if output_file is None else ()) diff --git a/borgmatic/borg/export_tar.py b/borgmatic/borg/export_tar.py index 5028ce8e..9869b827 100644 --- a/borgmatic/borg/export_tar.py +++ b/borgmatic/borg/export_tar.py @@ -33,12 +33,14 @@ def export_tar_archive( If the destination path is "-", then stream the output to stdout instead of to a file. ''' borgmatic.logger.add_custom_log_levels() - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') extra_borg_options = config.get('extra_borg_options', {}).get('export_tar', '') full_command = ( (local_path, 'export-tar') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + (('--log-json',) if destination_path != '-' else ()) diff --git a/borgmatic/borg/extract.py b/borgmatic/borg/extract.py index 1765b744..49ee8230 100644 --- a/borgmatic/borg/extract.py +++ b/borgmatic/borg/extract.py @@ -24,6 +24,7 @@ def extract_last_archive_dry_run( Perform an extraction dry-run of the most recent archive. If there are no archives, skip the dry-run. ''' + archive_hostname = config.get('archive_hostname') extra_borg_options = config.get('extra_borg_options', {}).get('extract', '') verbosity_flags = () if logger.isEnabledFor(logging.DEBUG): @@ -48,6 +49,7 @@ def extract_last_archive_dry_run( list_flag = ('--list',) if logger.isEnabledFor(logging.DEBUG) else () full_extract_command = ( (local_path, 'extract', '--dry-run') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--log-json',) if not config.get('progress') else ()) + (('--lock-wait', str(lock_wait)) if lock_wait else ()) @@ -104,8 +106,9 @@ def extract_archive( If extract to stdout is True, then start the extraction streaming to stdout, and return that extract process as an instance of subprocess.Popen. ''' - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') extra_borg_options = config.get('extra_borg_options', {}).get('extract', '') if feature.available(feature.Feature.NUMERIC_IDS, local_borg_version): @@ -132,6 +135,7 @@ def extract_archive( full_command = ( (local_path, 'extract') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + numeric_ids_flags + (('--umask', str(umask)) if umask else ()) diff --git a/borgmatic/borg/import_key.py b/borgmatic/borg/import_key.py index 3a9595c7..c853e9b4 100644 --- a/borgmatic/borg/import_key.py +++ b/borgmatic/borg/import_key.py @@ -27,8 +27,9 @@ def import_key( Raise ValueError if the path is given and it does not exist. ''' - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') working_directory = borgmatic.config.paths.get_working_directory(config) extra_borg_options = config.get('extra_borg_options', {}).get('key_import', '') @@ -41,6 +42,7 @@ def import_key( full_command = ( (local_path, 'key', 'import') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + ('--log-json',) diff --git a/borgmatic/borg/info.py b/borgmatic/borg/info.py index 134d2f9c..0b3961cf 100644 --- a/borgmatic/borg/info.py +++ b/borgmatic/borg/info.py @@ -38,6 +38,7 @@ def make_info_command( if logger.isEnabledFor(logging.DEBUG) and not info_arguments.json else () ) + + flags.make_flags('hostname', config.get('archive_hostname')) + flags.make_flags('remote-path', remote_path) + flags.make_flags('umask', config.get('umask')) + ('--log-json',) diff --git a/borgmatic/borg/list.py b/borgmatic/borg/list.py index 45391548..9131a9b4 100644 --- a/borgmatic/borg/list.py +++ b/borgmatic/borg/list.py @@ -53,6 +53,7 @@ def make_list_command( if logger.isEnabledFor(logging.DEBUG) and not list_arguments.json else () ) + + flags.make_flags('hostname', config.get('archive_hostname')) + flags.make_flags('remote-path', remote_path) + flags.make_flags('umask', config.get('umask')) + ('--log-json',) diff --git a/borgmatic/borg/mount.py b/borgmatic/borg/mount.py index 9561bbe1..2e0354ab 100644 --- a/borgmatic/borg/mount.py +++ b/borgmatic/borg/mount.py @@ -24,12 +24,14 @@ def mount_archive( dict, the local Borg version, global arguments as an argparse.Namespace instance, and optional local and remote Borg paths, mount the archive onto the mount point. ''' - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') extra_borg_options = config.get('extra_borg_options', {}).get('mount', '') full_command = ( (local_path, 'mount') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + (('--log-json',) if not mount_arguments.foreground else ()) diff --git a/borgmatic/borg/prune.py b/borgmatic/borg/prune.py index 7789ee2e..68416897 100644 --- a/borgmatic/borg/prune.py +++ b/borgmatic/borg/prune.py @@ -65,14 +65,16 @@ def prune_archives( archives according to the retention policy specified in that configuration. ''' borgmatic.logger.add_custom_log_levels() - umask = config.get('umask', None) - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + umask = config.get('umask') + lock_wait = config.get('lock_wait') extra_borg_options = config.get('extra_borg_options', {}).get('prune', '') full_command = ( (local_path, 'prune') + make_prune_flags(config, prune_arguments, local_borg_version) + ('--log-json',) + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + (('--lock-wait', str(lock_wait)) if lock_wait else ()) diff --git a/borgmatic/borg/recreate.py b/borgmatic/borg/recreate.py index 598df637..6e801fe4 100644 --- a/borgmatic/borg/recreate.py +++ b/borgmatic/borg/recreate.py @@ -28,14 +28,15 @@ def recreate_archive( arguments, optional local and remote Borg paths, executes the recreate command with the given arguments. ''' - lock_wait = config.get('lock_wait', None) + archive_hostname = config.get('archive_hostname') + lock_wait = config.get('lock_wait') exclude_flags = flags.make_exclude_flags(config) - compression = config.get('compression', None) - chunker_params = config.get('chunker_params', None) + compression = config.get('compression') + chunker_params = config.get('chunker_params') extra_borg_options = config.get('extra_borg_options', {}).get('recreate', '') # Available recompress MODES: "if-different", "always", "never" (default) - recompress = config.get('recompress', None) + recompress = config.get('recompress') # Write patterns to a temporary file and use that file with --patterns-from. patterns_file = write_patterns_file( @@ -45,6 +46,7 @@ def recreate_archive( recreate_command = ( (local_path, 'recreate') + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + ('--log-json',) + (('--lock-wait', str(lock_wait)) if lock_wait is not None else ()) diff --git a/borgmatic/borg/rename.py b/borgmatic/borg/rename.py index 90af796a..ae7db381 100644 --- a/borgmatic/borg/rename.py +++ b/borgmatic/borg/rename.py @@ -24,6 +24,7 @@ def make_rename_command( + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ()) + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ()) + borgmatic.borg.flags.make_flags('dry-run', dry_run) + + borgmatic.borg.flags.make_flags('hostname', config.get('archive_hostname')) + borgmatic.borg.flags.make_flags('remote-path', remote_path) + borgmatic.borg.flags.make_flags('umask', config.get('umask')) + ('--log-json',) diff --git a/borgmatic/borg/repo_create.py b/borgmatic/borg/repo_create.py index fa36e14f..0ff84a9b 100644 --- a/borgmatic/borg/repo_create.py +++ b/borgmatic/borg/repo_create.py @@ -64,6 +64,7 @@ def create_repository( if error.returncode not in REPO_INFO_REPOSITORY_NOT_FOUND_EXIT_CODES: raise + archive_hostname = config.get('archive_hostname') lock_wait = config.get('lock_wait') umask = config.get('umask') extra_borg_options_from_init = config.get('extra_borg_options', {}).get('init', '') @@ -90,6 +91,7 @@ def create_repository( + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ()) + (('--debug',) if logger.isEnabledFor(logging.DEBUG) else ()) + (('--lock-wait', str(lock_wait)) if lock_wait else ()) + + (('--hostname', archive_hostname) if archive_hostname else ()) + (('--remote-path', remote_path) if remote_path else ()) + (('--umask', str(umask)) if umask else ()) + (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ()) diff --git a/borgmatic/borg/repo_delete.py b/borgmatic/borg/repo_delete.py index 5cb57bb9..262e7f2f 100644 --- a/borgmatic/borg/repo_delete.py +++ b/borgmatic/borg/repo_delete.py @@ -51,6 +51,7 @@ def make_repo_delete_command( + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ()) + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ()) + borgmatic.borg.flags.make_flags('dry-run', global_arguments.dry_run) + + borgmatic.borg.flags.make_flags('hostname', config.get('archive_hostname')) + borgmatic.borg.flags.make_flags('remote-path', remote_path) + borgmatic.borg.flags.make_flags('umask', config.get('umask')) + (('--log-json',) if output_file is None else ()) diff --git a/borgmatic/borg/repo_info.py b/borgmatic/borg/repo_info.py index 9924aa8d..93d3a6b2 100644 --- a/borgmatic/borg/repo_info.py +++ b/borgmatic/borg/repo_info.py @@ -24,7 +24,7 @@ def display_repository_info( information for the Borg repository or return JSON summary information. ''' borgmatic.logger.add_custom_log_levels() - lock_wait = config.get('lock_wait', None) + lock_wait = config.get('lock_wait') extra_borg_options = config.get('extra_borg_options', {}).get( 'repo_info' if feature.available(feature.Feature.REPO_INFO, local_borg_version) else 'info', '', @@ -48,6 +48,7 @@ def display_repository_info( if logger.isEnabledFor(logging.DEBUG) and not repo_info_arguments.json else () ) + + flags.make_flags('hostname', config.get('archive_hostname')) + flags.make_flags('remote-path', remote_path) + flags.make_flags('umask', config.get('umask')) + flags.make_flags('lock-wait', lock_wait) diff --git a/borgmatic/borg/repo_list.py b/borgmatic/borg/repo_list.py index 6ef4d7bd..458fc7c7 100644 --- a/borgmatic/borg/repo_list.py +++ b/borgmatic/borg/repo_list.py @@ -75,6 +75,7 @@ def get_latest_archive( if feature.available(feature.Feature.REPO_LIST, local_borg_version) else 'list' ), + *flags.make_flags('hostname', config.get('archive_hostname')), *flags.make_flags('remote-path', remote_path), *flags.make_flags('umask', config.get('umask')), *('--log-json',), @@ -160,6 +161,7 @@ def make_repo_list_command( if logger.isEnabledFor(logging.DEBUG) and not repo_list_arguments.json else () ) + + flags.make_flags('hostname', config.get('archive_hostname')) + flags.make_flags('remote-path', remote_path) + flags.make_flags('umask', config.get('umask')) + ('--log-json',) diff --git a/borgmatic/borg/transfer.py b/borgmatic/borg/transfer.py index 76bdef47..1e2f7767 100644 --- a/borgmatic/borg/transfer.py +++ b/borgmatic/borg/transfer.py @@ -31,6 +31,7 @@ def transfer_archives( (local_path, 'transfer') + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ()) + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ()) + + flags.make_flags('hostname', config.get('archive_hostname')) + flags.make_flags('remote-path', remote_path) + flags.make_flags('umask', config.get('umask')) + (('--log-json',) if (config.get('log_json') or not config.get('progress')) else ()) diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 6e936ab4..494d5944 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -511,13 +511,6 @@ properties: info, or check, borgmatic automatically tries to match only archives created with this name format. example: "{hostname}-documents-{now}" - archive_hostname: - type: string - description: | - Hostname to use when creating an archive, e.g. via the "{hostname}" - placeholder in "archive_name_format". Defaults to the system - hostname. (This option is supported for Borg 1.4.5+ only.) - example: example.org match_archives: type: string description: | @@ -528,6 +521,13 @@ properties: If match_archives is not specified, borgmatic defaults to deriving the match_archives value from archive_name_format. example: "sh:{hostname}-*" + archive_hostname: + type: string + description: | + Hostname to use for the "{hostname}" placeholder in + "archive_name_format", "match_archives", etc. Defaults to the system + hostname. (This option is supported for Borg 1.4.5+ only.) + example: example.org file_list_format: type: string description: | diff --git a/tests/unit/borg/test_borg.py b/tests/unit/borg/test_borg.py index 269b96e5..e93d1d46 100644 --- a/tests/unit/borg/test_borg.py +++ b/tests/unit/borg/test_borg.py @@ -188,12 +188,45 @@ def test_run_arbitrary_borg_with_exit_codes_calls_borg_using_them(): ) +def test_run_arbitrary_borg_with_archive_hostname_calls_borg_with_hostname_flags(): + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'hostname', 'example.org' + ).and_return( + ('--hostname', 'example.org'), + ) + flexmock(module.environment).should_receive('make_environment') + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module).should_receive('execute_command').with_args( + ('borg', 'break-lock', '--hostname', 'example.org', '::'), + output_file=module.borgmatic.execute.DO_NOT_CAPTURE, + shell=True, + environment={'BORG_REPO': 'repo', 'ARCHIVE': ''}, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ) + insert_logging_mock(logging.WARNING) + + module.run_arbitrary_borg( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + options=['break-lock', '::'], + ) + + def test_run_arbitrary_borg_with_remote_path_calls_borg_with_remote_path_flags(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER - flexmock(module.flags).should_receive('make_flags').and_return( + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'remote-path', 'borg1' + ).and_return( ('--remote-path', 'borg1'), - ).and_return(()) + ) flexmock(module.environment).should_receive('make_environment') flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) flexmock(module).should_receive('execute_command').with_args( @@ -219,9 +252,12 @@ def test_run_arbitrary_borg_with_remote_path_calls_borg_with_remote_path_flags() def test_run_arbitrary_borg_with_remote_path_injection_attack_gets_escaped(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER - flexmock(module.flags).should_receive('make_flags').and_return( + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'remote-path', 'borg1; naughty-command' + ).and_return( ('--remote-path', 'borg1; naughty-command'), - ).and_return(()) + ) flexmock(module.environment).should_receive('make_environment') flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) flexmock(module).should_receive('execute_command').with_args( @@ -240,7 +276,7 @@ def test_run_arbitrary_borg_with_remote_path_injection_attack_gets_escaped(): config={}, local_borg_version='1.2.3', options=['break-lock', '::'], - remote_path='borg1', + remote_path='borg1; naughty-command', ) diff --git a/tests/unit/borg/test_break_lock.py b/tests/unit/borg/test_break_lock.py index 1b43ca5f..8e065f53 100644 --- a/tests/unit/borg/test_break_lock.py +++ b/tests/unit/borg/test_break_lock.py @@ -62,6 +62,21 @@ def test_break_lock_calls_borg_using_exit_codes(): ) +def test_break_lock_calls_borg_with_hostname_flags(): + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + insert_execute_command_mock( + ('borg', 'break-lock', '--hostname', 'example.org', '--log-json', 'repo') + ) + insert_logging_mock(logging.WARNING) + + module.break_lock( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + global_arguments=flexmock(), + ) + + def test_break_lock_calls_borg_with_remote_path_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock( diff --git a/tests/unit/borg/test_change_passphrase.py b/tests/unit/borg/test_change_passphrase.py index 666c6be2..5c6f6250 100644 --- a/tests/unit/borg/test_change_passphrase.py +++ b/tests/unit/borg/test_change_passphrase.py @@ -81,6 +81,24 @@ def test_change_passphrase_calls_borg_using_exit_codes(): ) +def test_change_passphrase_calls_borg_with_hostname_flags(): + config = {'archive_hostname': 'example.org'} + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + insert_execute_command_mock( + ('borg', 'key', 'change-passphrase', '--hostname', 'example.org', 'repo'), + config=config, + ) + insert_logging_mock(logging.WARNING) + + module.change_passphrase( + repository_path='repo', + config=config, + local_borg_version='1.2.3', + change_passphrase_arguments=flexmock(), + global_arguments=flexmock(dry_run=False), + ) + + def test_change_passphrase_calls_borg_with_remote_path_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock( diff --git a/tests/unit/borg/test_check.py b/tests/unit/borg/test_check.py index 9e64cb1e..4352ccd9 100644 --- a/tests/unit/borg/test_check.py +++ b/tests/unit/borg/test_check.py @@ -895,6 +895,33 @@ def test_check_archives_with_exit_codes_calls_borg_using_them(): ) +def test_check_archives_with_archive_hostname_passes_through_to_borg(): + checks = {'repository'} + flexmock(module).should_receive('make_check_name_flags').with_args(checks, ()).and_return(()) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + insert_execute_command_mock( + ('borg', 'check', '--hostname', 'example.org', '--log-json', 'repo') + ) + insert_logging_mock(logging.WARNING) + + module.check_archives( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + check_arguments=flexmock( + progress=None, + repair=None, + only_checks=None, + force=None, + match_archives=None, + max_duration=None, + ), + global_arguments=flexmock(), + checks=checks, + archive_filter_flags=(), + ) + + def test_check_archives_with_remote_path_passes_through_to_borg(): checks = {'repository'} config = {} diff --git a/tests/unit/borg/test_compact.py b/tests/unit/borg/test_compact.py index 1b61960e..a04808d0 100644 --- a/tests/unit/borg/test_compact.py +++ b/tests/unit/borg/test_compact.py @@ -149,6 +149,22 @@ def test_compact_segments_with_exit_codes_calls_borg_using_them(): ) +def test_compact_segments_with_archive_hostname_calls_borg_with_hostname_flags(): + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + insert_execute_command_mock( + (*COMPACT_COMMAND, '--hostname', 'example.org', '--log-json', 'repo'), logging.INFO + ) + insert_logging_mock(logging.WARNING) + + module.compact_segments( + dry_run=False, + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + global_arguments=flexmock(), + ) + + def test_compact_segments_with_remote_path_calls_borg_with_remote_path_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock( diff --git a/tests/unit/borg/test_delete.py b/tests/unit/borg/test_delete.py index 506317ef..2e61570a 100644 --- a/tests/unit/borg/test_delete.py +++ b/tests/unit/borg/test_delete.py @@ -78,6 +78,32 @@ def test_make_delete_command_includes_dry_run(): assert command == ('borg', 'delete', '--dry-run', '--log-json', 'repo') +def test_make_delete_command_includes_hostname(): + flexmock(module.borgmatic.borg.flags).should_receive('make_flags').and_return(()) + flexmock(module.borgmatic.borg.flags).should_receive('make_flags').with_args( + 'hostname', + 'example.org', + ).and_return(('--hostname', 'example.org')) + flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(()) + flexmock(module.borgmatic.borg.flags).should_receive('make_flags_from_arguments').and_return(()) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('repo',), + ) + insert_logging_mock(logging.WARNING) + + command = module.make_delete_command( + repository={'path': 'repo'}, + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + delete_arguments=flexmock(list_details=False, force=0, match_archives=None, archive=None), + global_arguments=flexmock(dry_run=False), + local_path='borg', + remote_path='borg1', + ) + + assert command == ('borg', 'delete', '--hostname', 'example.org', '--log-json', 'repo') + + def test_make_delete_command_includes_remote_path(): flexmock(module.borgmatic.borg.flags).should_receive('make_flags').and_return(()) flexmock(module.borgmatic.borg.flags).should_receive('make_flags').with_args( diff --git a/tests/unit/borg/test_diff.py b/tests/unit/borg/test_diff.py index 70d3a7db..a4c73763 100644 --- a/tests/unit/borg/test_diff.py +++ b/tests/unit/borg/test_diff.py @@ -119,6 +119,62 @@ def test_diff_with_local_path_calls_borg_with_it(): ) +def test_diff_with_archive_hostname_calls_borg_with_it(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--hostname', + 'example.org', + '--log-json', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + insert_logging_mock(logging.WARNING) + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={'archive_hostname': 'example.org'}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + patterns=[], + ) + + def test_diff_with_remote_path_calls_borg_with_it(): flexmock(module.logging).ANSWER = LOGGING_ANSWER flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') diff --git a/tests/unit/borg/test_export_key.py b/tests/unit/borg/test_export_key.py index 51f487f1..37325635 100644 --- a/tests/unit/borg/test_export_key.py +++ b/tests/unit/borg/test_export_key.py @@ -79,6 +79,21 @@ def test_export_key_calls_borg_using_exit_codes(): ) +def test_export_key_calls_borg_with_hostname_flags(): + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + flexmock(module.os.path).should_receive('exists').never() + insert_execute_command_mock(('borg', 'key', 'export', '--hostname', 'example.org', 'repo')) + insert_logging_mock(logging.WARNING) + + module.export_key( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + export_arguments=flexmock(paper=False, qr_html=False, path=None), + global_arguments=flexmock(dry_run=False), + ) + + def test_export_key_calls_borg_with_remote_path_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.os.path).should_receive('exists').never() diff --git a/tests/unit/borg/test_export_tar.py b/tests/unit/borg/test_export_tar.py index 57e670fc..c128a8f0 100644 --- a/tests/unit/borg/test_export_tar.py +++ b/tests/unit/borg/test_export_tar.py @@ -103,6 +103,37 @@ def test_export_tar_archive_calls_borg_using_exit_codes(): ) +def test_export_tar_archive_calls_borg_with_hostname_flags(): + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER + flexmock(module.flags).should_receive('make_repository_archive_flags').and_return( + ('repo::archive',), + ) + insert_execute_command_mock( + ( + 'borg', + 'export-tar', + '--hostname', + 'example.org', + '--log-json', + 'repo::archive', + 'test.tar', + ), + ) + insert_logging_mock(logging.WARNING) + + module.export_tar_archive( + dry_run=False, + repository_path='repo', + archive='archive', + paths=None, + destination_path='test.tar', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + global_arguments=flexmock(), + ) + + def test_export_tar_archive_calls_borg_with_remote_path_flags(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER diff --git a/tests/unit/borg/test_extract.py b/tests/unit/borg/test_extract.py index 29bdc9e2..7852736a 100644 --- a/tests/unit/borg/test_extract.py +++ b/tests/unit/borg/test_extract.py @@ -161,6 +161,33 @@ def test_extract_last_archive_dry_run_calls_borg_using_exit_codes(): ) +def test_extract_last_archive_dry_run_calls_borg_with_hostname_flags(): + flexmock(module.repo_list).should_receive('resolve_archive_name').and_return('archive') + insert_execute_command_mock( + ( + 'borg', + 'extract', + '--dry-run', + '--hostname', + 'example.org', + '--log-json', + 'repo::archive', + ), + ) + flexmock(module.flags).should_receive('make_repository_archive_flags').and_return( + ('repo::archive',), + ) + insert_logging_mock(logging.WARNING) + + module.extract_last_archive_dry_run( + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + global_arguments=flexmock(), + repository_path='repo', + lock_wait=None, + ) + + def test_extract_last_archive_dry_run_calls_borg_with_remote_path_flags(): flexmock(module.repo_list).should_receive('resolve_archive_name').and_return('archive') insert_execute_command_mock( @@ -305,6 +332,32 @@ def test_extract_archive_calls_borg_with_exit_codes(): ) +def test_extract_archive_calls_borg_with_hostname_flags(): + flexmock(module.os.path).should_receive('abspath').and_return('repo') + insert_execute_command_mock( + ('borg', 'extract', '--hostname', 'example.org', '--log-json', 'repo::archive') + ) + flexmock(module.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.flags).should_receive('make_repository_archive_flags').and_return( + ('repo::archive',), + ) + flexmock(module.borgmatic.config.validate).should_receive( + 'normalize_repository_path', + ).and_return('repo') + insert_logging_mock(logging.WARNING) + + module.extract_archive( + dry_run=False, + repository='repo', + archive='archive', + paths=None, + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + global_arguments=flexmock(), + ) + + def test_extract_archive_calls_borg_with_remote_path_flags(): flexmock(module.os.path).should_receive('abspath').and_return('repo') insert_execute_command_mock( diff --git a/tests/unit/borg/test_import_key.py b/tests/unit/borg/test_import_key.py index 6aed2d67..cda50407 100644 --- a/tests/unit/borg/test_import_key.py +++ b/tests/unit/borg/test_import_key.py @@ -79,6 +79,24 @@ def test_import_key_calls_borg_using_exit_codes(): ) +def test_import_key_calls_borg_with_hostname_flags(): + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + flexmock(module.os.path).should_receive('exists').never() + insert_execute_command_mock( + ('borg', 'key', 'import', '--hostname', 'example.org', '--log-json', 'repo', '-') + ) + insert_logging_mock(logging.WARNING) + + module.import_key( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + import_arguments=flexmock(paper=False, path=None), + global_arguments=flexmock(dry_run=False), + ) + + def test_import_key_calls_borg_with_remote_path_flags(): flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) diff --git a/tests/unit/borg/test_info.py b/tests/unit/borg/test_info.py index 2ed8a07c..0a4c0fd8 100644 --- a/tests/unit/borg/test_info.py +++ b/tests/unit/borg/test_info.py @@ -208,6 +208,34 @@ def test_make_info_command_with_local_path_passes_through_to_command(): assert command == ('borg1', 'info', '--log-json', '--repo', 'repo') +def test_make_info_command_with_archive_hostname_passes_through_to_command(): + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'hostname', + 'example.org', + ).and_return(('--hostname', 'example.org')) + flexmock(module.flags).should_receive('make_match_archives_flags').with_args( + None, + None, + '2.3.4', + ).and_return(()) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) + insert_logging_mock(logging.WARNING) + + command = module.make_info_command( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='2.3.4', + global_arguments=flexmock(), + info_arguments=flexmock(archive=None, json=False, prefix=None, match_archives=None), + local_path='borg', + remote_path=None, + ) + + assert command == ('borg', 'info', '--hostname', 'example.org', '--log-json', '--repo', 'repo') + + def test_make_info_command_with_remote_path_passes_through_to_command(): flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').with_args( diff --git a/tests/unit/borg/test_list.py b/tests/unit/borg/test_list.py index 67ed518e..f02787ef 100644 --- a/tests/unit/borg/test_list.py +++ b/tests/unit/borg/test_list.py @@ -96,9 +96,10 @@ def test_make_list_command_includes_json(): def test_make_list_command_includes_lock_wait(): insert_logging_mock(logging.WARNING) - flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return( + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return( ('--lock-wait', '5'), - ).and_return(()) + ) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) @@ -205,6 +206,30 @@ def test_make_list_command_includes_local_path(): assert command == ('borg2', 'list', '--log-json', 'repo') +def test_make_list_command_includes_hostname(): + insert_logging_mock(logging.WARNING) + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'hostname', + 'example.org', + ).and_return(('--hostname', 'example.org')) + flexmock(module.flags).should_receive('make_flags').with_args('log-json', True).and_return( + ('--log-json'), + ) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + + command = module.make_list_command( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + list_arguments=flexmock(archive=None, paths=None, format=None, json=False), + global_arguments=flexmock(), + ) + + assert command == ('borg', 'list', '--hostname', 'example.org', '--log-json', 'repo') + + def test_make_list_command_includes_remote_path(): insert_logging_mock(logging.WARNING) flexmock(module.flags).should_receive('make_flags').and_return(()) diff --git a/tests/unit/borg/test_mount.py b/tests/unit/borg/test_mount.py index 6e67ce32..8c594dce 100644 --- a/tests/unit/borg/test_mount.py +++ b/tests/unit/borg/test_mount.py @@ -150,6 +150,27 @@ def test_mount_archive_calls_borg_using_exit_codes(): ) +def test_mount_archive_calls_borg_with_hostname_flags(): + flexmock(module.feature).should_receive('available').and_return(False) + flexmock(module.flags).should_receive('make_repository_archive_flags').and_return( + ('repo::archive',), + ) + insert_execute_command_mock( + ('borg', 'mount', '--hostname', 'example.org', '--log-json', 'repo::archive', '/mnt'), + ) + insert_logging_mock(logging.WARNING) + + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) + module.mount_archive( + repository_path='repo', + archive='archive', + mount_arguments=mount_arguments, + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + global_arguments=flexmock(), + ) + + def test_mount_archive_calls_borg_with_remote_path_flags(): flexmock(module.feature).should_receive('available').and_return(False) flexmock(module.flags).should_receive('make_repository_archive_flags').and_return( diff --git a/tests/unit/borg/test_prune.py b/tests/unit/borg/test_prune.py index 8ccf8a47..07d5e1e4 100644 --- a/tests/unit/borg/test_prune.py +++ b/tests/unit/borg/test_prune.py @@ -378,6 +378,29 @@ def test_prune_archives_with_exit_codes_calls_borg_using_them(): ) +def test_prune_archives_with_archive_hostname_calls_borg_with_remote_hostname_flags(): + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER + flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + flexmock(module.feature).should_receive('available').with_args( + module.feature.Feature.NO_PRUNE_STATS, + '1.2.3', + ).and_return(False) + insert_execute_command_mock((*PRUNE_COMMAND, '--hostname', 'example.org', 'repo'), logging.INFO) + insert_logging_mock(logging.WARNING) + + prune_arguments = flexmock(statistics=False, list_details=False) + module.prune_archives( + dry_run=False, + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + global_arguments=flexmock(), + prune_arguments=prune_arguments, + ) + + def test_prune_archives_with_remote_path_calls_borg_with_remote_path_flags(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER diff --git a/tests/unit/borg/test_recreate.py b/tests/unit/borg/test_recreate.py index 36db2466..679ca95b 100644 --- a/tests/unit/borg/test_recreate.py +++ b/tests/unit/borg/test_recreate.py @@ -92,6 +92,43 @@ def test_recreate_with_dry_run_calls_borg_with_dry_run_flag(): ) +def test_recreate_with_archive_hostname(): + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None) + flexmock(module.borgmatic.borg.flags).should_receive('make_list_filter_flags').and_return('') + flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(()) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive( + 'make_repository_archive_flags', + ).and_return( + ( + '--repo', + 'repo', + ), + ) + insert_execute_command_mock( + ('borg', 'recreate', '--hostname', 'example.org', '--log-json', '--repo', 'repo') + ) + insert_logging_mock(logging.WARNING) + + module.recreate_archive( + repository='repo', + archive='archive', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + recreate_arguments=flexmock( + list=None, + target=None, + comment=None, + timestamp=None, + match_archives=None, + ), + global_arguments=flexmock(dry_run=False), + local_path='borg', + patterns=None, + ) + + def test_recreate_with_remote_path(): flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return(None) diff --git a/tests/unit/borg/test_repo_create.py b/tests/unit/borg/test_repo_create.py index 92515671..981f8561 100644 --- a/tests/unit/borg/test_repo_create.py +++ b/tests/unit/borg/test_repo_create.py @@ -430,6 +430,30 @@ def test_create_repository_with_exit_codes_calls_borg_using_them(): ) +def test_create_repository_with_archive_hostname_calls_borg_with_hostname_flag(): + insert_repo_info_command_not_found_mock() + insert_repo_create_command_mock( + (*REPO_CREATE_COMMAND, '--hostname', 'example.org', '--repo', 'repo'), + ) + insert_logging_mock(logging.WARNING) + flexmock(module.feature).should_receive('available').and_return(True) + flexmock(module.flags).should_receive('make_repository_flags').and_return( + ( + '--repo', + 'repo', + ), + ) + + module.create_repository( + dry_run=False, + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='2.3.4', + global_arguments=flexmock(), + encryption_mode='repokey', + ) + + def test_create_repository_with_remote_path_calls_borg_with_remote_path_flag(): insert_repo_info_command_not_found_mock() insert_repo_create_command_mock( diff --git a/tests/unit/borg/test_repo_delete.py b/tests/unit/borg/test_repo_delete.py index c7968ad0..57509191 100644 --- a/tests/unit/borg/test_repo_delete.py +++ b/tests/unit/borg/test_repo_delete.py @@ -126,6 +126,33 @@ def test_make_repo_delete_command_includes_dry_run(): assert command == ('borg', 'repo-delete', '--dry-run', '--log-json', 'repo') +def test_make_repo_delete_command_includes_hostname(): + insert_logging_mock(logging.WARNING) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_flags').and_return(()) + flexmock(module.borgmatic.borg.flags).should_receive('make_flags').with_args( + 'hostname', + 'example.org', + ).and_return(('--hostname', 'example.org')) + flexmock(module.borgmatic.borg.flags).should_receive('make_flags_from_arguments').and_return(()) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('repo',), + ) + + command = module.make_repo_delete_command( + repository={'path': 'repo'}, + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + repo_delete_arguments=flexmock(list_details=False, force=0), + global_arguments=flexmock(dry_run=False), + local_path='borg', + remote_path=None, + output_file=None, + ) + + assert command == ('borg', 'repo-delete', '--hostname', 'example.org', '--log-json', 'repo') + + def test_make_repo_delete_command_includes_remote_path(): insert_logging_mock(logging.WARNING) flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) diff --git a/tests/unit/borg/test_repo_info.py b/tests/unit/borg/test_repo_info.py index ee8b6316..628faec2 100644 --- a/tests/unit/borg/test_repo_info.py +++ b/tests/unit/borg/test_repo_info.py @@ -364,6 +364,57 @@ def test_display_repository_info_with_exit_codes_calls_borg_using_them(): ) +def test_display_repository_info_with_archve_hostname_calls_borg_with_hostname_flags(): + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER + flexmock(module.feature).should_receive('available').and_return(True) + flexmock(module.flags).should_receive('make_flags').replace_with( + lambda name, value: (f'--{name}', value) if value else (), + ) + flexmock(module.flags).should_receive('make_repository_flags').and_return( + ( + '--repo', + 'repo', + ), + ) + flexmock(module.environment).should_receive('make_environment') + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module).should_receive('execute_command_and_capture_output').with_args( + ( + 'borg', + 'repo-info', + '--hostname', + 'example.org', + '--log-json', + '--json', + '--repo', + 'repo', + ), + environment=None, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).and_yield('[]') + flexmock(module.flags).should_receive('warn_for_aggressive_archive_flags') + flexmock(module).should_receive('execute_command').with_args( + ('borg', 'repo-info', '--hostname', 'example.org', '--log-json', '--repo', 'repo'), + output_log_level=module.borgmatic.logger.ANSWER, + environment=None, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ) + insert_logging_mock(logging.WARNING) + + module.display_repository_info( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='2.3.4', + repo_info_arguments=flexmock(json=False), + global_arguments=flexmock(), + ) + + def test_display_repository_info_with_remote_path_calls_borg_with_remote_path_flags(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER diff --git a/tests/unit/borg/test_repo_list.py b/tests/unit/borg/test_repo_list.py index 47b21c73..3308d9ac 100644 --- a/tests/unit/borg/test_repo_list.py +++ b/tests/unit/borg/test_repo_list.py @@ -265,6 +265,40 @@ def test_get_latest_archive_with_exit_codes_calls_borg_using_them(): ) +def test_get_latest_archive_with_archive_hostname_calls_borg_with_hostname_flags(): + expected_archive = {'name': 'archive-name', 'id': 'd34db33f'} + flexmock(module.feature).should_receive('available').and_return(False) + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'hostname', 'example.org' + ).and_return(('--hostname', 'example.org')) + flexmock(module.flags).should_receive('make_flags').with_args('last', 1).and_return( + ('--last', '1') + ) + flexmock(module.flags).should_receive('make_match_archives_flags').and_return(()) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + flexmock(module.environment).should_receive('make_environment') + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module).should_receive('execute_command_and_capture_output').with_args( + ('borg', 'list', '--hostname', 'example.org', '--log-json', *BORG_LIST_LATEST_ARGUMENTS), + environment=None, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).and_yield(json.dumps({'archives': [expected_archive]})) + insert_logging_mock(logging.WARNING) + + assert ( + module.get_latest_archive( + 'repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + global_arguments=flexmock(), + ) + == expected_archive + ) + + def test_get_latest_archive_with_remote_path_calls_borg_with_remote_path_flags(): expected_archive = {'name': 'archive-name', 'id': 'd34db33f'} flexmock(module.feature).should_receive('available').and_return(False) @@ -882,6 +916,38 @@ def test_make_repo_list_command_includes_local_path(): assert command == ('borg2', 'list', '--log-json', 'repo') +def test_make_repo_list_command_includes_hostname(): + flexmock(module.feature).should_receive('available').and_return(False) + flexmock(module.flags).should_receive('make_flags').replace_with( + lambda name, value: (f'--{name}', value) if value else (), + ) + flexmock(module.flags).should_receive('make_match_archives_flags').with_args( + None, + None, + '1.2.3', + ).and_return(()) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + insert_logging_mock(logging.WARNING) + + command = module.make_repo_list_command( + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='1.2.3', + repo_list_arguments=flexmock( + archive=None, + paths=None, + format=None, + json=False, + prefix=None, + match_archives=None, + ), + global_arguments=flexmock(), + ) + + assert command == ('borg', 'list', '--hostname', 'example.org', '--log-json', 'repo') + + def test_make_repo_list_command_includes_remote_path(): flexmock(module.feature).should_receive('available').and_return(False) flexmock(module.flags).should_receive('make_flags').replace_with( @@ -948,10 +1014,13 @@ def test_make_repo_list_command_includes_umask(): def test_make_repo_list_command_transforms_prefix_into_match_archives(): - flexmock(module.feature).should_receive('available').and_return(False) - flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return( + flexmock(module.feature).should_receive('available').and_return(True) + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'match-archives', 'sh:foo*' + ).and_return( ('--match-archives', 'sh:foo*'), - ).and_return(()) + ) flexmock(module.flags).should_receive('make_match_archives_flags').with_args( None, None, @@ -971,14 +1040,17 @@ def test_make_repo_list_command_transforms_prefix_into_match_archives(): global_arguments=flexmock(), ) - assert command == ('borg', 'list', '--log-json', '--match-archives', 'sh:foo*', 'repo') + assert command == ('borg', 'repo-list', '--log-json', '--match-archives', 'sh:foo*', 'repo') def test_make_repo_list_command_prefers_prefix_over_archive_name_format(): - flexmock(module.feature).should_receive('available').and_return(False) - flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return( + flexmock(module.feature).should_receive('available').and_return(True) + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'match-archives', 'sh:foo*' + ).and_return( ('--match-archives', 'sh:foo*'), - ).and_return(()) + ) flexmock(module.flags).should_receive('make_match_archives_flags').never() flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) @@ -994,7 +1066,7 @@ def test_make_repo_list_command_prefers_prefix_over_archive_name_format(): global_arguments=flexmock(), ) - assert command == ('borg', 'list', '--log-json', '--match-archives', 'sh:foo*', 'repo') + assert command == ('borg', 'repo-list', '--log-json', '--match-archives', 'sh:foo*', 'repo') def test_make_repo_list_command_transforms_archive_name_format_into_match_archives(): @@ -1029,7 +1101,8 @@ def test_make_repo_list_command_transforms_archive_name_format_into_match_archiv def test_make_repo_list_command_includes_format_from_command_line(): flexmock(module.feature).should_receive('available').and_return(False) - flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return( + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args('format', 'stuff').and_return( ('--format', 'stuff') ) flexmock(module.flags).should_receive('make_match_archives_flags').with_args( diff --git a/tests/unit/borg/test_transfer.py b/tests/unit/borg/test_transfer.py index 6d973525..62f0aa41 100644 --- a/tests/unit/borg/test_transfer.py +++ b/tests/unit/borg/test_transfer.py @@ -340,6 +340,45 @@ def test_transfer_archives_with_exit_codes_calls_borg_using_them(): ) +def test_transfer_archives_with_archive_hostname_calls_borg_with_hostname_flags(): + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags').with_args( + 'hostname', + 'example.org', + ).and_return(('--hostname', 'example.org')) + flexmock(module.flags).should_receive('make_match_archives_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) + flexmock(module.environment).should_receive('make_environment') + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module).should_receive('execute_command').with_args( + ('borg', 'transfer', '--hostname', 'example.org', '--log-json', '--repo', 'repo'), + output_log_level=module.borgmatic.logger.ANSWER, + output_file=None, + environment=None, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ) + insert_logging_mock(logging.WARNING) + + module.transfer_archives( + dry_run=False, + repository_path='repo', + config={'archive_hostname': 'example.org'}, + local_borg_version='2.3.4', + transfer_arguments=flexmock( + archive=None, + progress=None, + match_archives=None, + source_repository=None, + ), + global_arguments=flexmock(), + ) + + def test_transfer_archives_with_remote_path_calls_borg_with_remote_path_flags(): flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER