Merge branch 'main' into config-command-line.

This commit is contained in:
Dan Helfman
2025-03-31 17:13:20 -07:00
6 changed files with 348 additions and 79 deletions
+2
View File
@@ -29,6 +29,8 @@
"working_directory" are used.
* #1044: Fix an error in the systemd credential hook when the credential name contains a "."
character.
* #1048: Fix a "no such file or directory" error in ZFS, Btrfs, and LVM hooks with nested
directories that reside on separate devices/filesystems.
1.9.14
* #409: With the PagerDuty monitoring hook, send borgmatic logs to PagerDuty so they show up in the
+17 -10
View File
@@ -2,6 +2,7 @@ import logging
import shlex
import borgmatic.borg.environment
import borgmatic.borg.feature
import borgmatic.config.paths
import borgmatic.execute
from borgmatic.borg import flags
@@ -66,19 +67,25 @@ def recreate_archive(
+ (('--timestamp', recreate_arguments.timestamp) if recreate_arguments.timestamp else ())
+ (('--compression', compression) if compression else ())
+ (('--chunker-params', chunker_params) if chunker_params else ())
+ (
flags.make_match_archives_flags(
archive or config.get('match_archives'),
config.get('archive_name_format'),
local_borg_version,
)
)
+ (('--recompress', recompress) if recompress else ())
+ exclude_flags
+ (
flags.make_repository_archive_flags(repository, archive, local_borg_version)
if archive
else flags.make_repository_flags(repository, local_borg_version)
(
flags.make_repository_flags(repository, local_borg_version)
+ flags.make_match_archives_flags(
archive or config.get('match_archives'),
config.get('archive_name_format'),
local_borg_version,
)
)
if borgmatic.borg.feature.available(
borgmatic.borg.feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, local_borg_version
)
else (
flags.make_repository_archive_flags(repository, archive, local_borg_version)
if archive
else flags.make_repository_flags(repository, local_borg_version)
)
)
)
+1 -1
View File
@@ -1813,7 +1813,7 @@ def make_parsers(schema, unparsed_arguments):
'--glob-archives',
dest='match_archives',
metavar='PATTERN',
help='Only consider archive names, hashes, or series matching this pattern',
help='Only consider archive names, hashes, or series matching this pattern [Borg 2.x+ only]',
)
recreate_group.add_argument(
'-h', '--help', action='help', help='Show this help message and exit'
+8
View File
@@ -1,3 +1,4 @@
import os
import pathlib
IS_A_HOOK = False
@@ -11,6 +12,10 @@ def get_contained_patterns(parent_directory, candidate_patterns):
paths, but there's a parent directory (logical volume, dataset, subvolume, etc.) at /var, then
/var is what we want to snapshot.
If a parent directory and a candidate pattern are on different devices, skip the pattern. That's
because any snapshot of a parent directory won't actually include "contained" directories if
they reside on separate devices.
For this function to work, a candidate pattern path can't have any globs or other non-literal
characters in the initial portion of the path that matches the parent directory. For instance, a
parent directory of /var would match a candidate pattern path of /var/log/*/data, but not a
@@ -27,6 +32,8 @@ def get_contained_patterns(parent_directory, candidate_patterns):
if not candidate_patterns:
return ()
parent_device = os.stat(parent_directory).st_dev if os.path.exists(parent_directory) else None
contained_patterns = tuple(
candidate
for candidate in candidate_patterns
@@ -35,6 +42,7 @@ def get_contained_patterns(parent_directory, candidate_patterns):
pathlib.PurePath(parent_directory) == candidate_path
or pathlib.PurePath(parent_directory) in candidate_path.parents
)
if candidate.device == parent_device
)
candidate_patterns -= set(contained_patterns)
+234 -56
View File
@@ -25,9 +25,15 @@ def test_recreate_archive_dry_run_skips_execution():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
).and_return(
(
'--repo',
'repo',
)
)
flexmock(module.borgmatic.execute).should_receive('execute_command').never()
recreate_arguments = flexmock(
@@ -57,10 +63,16 @@ def test_recreate_calls_borg_with_required_flags():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -86,10 +98,16 @@ def test_recreate_with_remote_path():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', '--remote-path', 'borg1', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--remote-path', 'borg1', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -115,10 +133,16 @@ def test_recreate_with_lock_wait():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', '--lock-wait', '5', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--lock-wait', '5', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -143,10 +167,16 @@ def test_recreate_with_log_info():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', '--info', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--info', '--repo', 'repo'))
insert_logging_mock(logging.INFO)
@@ -173,10 +203,16 @@ def test_recreate_with_log_debug():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', '--debug', '--show-rc', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--debug', '--show-rc', '--repo', 'repo'))
insert_logging_mock(logging.DEBUG)
module.recreate_archive(
@@ -202,10 +238,16 @@ def test_recreate_with_log_json():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', '--log-json', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--log-json', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -229,12 +271,18 @@ def test_recreate_with_list_config_calls_borg_with_list_flag():
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
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::archive',))
).and_return(
(
'--repo',
'repo',
)
)
flexmock(module).should_receive('make_list_filter_flags').and_return('AME+-')
insert_execute_command_mock(
('borg', 'recreate', '--list', '--filter', 'AME+-', 'repo::archive')
('borg', 'recreate', '--list', '--filter', 'AME+-', '--repo', 'repo')
)
module.recreate_archive(
@@ -259,13 +307,19 @@ def test_recreate_with_patterns_from_flag():
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
flexmock(module.borgmatic.borg.create).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::archive',))
).and_return(
(
'--repo',
'repo',
)
)
mock_patterns_file = flexmock(name='patterns_file')
flexmock(module).should_receive('write_patterns_file').and_return(mock_patterns_file)
insert_execute_command_mock(
('borg', 'recreate', '--patterns-from', 'patterns_file', 'repo::archive')
('borg', 'recreate', '--patterns-from', 'patterns_file', '--repo', 'repo')
)
module.recreate_archive(
@@ -290,11 +344,17 @@ def test_recreate_with_exclude_flags():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
).and_return(
(
'--repo',
'repo',
)
)
flexmock(module).should_receive('make_exclude_flags').and_return(('--exclude', 'pattern'))
insert_execute_command_mock(('borg', 'recreate', '--exclude', 'pattern', 'repo::archive'))
insert_execute_command_mock(('borg', 'recreate', '--exclude', 'pattern', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -319,10 +379,16 @@ def test_recreate_with_target_flag():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', '--target', 'new-archive', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--target', 'new-archive', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -347,11 +413,17 @@ def test_recreate_with_comment_flag():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(
('borg', 'recreate', '--comment', shlex.quote('This is a test comment'), 'repo::archive')
('borg', 'recreate', '--comment', shlex.quote('This is a test comment'), '--repo', 'repo')
)
module.recreate_archive(
@@ -377,11 +449,17 @@ def test_recreate_with_timestamp_flag():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(
('borg', 'recreate', '--timestamp', '2023-10-01T12:00:00', 'repo::archive')
('borg', 'recreate', '--timestamp', '2023-10-01T12:00:00', '--repo', 'repo')
)
module.recreate_archive(
@@ -407,10 +485,16 @@ def test_recreate_with_compression_flag():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', '--compression', 'lz4', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--compression', 'lz4', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -435,11 +519,17 @@ def test_recreate_with_chunker_params_flag():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(
('borg', 'recreate', '--chunker-params', '19,23,21,4095', 'repo::archive')
('borg', 'recreate', '--chunker-params', '19,23,21,4095', '--repo', 'repo')
)
module.recreate_archive(
@@ -465,10 +555,16 @@ def test_recreate_with_recompress_flag():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', '--recompress', 'always', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--recompress', 'always', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -493,10 +589,16 @@ def test_recreate_with_match_archives_star():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -521,10 +623,16 @@ def test_recreate_with_match_archives_regex():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -549,10 +657,16 @@ def test_recreate_with_match_archives_shell():
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).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::archive',))
insert_execute_command_mock(('borg', 'recreate', 'repo::archive'))
).and_return(
(
'--repo',
'repo',
)
)
insert_execute_command_mock(('borg', 'recreate', '--repo', 'repo'))
module.recreate_archive(
repository='repo',
@@ -572,22 +686,24 @@ def test_recreate_with_match_archives_shell():
)
def test_recreate_with_glob_archives_flag():
def test_recreate_with_match_archives_and_feature_available_calls_borg_with_match_archives():
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(
('--glob-archives', 'foo-*')
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').with_args(
'foo-*', None, '1.2.3'
).and_return(('--match-archives', 'foo-*'))
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'
).and_return(('repo::archive',))
insert_execute_command_mock(('borg', 'recreate', '--glob-archives', 'foo-*', 'repo::archive'))
flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never()
insert_execute_command_mock(('borg', 'recreate', '--repo', 'repo', '--match-archives', 'foo-*'))
module.recreate_archive(
repository='repo',
archive='archive',
config={},
archive=None,
config={'match_archives': 'foo-*'},
local_borg_version='1.2.3',
recreate_arguments=flexmock(
list=None,
@@ -602,31 +718,93 @@ def test_recreate_with_glob_archives_flag():
)
def test_recreate_with_match_archives_flag():
def test_recreate_with_archives_flag_and_feature_available_calls_borg_with_match_archives():
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(
('--match-archives', 'sh:foo-*')
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').with_args(
'archive', None, '1.2.3'
).and_return(('--match-archives', 'archive'))
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'
).and_return(('--repo', 'repo', 'archive'))
flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never()
insert_execute_command_mock(
('borg', 'recreate', '--match-archives', 'sh:foo-*', '--repo', 'repo', 'archive')
('borg', 'recreate', '--repo', 'repo', '--match-archives', 'archive')
)
module.recreate_archive(
repository='repo',
archive='archive',
config={},
local_borg_version='2.0.0b3',
config={'match_archives': 'foo-*'},
local_borg_version='1.2.3',
recreate_arguments=flexmock(
list=None,
target=None,
comment=None,
timestamp=None,
match_archives='sh:foo-*',
match_archives='foo-*',
),
global_arguments=flexmock(dry_run=False, log_json=False),
local_path='borg',
patterns=None,
)
def test_recreate_with_match_archives_and_feature_not_available_calls_borg_without_match_archives():
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').never()
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(False)
flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return(
('repo',)
)
flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never()
insert_execute_command_mock(('borg', 'recreate', 'repo'))
module.recreate_archive(
repository='repo',
archive=None,
config={'match_archives': 'foo-*'},
local_borg_version='1.2.3',
recreate_arguments=flexmock(
list=None,
target=None,
comment=None,
timestamp=None,
match_archives='foo-*',
),
global_arguments=flexmock(dry_run=False, log_json=False),
local_path='borg',
patterns=None,
)
def test_recreate_with_archives_flags_and_feature_not_available_calls_borg_with_combined_repo_and_archive():
flexmock(module.borgmatic.borg.create).should_receive('make_exclude_flags').and_return(())
flexmock(module.borgmatic.borg.create).should_receive('write_patterns_file').and_return(None)
flexmock(module.borgmatic.borg.create).should_receive('make_list_filter_flags').and_return('')
flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').never()
flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(False)
flexmock(module.borgmatic.borg.flags).should_receive(
'make_repository_archive_flags'
).and_return(('repo::archive',))
flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').never()
insert_execute_command_mock(('borg', 'recreate', 'repo::archive'))
module.recreate_archive(
repository='repo',
archive='archive',
config={'match_archives': 'foo-*'},
local_borg_version='1.2.3',
recreate_arguments=flexmock(
list=None,
target=None,
comment=None,
timestamp=None,
match_archives='foo-*',
),
global_arguments=flexmock(dry_run=False, log_json=False),
local_path='borg',
+86 -12
View File
@@ -1,34 +1,108 @@
from flexmock import flexmock
from borgmatic.borg.pattern import Pattern
from borgmatic.hooks.data_source import snapshot as module
def test_get_contained_patterns_without_candidates_returns_empty():
flexmock(module.os).should_receive('stat').and_return(flexmock(st_dev=flexmock()))
flexmock(module.os.path).should_receive('exists').and_return(True)
assert module.get_contained_patterns('/mnt', {}) == ()
def test_get_contained_patterns_with_self_candidate_returns_self():
candidates = {Pattern('/foo'), Pattern('/mnt'), Pattern('/bar')}
device = flexmock()
flexmock(module.os).should_receive('stat').and_return(flexmock(st_dev=device))
flexmock(module.os.path).should_receive('exists').and_return(True)
candidates = {
Pattern('/foo', device=device),
Pattern('/mnt', device=device),
Pattern('/bar', device=device),
}
assert module.get_contained_patterns('/mnt', candidates) == (Pattern('/mnt'),)
assert candidates == {Pattern('/foo'), Pattern('/bar')}
assert module.get_contained_patterns('/mnt', candidates) == (Pattern('/mnt', device=device),)
assert candidates == {Pattern('/foo', device=device), Pattern('/bar', device=device)}
def test_get_contained_patterns_with_self_candidate_and_caret_prefix_returns_self():
candidates = {Pattern('^/foo'), Pattern('^/mnt'), Pattern('^/bar')}
device = flexmock()
flexmock(module.os).should_receive('stat').and_return(flexmock(st_dev=device))
flexmock(module.os.path).should_receive('exists').and_return(True)
candidates = {
Pattern('^/foo', device=device),
Pattern('^/mnt', device=device),
Pattern('^/bar', device=device),
}
assert module.get_contained_patterns('/mnt', candidates) == (Pattern('^/mnt'),)
assert candidates == {Pattern('^/foo'), Pattern('^/bar')}
assert module.get_contained_patterns('/mnt', candidates) == (Pattern('^/mnt', device=device),)
assert candidates == {Pattern('^/foo', device=device), Pattern('^/bar', device=device)}
def test_get_contained_patterns_with_child_candidate_returns_child():
candidates = {Pattern('/foo'), Pattern('/mnt/subdir'), Pattern('/bar')}
device = flexmock()
flexmock(module.os).should_receive('stat').and_return(flexmock(st_dev=device))
flexmock(module.os.path).should_receive('exists').and_return(True)
candidates = {
Pattern('/foo', device=device),
Pattern('/mnt/subdir', device=device),
Pattern('/bar', device=device),
}
assert module.get_contained_patterns('/mnt', candidates) == (Pattern('/mnt/subdir'),)
assert candidates == {Pattern('/foo'), Pattern('/bar')}
assert module.get_contained_patterns('/mnt', candidates) == (
Pattern('/mnt/subdir', device=device),
)
assert candidates == {Pattern('/foo', device=device), Pattern('/bar', device=device)}
def test_get_contained_patterns_with_grandchild_candidate_returns_child():
candidates = {Pattern('/foo'), Pattern('/mnt/sub/dir'), Pattern('/bar')}
device = flexmock()
flexmock(module.os).should_receive('stat').and_return(flexmock(st_dev=device))
flexmock(module.os.path).should_receive('exists').and_return(True)
candidates = {
Pattern('/foo', device=device),
Pattern('/mnt/sub/dir', device=device),
Pattern('/bar', device=device),
}
assert module.get_contained_patterns('/mnt', candidates) == (Pattern('/mnt/sub/dir'),)
assert candidates == {Pattern('/foo'), Pattern('/bar')}
assert module.get_contained_patterns('/mnt', candidates) == (
Pattern('/mnt/sub/dir', device=device),
)
assert candidates == {Pattern('/foo', device=device), Pattern('/bar', device=device)}
def test_get_contained_patterns_ignores_child_candidate_on_another_device():
one_device = flexmock()
another_device = flexmock()
flexmock(module.os).should_receive('stat').and_return(flexmock(st_dev=one_device))
flexmock(module.os.path).should_receive('exists').and_return(True)
candidates = {
Pattern('/foo', device=one_device),
Pattern('/mnt/subdir', device=another_device),
Pattern('/bar', device=one_device),
}
assert module.get_contained_patterns('/mnt', candidates) == ()
assert candidates == {
Pattern('/foo', device=one_device),
Pattern('/mnt/subdir', device=another_device),
Pattern('/bar', device=one_device),
}
def test_get_contained_patterns_with_non_existent_parent_directory_ignores_child_candidate():
device = flexmock()
flexmock(module.os).should_receive('stat').and_return(flexmock(st_dev=device))
flexmock(module.os.path).should_receive('exists').and_return(False)
candidates = {
Pattern('/foo', device=device),
Pattern('/mnt/subdir', device=device),
Pattern('/bar', device=device),
}
assert module.get_contained_patterns('/mnt', candidates) == ()
assert candidates == {
Pattern('/foo', device=device),
Pattern('/mnt/subdir', device=device),
Pattern('/bar', device=device),
}