Fix error when configured source directories are not present on the filesystem at the time of backup (#387).
This commit is contained in:
parent
1004500d65
commit
449896f661
3 changed files with 37 additions and 4 deletions
2
NEWS
2
NEWS
|
@ -1,4 +1,6 @@
|
||||||
1.5.19.dev0
|
1.5.19.dev0
|
||||||
|
* #387: Fix error when configured source directories are not present on the filesystem at the time
|
||||||
|
of backup. Now, Borg will complain, but the backup will still continue.
|
||||||
* Update sample systemd service file with more granular read-only filesystem settings.
|
* Update sample systemd service file with more granular read-only filesystem settings.
|
||||||
* Move Gitea and GitHub hosting from a personal namespace to an organization for better
|
* Move Gitea and GitHub hosting from a personal namespace to an organization for better
|
||||||
collaboration with related projects.
|
collaboration with related projects.
|
||||||
|
|
|
@ -44,13 +44,18 @@ def _expand_home_directories(directories):
|
||||||
return tuple(os.path.expanduser(directory) for directory in directories)
|
return tuple(os.path.expanduser(directory) for directory in directories)
|
||||||
|
|
||||||
|
|
||||||
def map_directories_to_devices(directories): # pragma: no cover
|
def map_directories_to_devices(directories):
|
||||||
'''
|
'''
|
||||||
Given a sequence of directories, return a map from directory to an identifier for the device on
|
Given a sequence of directories, return a map from directory to an identifier for the device on
|
||||||
which that directory resides. This is handy for determining whether two different directories
|
which that directory resides or None if the path doesn't exist.
|
||||||
are on the same filesystem (have the same device identifier).
|
|
||||||
|
This is handy for determining whether two different directories are on the same filesystem (have
|
||||||
|
the same device identifier).
|
||||||
'''
|
'''
|
||||||
return {directory: os.stat(directory).st_dev for directory in directories}
|
return {
|
||||||
|
directory: os.stat(directory).st_dev if os.path.exists(directory) else None
|
||||||
|
for directory in directories
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def deduplicate_directories(directory_devices):
|
def deduplicate_directories(directory_devices):
|
||||||
|
@ -82,6 +87,7 @@ def deduplicate_directories(directory_devices):
|
||||||
for parent in parents:
|
for parent in parents:
|
||||||
if (
|
if (
|
||||||
pathlib.PurePath(other_directory) == parent
|
pathlib.PurePath(other_directory) == parent
|
||||||
|
and directory_devices[directory] is not None
|
||||||
and directory_devices[other_directory] == directory_devices[directory]
|
and directory_devices[other_directory] == directory_devices[directory]
|
||||||
):
|
):
|
||||||
if directory in deduplicated:
|
if directory in deduplicated:
|
||||||
|
|
|
@ -60,6 +60,30 @@ def test_expand_home_directories_considers_none_as_no_directories():
|
||||||
assert paths == ()
|
assert paths == ()
|
||||||
|
|
||||||
|
|
||||||
|
def test_map_directories_to_devices_gives_device_id_per_path():
|
||||||
|
flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
|
||||||
|
flexmock(module.os).should_receive('stat').with_args('/bar').and_return(flexmock(st_dev=66))
|
||||||
|
|
||||||
|
device_map = module.map_directories_to_devices(('/foo', '/bar'))
|
||||||
|
|
||||||
|
assert device_map == {
|
||||||
|
'/foo': 55,
|
||||||
|
'/bar': 66,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_map_directories_to_devices_with_missing_path_does_not_error():
|
||||||
|
flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
|
||||||
|
flexmock(module.os).should_receive('stat').with_args('/bar').and_raise(FileNotFoundError)
|
||||||
|
|
||||||
|
device_map = module.map_directories_to_devices(('/foo', '/bar'))
|
||||||
|
|
||||||
|
assert device_map == {
|
||||||
|
'/foo': 55,
|
||||||
|
'/bar': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'directories,expected_directories',
|
'directories,expected_directories',
|
||||||
(
|
(
|
||||||
|
@ -72,6 +96,7 @@ def test_expand_home_directories_considers_none_as_no_directories():
|
||||||
({'/root': 1, '/root/foo/': 1}, ('/root',)),
|
({'/root': 1, '/root/foo/': 1}, ('/root',)),
|
||||||
({'/root': 1, '/root/foo': 2}, ('/root', '/root/foo')),
|
({'/root': 1, '/root/foo': 2}, ('/root', '/root/foo')),
|
||||||
({'/root/foo': 1, '/root': 1}, ('/root',)),
|
({'/root/foo': 1, '/root': 1}, ('/root',)),
|
||||||
|
({'/root': None, '/root/foo': None}, ('/root', '/root/foo')),
|
||||||
({'/root': 1, '/etc': 1, '/root/foo/bar': 1}, ('/etc', '/root')),
|
({'/root': 1, '/etc': 1, '/root/foo/bar': 1}, ('/etc', '/root')),
|
||||||
({'/root': 1, '/root/foo': 1, '/root/foo/bar': 1}, ('/root',)),
|
({'/root': 1, '/root/foo': 1, '/root/foo/bar': 1}, ('/root',)),
|
||||||
({'/dup': 1, '/dup': 1}, ('/dup',)),
|
({'/dup': 1, '/dup': 1}, ('/dup',)),
|
||||||
|
|
Loading…
Reference in a new issue