Fix for a regression in the ZFS, LVM, and Btrfs hooks in which partial excludes of snapshot paths were ignored (#1169).

This commit is contained in:
2025-10-26 14:12:52 -07:00
parent 51d2ce7963
commit 0f7ebcb4b7
11 changed files with 212 additions and 12 deletions

2
NEWS
View File

@@ -3,6 +3,8 @@
contain multiple paths.
* #1168: Fix for the "list", "info", and "delete" options in "extra_borg_options" being ignored
when "--archive" is omitted with Borg 1.x.
* #1169: Fix for a regression in the ZFS, LVM, and Btrfs hooks in which partial excludes of
snapshot paths were ignored.
* Add a "rename" option to "extra_borg_options" to support passing arbitrary flags to "borg
rename".

View File

@@ -329,9 +329,15 @@ def dump_data_sources(
snapshot_subvolume(btrfs_command, subvolume.path, snapshot_path)
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
patterns, subvolume.contained_patterns
)
for pattern in subvolume.contained_patterns:
snapshot_pattern = make_borg_snapshot_pattern(subvolume.path, pattern)
borgmatic.hooks.data_source.config.replace_pattern(patterns, pattern, snapshot_pattern)
borgmatic.hooks.data_source.config.replace_pattern(
patterns, pattern, snapshot_pattern, last_contained_pattern_index
)
borgmatic.hooks.data_source.config.inject_pattern(
patterns, make_snapshot_exclude_pattern(subvolume.path)

View File

@@ -1,3 +1,4 @@
import contextlib
import json
import logging
import shutil
@@ -131,7 +132,23 @@ def inject_pattern(patterns, data_source_pattern):
patterns.insert(0, data_source_pattern)
def replace_pattern(patterns, pattern_to_replace, data_source_pattern):
def get_last_pattern_index(patterns, patterns_subset):
'''
Given a sequence of all patterns and a subset of those patterns, find each subset pattern in the
all patterns sequence and return the highest (last) index.
'''
last_pattern_index = 0
for pattern in patterns_subset:
with contextlib.suppress(ValueError):
last_pattern_index = max(patterns.index(pattern), last_pattern_index)
return last_pattern_index
def replace_pattern(
patterns, pattern_to_replace, data_source_pattern, last_contained_pattern_index
):
'''
Given a list of borgmatic.borg.pattern.Pattern instances representing the configured patterns,
replace the given pattern with the given data source pattern. The idea is that borgmatic is
@@ -139,8 +156,32 @@ def replace_pattern(patterns, pattern_to_replace, data_source_pattern):
that the hook's data gets included in the backup.
As part of this replacement, if the data source pattern is a root pattern, also insert an
"include" version of the given root pattern right after the replaced pattern, in an attempt to
preempt any of the user's configured exclude patterns that may follow.
"include" version of the given root pattern right after the given last contained pattern index
in an attempt to preempt any of the user's configured global exclude patterns that may follow.
But we don't want to preempt any intentional partial excludes of the data source pattern itself,
which is why the include goes after the last contained pattern index.
For instance, let's say that the patterns are effectively:
R /foo
R /bar
- /bar/.cache
R /baz
- **
... and "R /bar" is the pattern to replace, data source pattern is "R /bar/snapshot", and the
last contained pattern index is 2 (corresponding to "- /bar/.cache"). The resulting patterns
after calling this function would be:
R /foo
R /bar/snapshot
- /bar/snapshot/.cache
+ /bar/snapshot
R /baz
- **
Note that the positioning of "+ /bar/snapshot" means that it overrides the "- **" global exclude
but not the "- /bar/snapshot/.cache" contained pattern exclude.
If the pattern to replace can't be found in the given patterns, then just inject the data source
pattern at the start of the list.
@@ -156,7 +197,7 @@ def replace_pattern(patterns, pattern_to_replace, data_source_pattern):
if data_source_pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT:
patterns.insert(
index + 1,
last_contained_pattern_index + 1,
borgmatic.borg.pattern.Pattern(
path=data_source_pattern.path,
type=borgmatic.borg.pattern.Pattern_type.INCLUDE,

View File

@@ -269,6 +269,10 @@ def dump_data_sources(
snapshot_mount_path,
)
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
patterns, logical_volume.contained_patterns
)
for pattern in logical_volume.contained_patterns:
snapshot_pattern = make_borg_snapshot_pattern(
pattern,
@@ -276,7 +280,9 @@ def dump_data_sources(
normalized_runtime_directory,
)
borgmatic.hooks.data_source.config.replace_pattern(patterns, pattern, snapshot_pattern)
borgmatic.hooks.data_source.config.replace_pattern(
patterns, pattern, snapshot_pattern, last_contained_pattern_index
)
return []

View File

@@ -300,6 +300,10 @@ def dump_data_sources(
snapshot_mount_path,
)
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
patterns, dataset.contained_patterns
)
for pattern in dataset.contained_patterns:
snapshot_pattern = make_borg_snapshot_pattern(
pattern,
@@ -307,7 +311,9 @@ def dump_data_sources(
normalized_runtime_directory,
)
borgmatic.hooks.data_source.config.replace_pattern(patterns, pattern, snapshot_pattern)
borgmatic.hooks.data_source.config.replace_pattern(
patterns, pattern, snapshot_pattern, last_contained_pattern_index
)
return []

View File

@@ -5,11 +5,22 @@ from borgmatic.hooks.data_source import btrfs as module
def test_dump_data_sources_snapshots_each_subvolume_and_updates_patterns():
patterns = [Pattern('/foo'), Pattern('/mnt/subvol1'), Pattern('/mnt/subvol2')]
patterns = [
Pattern('/foo'),
Pattern('/mnt/subvol1'),
Pattern('/mnt/subvol1/.cache', Pattern_type.EXCLUDE),
Pattern('/mnt/subvol2'),
]
config = {'btrfs': {}}
flexmock(module).should_receive('get_subvolumes').and_return(
(
module.Subvolume('/mnt/subvol1', contained_patterns=(Pattern('/mnt/subvol1'),)),
module.Subvolume(
'/mnt/subvol1',
contained_patterns=(
Pattern('/mnt/subvol1'),
Pattern('/mnt/subvol1/.cache', Pattern_type.EXCLUDE),
),
),
module.Subvolume('/mnt/subvol2', contained_patterns=(Pattern('/mnt/subvol2'),)),
),
)
@@ -50,6 +61,7 @@ def test_dump_data_sources_snapshots_each_subvolume_and_updates_patterns():
),
Pattern('/foo'),
Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1'),
Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1/.cache', Pattern_type.EXCLUDE),
Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1', Pattern_type.INCLUDE),
Pattern('/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2'),
Pattern('/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2', Pattern_type.INCLUDE),

View File

@@ -6,13 +6,20 @@ from borgmatic.hooks.data_source import lvm as module
def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
config = {'lvm': {}}
patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
patterns = [
Pattern('/mnt/lvolume1/subdir'),
Pattern('/mnt/lvolume1/subdir/.cache', Pattern_type.EXCLUDE),
Pattern('/mnt/lvolume2'),
]
logical_volumes = (
module.Logical_volume(
name='lvolume1',
device_path='/dev/lvolume1',
mount_point='/mnt/lvolume1',
contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
contained_patterns=(
Pattern('/mnt/lvolume1/subdir'),
Pattern('/mnt/lvolume1/subdir/.cache', Pattern_type.EXCLUDE),
),
),
module.Logical_volume(
name='lvolume2',
@@ -75,6 +82,9 @@ def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
assert patterns == [
Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir'),
Pattern(
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir/.cache', Pattern_type.EXCLUDE
),
Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir', Pattern_type.INCLUDE),
Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'),
Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2', Pattern_type.INCLUDE),

View File

@@ -450,6 +450,9 @@ def test_dump_data_sources_snapshots_each_subvolume_and_replaces_patterns():
'/mnt/subvol2',
object,
).and_return(Pattern('/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/subvol1'),
@@ -457,6 +460,7 @@ def test_dump_data_sources_snapshots_each_subvolume_and_replaces_patterns():
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
@@ -465,6 +469,7 @@ def test_dump_data_sources_snapshots_each_subvolume_and_replaces_patterns():
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
object,
@@ -527,6 +532,9 @@ def test_dump_data_sources_uses_custom_btrfs_command_in_commands():
'/mnt/subvol1',
object,
).and_return(Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/subvol1'),
@@ -534,6 +542,7 @@ def test_dump_data_sources_uses_custom_btrfs_command_in_commands():
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
object,
@@ -594,6 +603,9 @@ def test_dump_data_sources_with_findmnt_command_warns():
'/mnt/subvol1',
object,
).and_return(Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/subvol1'),
@@ -601,6 +613,7 @@ def test_dump_data_sources_with_findmnt_command_warns():
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
object,
@@ -641,6 +654,9 @@ def test_dump_data_sources_with_dry_run_skips_snapshot_and_patterns_update():
)
flexmock(module).should_receive('snapshot_subvolume').never()
flexmock(module).should_receive('make_snapshot_exclude_pattern').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').never()
@@ -666,6 +682,9 @@ def test_dump_data_sources_without_matching_subvolumes_skips_snapshot_and_patter
flexmock(module).should_receive('make_snapshot_path').never()
flexmock(module).should_receive('snapshot_subvolume').never()
flexmock(module).should_receive('make_snapshot_exclude_pattern').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').never()

View File

@@ -203,6 +203,52 @@ def test_inject_pattern_with_root_pattern_prepends_it_along_with_corresponding_i
]
def test_get_last_pattern_index_with_ordered_subset_patterns_finds_last_one():
patterns = [
module.borgmatic.borg.pattern.Pattern('/foo'),
module.borgmatic.borg.pattern.Pattern('/bar'),
module.borgmatic.borg.pattern.Pattern('/baz'),
module.borgmatic.borg.pattern.Pattern('/quux'),
]
patterns_subset = [
module.borgmatic.borg.pattern.Pattern('/bar'),
module.borgmatic.borg.pattern.Pattern('/baz'),
]
assert module.get_last_pattern_index(patterns, patterns_subset) == 2
def test_get_last_pattern_index_with_unordered_subset_patterns_finds_last_one():
patterns = [
module.borgmatic.borg.pattern.Pattern('/foo'),
module.borgmatic.borg.pattern.Pattern('/bar'),
module.borgmatic.borg.pattern.Pattern('/baz'),
module.borgmatic.borg.pattern.Pattern('/quux'),
]
patterns_subset = [
module.borgmatic.borg.pattern.Pattern('/baz'),
module.borgmatic.borg.pattern.Pattern('/bar'),
]
assert module.get_last_pattern_index(patterns, patterns_subset) == 2
def test_get_last_pattern_index_with_unknown_subset_patterns_skips_it():
patterns = [
module.borgmatic.borg.pattern.Pattern('/foo'),
module.borgmatic.borg.pattern.Pattern('/bar'),
module.borgmatic.borg.pattern.Pattern('/baz'),
module.borgmatic.borg.pattern.Pattern('/quux'),
]
patterns_subset = [
module.borgmatic.borg.pattern.Pattern('/baz'),
module.borgmatic.borg.pattern.Pattern('/unknown'),
module.borgmatic.borg.pattern.Pattern('/bar'),
]
assert module.get_last_pattern_index(patterns, patterns_subset) == 2
def test_replace_pattern_swaps_out_pattern_in_place():
patterns = [
module.borgmatic.borg.pattern.Pattern('/etc'),
@@ -217,6 +263,7 @@ def test_replace_pattern_swaps_out_pattern_in_place():
'/foo/bar',
type=module.borgmatic.borg.pattern.Pattern_type.EXCLUDE,
),
0,
)
assert patterns == [
@@ -243,6 +290,7 @@ def test_replace_pattern_with_unknown_pattern_falls_back_to_injecting():
patterns,
module.borgmatic.borg.pattern.Pattern('/unknown'),
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
0,
)
@@ -251,20 +299,23 @@ def test_replace_pattern_with_root_pattern_swaps_it_in_along_with_corresponding_
module.borgmatic.borg.pattern.Pattern('/etc'),
module.borgmatic.borg.pattern.Pattern('/var'),
module.borgmatic.borg.pattern.Pattern('/lib'),
module.borgmatic.borg.pattern.Pattern('/run'),
]
module.replace_pattern(
patterns,
module.borgmatic.borg.pattern.Pattern('/var'),
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
2,
)
assert patterns == [
module.borgmatic.borg.pattern.Pattern('/etc'),
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
module.borgmatic.borg.pattern.Pattern('/lib'),
module.borgmatic.borg.pattern.Pattern(
'/foo/bar',
type=module.borgmatic.borg.pattern.Pattern_type.INCLUDE,
),
module.borgmatic.borg.pattern.Pattern('/lib'),
module.borgmatic.borg.pattern.Pattern('/run'),
]

View File

@@ -360,6 +360,9 @@ def test_dump_data_sources_snapshots_and_mounts_and_replaces_patterns():
logical_volumes[1],
'/run/borgmatic',
).and_return(Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/lvolume1/subdir'),
@@ -367,6 +370,7 @@ def test_dump_data_sources_snapshots_and_mounts_and_replaces_patterns():
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
@@ -375,6 +379,7 @@ def test_dump_data_sources_snapshots_and_mounts_and_replaces_patterns():
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
assert (
@@ -396,6 +401,9 @@ def test_dump_data_sources_with_no_logical_volumes_skips_snapshots():
flexmock(module).should_receive('get_logical_volumes').and_return(())
flexmock(module).should_receive('snapshot_logical_volume').never()
flexmock(module).should_receive('mount_snapshot').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
assert (
@@ -477,6 +485,9 @@ def test_dump_data_sources_uses_snapshot_size_for_snapshot():
logical_volumes[1],
'/run/borgmatic',
).and_return(Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/lvolume1/subdir'),
@@ -484,6 +495,7 @@ def test_dump_data_sources_uses_snapshot_size_for_snapshot():
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
@@ -492,6 +504,7 @@ def test_dump_data_sources_uses_snapshot_size_for_snapshot():
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
assert (
@@ -580,6 +593,9 @@ def test_dump_data_sources_uses_custom_commands():
logical_volumes[1],
'/run/borgmatic',
).and_return(Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/lvolume1/subdir'),
@@ -587,6 +603,7 @@ def test_dump_data_sources_uses_custom_commands():
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
@@ -595,6 +612,7 @@ def test_dump_data_sources_uses_custom_commands():
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
assert (
@@ -633,6 +651,9 @@ def test_dump_data_sources_with_dry_run_skips_snapshots_and_does_not_touch_patte
flexmock(module).should_receive('snapshot_logical_volume').never()
flexmock(module).should_receive('get_snapshots').never()
flexmock(module).should_receive('mount_snapshot').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
assert (
@@ -714,6 +735,9 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
logical_volumes[1],
'/run/borgmatic',
).and_return(Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/lvolume1/subdir'),
@@ -721,6 +745,7 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
@@ -729,6 +754,7 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
assert (
@@ -785,6 +811,9 @@ def test_dump_data_sources_with_missing_snapshot_errors():
snapshot_name='lvolume2_borgmatic-1234',
).never()
flexmock(module).should_receive('mount_snapshot').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
with pytest.raises(ValueError):

View File

@@ -328,6 +328,9 @@ def test_dump_data_sources_snapshots_and_mounts_and_replaces_patterns():
'/run/borgmatic',
).and_return(Pattern('/run/borgmatic/zfs_snapshots/b33f/./mnt/dataset/subdir'))
patterns = [Pattern('/mnt/dataset/subdir')]
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/dataset/subdir'),
@@ -335,6 +338,7 @@ def test_dump_data_sources_snapshots_and_mounts_and_replaces_patterns():
'/run/borgmatic/zfs_snapshots/b33f/./mnt/dataset/subdir',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
assert (
@@ -355,6 +359,9 @@ def test_dump_data_sources_with_no_datasets_skips_snapshots():
flexmock(module.os).should_receive('getpid').and_return(1234)
flexmock(module).should_receive('snapshot_dataset').never()
flexmock(module).should_receive('mount_snapshot').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
patterns = [Pattern('/mnt/dataset')]
@@ -400,6 +407,9 @@ def test_dump_data_sources_uses_custom_commands():
dataset,
'/run/borgmatic',
).and_return(Pattern('/run/borgmatic/zfs_snapshots/b33f/./mnt/dataset/subdir'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/dataset/subdir'),
@@ -407,6 +417,7 @@ def test_dump_data_sources_uses_custom_commands():
'/run/borgmatic/zfs_snapshots/b33f/./mnt/dataset/subdir',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
patterns = [Pattern('/mnt/dataset/subdir')]
hook_config = {
@@ -437,6 +448,9 @@ def test_dump_data_sources_with_dry_run_skips_commands_and_does_not_touch_patter
flexmock(module.os).should_receive('getpid').and_return(1234)
flexmock(module).should_receive('snapshot_dataset').never()
flexmock(module).should_receive('mount_snapshot').never()
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
patterns = [Pattern('/mnt/dataset')]
@@ -480,6 +494,9 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
dataset,
'/run/borgmatic',
).and_return(Pattern('/run/borgmatic/zfs_snapshots/b33f/./mnt/dataset/subdir'))
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
'get_last_pattern_index'
).and_return(0)
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
object,
Pattern('/mnt/dataset/subdir'),
@@ -487,6 +504,7 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
'/run/borgmatic/zfs_snapshots/b33f/./mnt/dataset/subdir',
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
),
0,
).once()
patterns = [Pattern('/hmm')]