Browse Source

Fix a borgmatic runtime directory error when running the "spot" check with a database hook enabled (#965).

Dan Helfman 4 months ago
parent
commit
5560f30aa6
3 changed files with 40 additions and 12 deletions
  1. 2 0
      NEWS
  2. 9 7
      borgmatic/borg/create.py
  3. 29 5
      tests/unit/borg/test_create.py

+ 2 - 0
NEWS

@@ -4,6 +4,8 @@
  * #960: Fix for archives storing relative source directory paths such that they contain the working
  * #960: Fix for archives storing relative source directory paths such that they contain the working
    directory.
    directory.
  * #960: Fix the "spot" check to support relative source directory paths.
  * #960: Fix the "spot" check to support relative source directory paths.
+ * #965: Fix a borgmatic runtime directory error when running the "spot" check with a database hook
+   enabled.
  * Fix the "spot" check to no longer consider pipe files within an archive for file comparisons.
  * Fix the "spot" check to no longer consider pipe files within an archive for file comparisons.
  * Fix auto-excluding of special files (when databases are configured) to support relative source
  * Fix auto-excluding of special files (when databases are configured) to support relative source
    directory paths.
    directory paths.

+ 9 - 7
borgmatic/borg/create.py

@@ -161,6 +161,7 @@ def any_parent_directories(path, candidate_parents):
 
 
 
 
 def collect_special_file_paths(
 def collect_special_file_paths(
+    dry_run,
     create_command,
     create_command,
     config,
     config,
     local_path,
     local_path,
@@ -169,11 +170,11 @@ def collect_special_file_paths(
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
 ):
 ):
     '''
     '''
-    Given a Borg create command as a tuple, a configuration dict, a local Borg path, a working
-    directory, a dict of environment variables to pass to Borg, and the borgmatic runtime directory,
-    collect the paths for any special files (character devices, block devices, and named pipes /
-    FIFOs) that Borg would encounter during a create. These are all paths that could cause Borg to
-    hang if its --read-special flag is used.
+    Given a dry-run flag, a Borg create command as a tuple, a configuration dict, a local Borg path,
+    a working directory, a dict of environment variables to pass to Borg, and the borgmatic runtime
+    directory, collect the paths for any special files (character devices, block devices, and named
+    pipes / FIFOs) that Borg would encounter during a create. These are all paths that could cause
+    Borg to hang if its --read-special flag is used.
 
 
     Skip looking for special files in the given borgmatic runtime directory, as borgmatic creates
     Skip looking for special files in the given borgmatic runtime directory, as borgmatic creates
     its own special files there for database dumps. And if the borgmatic runtime directory is
     its own special files there for database dumps. And if the borgmatic runtime directory is
@@ -204,9 +205,9 @@ def collect_special_file_paths(
             path for path in paths if any_parent_directories(path, (borgmatic_runtime_directory,))
             path for path in paths if any_parent_directories(path, (borgmatic_runtime_directory,))
         }
         }
 
 
-        if not skip_paths:
+        if not skip_paths and not dry_run:
             raise ValueError(
             raise ValueError(
-                f'The runtime directory {os.path.normpath(borgmatic_runtime_directory)} overlaps with the configured excludes or patterns. Please remove it from excludes/patterns or change the runtime directory.'
+                f'The runtime directory {os.path.normpath(borgmatic_runtime_directory)} overlaps with the configured excludes or patterns with excludes. Please ensure the runtime directory is not excluded.'
             )
             )
 
 
     return tuple(
     return tuple(
@@ -350,6 +351,7 @@ def make_base_create_command(
 
 
         logger.debug(f'{repository_path}: Collecting special file paths')
         logger.debug(f'{repository_path}: Collecting special file paths')
         special_file_paths = collect_special_file_paths(
         special_file_paths = collect_special_file_paths(
+            dry_run,
             create_flags + create_positional_arguments,
             create_flags + create_positional_arguments,
             config,
             config,
             local_path,
             local_path,

+ 29 - 5
tests/unit/borg/test_create.py

@@ -290,7 +290,8 @@ def test_collect_special_file_paths_parses_special_files_from_borg_dry_run_file_
     flexmock(module).should_receive('any_parent_directories').never()
     flexmock(module).should_receive('any_parent_directories').never()
 
 
     assert module.collect_special_file_paths(
     assert module.collect_special_file_paths(
-        ('borg', 'create'),
+        dry_run=False,
+        create_command=('borg', 'create'),
         config={},
         config={},
         local_path=None,
         local_path=None,
         working_directory=None,
         working_directory=None,
@@ -316,7 +317,8 @@ def test_collect_special_file_paths_skips_borgmatic_runtime_directory():
     ).and_return(False)
     ).and_return(False)
 
 
     assert module.collect_special_file_paths(
     assert module.collect_special_file_paths(
-        ('borg', 'create'),
+        dry_run=False,
+        create_command=('borg', 'create'),
         config={},
         config={},
         local_path=None,
         local_path=None,
         working_directory=None,
         working_directory=None,
@@ -335,7 +337,8 @@ def test_collect_special_file_paths_with_borgmatic_runtime_directory_missing_fro
 
 
     with pytest.raises(ValueError):
     with pytest.raises(ValueError):
         module.collect_special_file_paths(
         module.collect_special_file_paths(
-            ('borg', 'create'),
+            dry_run=False,
+            create_command=('borg', 'create'),
             config={},
             config={},
             local_path=None,
             local_path=None,
             working_directory=None,
             working_directory=None,
@@ -344,6 +347,25 @@ def test_collect_special_file_paths_with_borgmatic_runtime_directory_missing_fro
         )
         )
 
 
 
 
+def test_collect_special_file_paths_with_dry_run_and_borgmatic_runtime_directory_missing_from_paths_output_does_not_raise():
+    flexmock(module).should_receive('execute_command_and_capture_output').and_return(
+        '+ /foo\n- /bar\n- /baz'
+    )
+    flexmock(module).should_receive('special_file').and_return(True)
+    flexmock(module.os.path).should_receive('exists').and_return(True)
+    flexmock(module).should_receive('any_parent_directories').and_return(False)
+
+    assert module.collect_special_file_paths(
+        dry_run=True,
+        create_command=('borg', 'create'),
+        config={},
+        local_path=None,
+        working_directory=None,
+        borg_environment=None,
+        borgmatic_runtime_directory='/run/borgmatic',
+    ) == ('/foo', '/bar', '/baz')
+
+
 def test_collect_special_file_paths_excludes_non_special_files():
 def test_collect_special_file_paths_excludes_non_special_files():
     flexmock(module).should_receive('execute_command_and_capture_output').and_return(
     flexmock(module).should_receive('execute_command_and_capture_output').and_return(
         '+ /foo\n+ /bar\n+ /baz'
         '+ /foo\n+ /bar\n+ /baz'
@@ -355,7 +377,8 @@ def test_collect_special_file_paths_excludes_non_special_files():
     flexmock(module).should_receive('any_parent_directories').never()
     flexmock(module).should_receive('any_parent_directories').never()
 
 
     assert module.collect_special_file_paths(
     assert module.collect_special_file_paths(
-        ('borg', 'create'),
+        dry_run=False,
+        create_command=('borg', 'create'),
         config={},
         config={},
         local_path=None,
         local_path=None,
         working_directory=None,
         working_directory=None,
@@ -378,7 +401,8 @@ def test_collect_special_file_paths_omits_exclude_no_dump_flag_from_command():
     flexmock(module).should_receive('any_parent_directories').never()
     flexmock(module).should_receive('any_parent_directories').never()
 
 
     module.collect_special_file_paths(
     module.collect_special_file_paths(
-        ('borg', 'create', '--exclude-nodump'),
+        dry_run=False,
+        create_command=('borg', 'create', '--exclude-nodump'),
         config={},
         config={},
         local_path='borg',
         local_path='borg',
         working_directory=None,
         working_directory=None,