瀏覽代碼

Fix error when configured source directories are not present on the filesystem at the time of backup (#387).

Dan Helfman 3 年之前
父節點
當前提交
449896f661
共有 3 個文件被更改,包括 37 次插入4 次删除
  1. 2 0
      NEWS
  2. 10 4
      borgmatic/borg/create.py
  3. 25 0
      tests/unit/borg/test_create.py

+ 2 - 0
NEWS

@@ -1,4 +1,6 @@
 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.
  * Move Gitea and GitHub hosting from a personal namespace to an organization for better
    collaboration with related projects.

+ 10 - 4
borgmatic/borg/create.py

@@ -44,13 +44,18 @@ def _expand_home_directories(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
-    which that directory resides. This is handy for determining whether two different directories
-    are on the same filesystem (have the same device identifier).
+    which that directory resides or None if the path doesn't exist.
+
+    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):
@@ -82,6 +87,7 @@ def deduplicate_directories(directory_devices):
             for parent in parents:
                 if (
                     pathlib.PurePath(other_directory) == parent
+                    and directory_devices[directory] is not None
                     and directory_devices[other_directory] == directory_devices[directory]
                 ):
                     if directory in deduplicated:

+ 25 - 0
tests/unit/borg/test_create.py

@@ -60,6 +60,30 @@ def test_expand_home_directories_considers_none_as_no_directories():
     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(
     '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': 2}, ('/root', '/root/foo')),
         ({'/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, '/root/foo': 1, '/root/foo/bar': 1}, ('/root',)),
         ({'/dup': 1, '/dup': 1}, ('/dup',)),