Просмотр исходного кода

Fix for the database "restore" action restoring more databases than the "--database" flag specifies (#1208).

Dan Helfman 5 дней назад
Родитель
Сommit
b8642439cd

+ 4 - 0
NEWS

@@ -1,3 +1,7 @@
+2.0.14.dev0
+ * #1208: Fix for the database "restore" action restoring more databases than the "--database" flag
+   specifies.
+
 2.0.13
 2.0.13
  * #1054: Allow the Btrfs hook to create and delete snapshots even when running
  * #1054: Allow the Btrfs hook to create and delete snapshots even when running
    as a non-root user. See the documentation for more information:
    as a non-root user. See the documentation for more information:

+ 4 - 0
borgmatic/actions/restore.py

@@ -200,6 +200,10 @@ def restore_single_dump(
         borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
         borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
         borgmatic_runtime_directory,
         borgmatic_runtime_directory,
         data_source['name'],
         data_source['name'],
+        data_source.get('hostname'),
+        data_source.get('port'),
+        data_source.get('container'),
+        data_source.get('label'),
     )[hook_name.split('_databases', 1)[0]]
     )[hook_name.split('_databases', 1)[0]]
 
 
     destination_path = (
     destination_path = (

+ 4 - 0
borgmatic/hooks/data_source/bootstrap.py

@@ -116,6 +116,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Restores are implemented via the separate, purpose-specific "bootstrap" action rather than the
     Restores are implemented via the separate, purpose-specific "bootstrap" action rather than the

+ 4 - 0
borgmatic/hooks/data_source/btrfs.py

@@ -412,6 +412,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Restores aren't implemented, because stored files can be extracted directly with "extract".
     Restores aren't implemented, because stored files can be extracted directly with "extract".

+ 1 - 1
borgmatic/hooks/data_source/dump.py

@@ -124,6 +124,6 @@ def convert_glob_patterns_to_borg_pattern(patterns):
     # longer than the pattern. E.g., a pattern of "borgmatic/*/foo_databases/test" should also match
     # longer than the pattern. E.g., a pattern of "borgmatic/*/foo_databases/test" should also match
     # paths like "borgmatic/*/foo_databases/test/toc.dat"
     # paths like "borgmatic/*/foo_databases/test/toc.dat"
     return 're:' + '|'.join(
     return 're:' + '|'.join(
-        fnmatch.translate(pattern.lstrip('/')).replace('\\z', '').replace('\\Z', '')
+        fnmatch.translate(pattern.lstrip('/')).replace('\\z', '').replace('\\Z', '') + '$'
         for pattern in patterns
         for pattern in patterns
     )
     )

+ 4 - 0
borgmatic/hooks/data_source/lvm.py

@@ -452,6 +452,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Restores aren't implemented, because stored files can be extracted directly with "extract".
     Restores aren't implemented, because stored files can be extracted directly with "extract".

+ 15 - 3
borgmatic/hooks/data_source/mariadb.py

@@ -410,6 +410,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
@@ -419,16 +423,24 @@ def make_data_source_dump_patterns(
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
 
 
     return (
     return (
-        dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, label='*'),
+        dump.make_data_source_dump_filename(
+            make_dump_path('borgmatic'), name, hostname, port, container, label
+        ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_runtime_directory),
             make_dump_path(borgmatic_runtime_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_source_directory),
             make_dump_path(borgmatic_source_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
     )
     )
 
 

+ 15 - 3
borgmatic/hooks/data_source/mongodb.py

@@ -212,6 +212,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
@@ -221,16 +225,24 @@ def make_data_source_dump_patterns(
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
 
 
     return (
     return (
-        dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, label='*'),
+        dump.make_data_source_dump_filename(
+            make_dump_path('borgmatic'), name, hostname, port, container, label
+        ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_runtime_directory),
             make_dump_path(borgmatic_runtime_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_source_directory),
             make_dump_path(borgmatic_source_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
     )
     )
 
 

+ 15 - 3
borgmatic/hooks/data_source/mysql.py

@@ -341,6 +341,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
@@ -350,16 +354,24 @@ def make_data_source_dump_patterns(
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
 
 
     return (
     return (
-        dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, label='*'),
+        dump.make_data_source_dump_filename(
+            make_dump_path('borgmatic'), name, name, hostname, port, container, label
+        ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_runtime_directory),
             make_dump_path(borgmatic_runtime_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_source_directory),
             make_dump_path(borgmatic_source_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
     )
     )
 
 

+ 15 - 3
borgmatic/hooks/data_source/postgresql.py

@@ -302,6 +302,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
@@ -311,16 +315,24 @@ def make_data_source_dump_patterns(
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
 
 
     return (
     return (
-        dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, label='*'),
+        dump.make_data_source_dump_filename(
+            make_dump_path('borgmatic'), name, hostname, port, container, label
+        ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_runtime_directory),
             make_dump_path(borgmatic_runtime_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_source_directory),
             make_dump_path(borgmatic_source_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
     )
     )
 
 

+ 15 - 3
borgmatic/hooks/data_source/sqlite.py

@@ -144,6 +144,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
     Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
@@ -153,16 +157,24 @@ def make_data_source_dump_patterns(
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
     borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
 
 
     return (
     return (
-        dump.make_data_source_dump_filename(make_dump_path('borgmatic'), name, label='*'),
+        dump.make_data_source_dump_filename(
+            make_dump_path('borgmatic'), name, hostname, port, container, label
+        ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_runtime_directory),
             make_dump_path(borgmatic_runtime_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
         dump.make_data_source_dump_filename(
         dump.make_data_source_dump_filename(
             make_dump_path(borgmatic_source_directory),
             make_dump_path(borgmatic_source_directory),
             name,
             name,
-            label='*',
+            hostname,
+            port,
+            container,
+            label,
         ),
         ),
     )
     )
 
 

+ 4 - 0
borgmatic/hooks/data_source/zfs.py

@@ -453,6 +453,10 @@ def make_data_source_dump_patterns(
     config,
     config,
     borgmatic_runtime_directory,
     borgmatic_runtime_directory,
     name=None,
     name=None,
+    hostname=None,
+    port=None,
+    container=None,
+    label=None,
 ):  # pragma: no cover
 ):  # pragma: no cover
     '''
     '''
     Restores aren't implemented, because stored files can be extracted directly with "extract".
     Restores aren't implemented, because stored files can be extracted directly with "extract".

+ 1 - 1
pyproject.toml

@@ -1,6 +1,6 @@
 [project]
 [project]
 name = "borgmatic"
 name = "borgmatic"
-version = "2.0.13"
+version = "2.0.14.dev0"
 authors = [
 authors = [
   { name="Dan Helfman", email="witten@torsion.org" },
   { name="Dan Helfman", email="witten@torsion.org" },
 ]
 ]

+ 16 - 0
tests/unit/actions/test_restore.py

@@ -315,6 +315,10 @@ def test_restore_single_dump_extracts_and_restores_single_file_dump():
         object,
         object,
         object,
         object,
         object,
         object,
+        object,
+        object,
+        object,
+        object,
     ).and_return({'postgresql': flexmock()})
     ).and_return({'postgresql': flexmock()})
     flexmock(module.tempfile).should_receive('mkdtemp').never()
     flexmock(module.tempfile).should_receive('mkdtemp').never()
     flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
     flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
@@ -359,6 +363,10 @@ def test_restore_single_dump_extracts_and_restores_directory_dump():
         object,
         object,
         object,
         object,
         object,
         object,
+        object,
+        object,
+        object,
+        object,
     ).and_return({'postgresql': flexmock()})
     ).and_return({'postgresql': flexmock()})
     flexmock(module.tempfile).should_receive('mkdtemp').once().and_return(
     flexmock(module.tempfile).should_receive('mkdtemp').once().and_return(
         '/run/user/0/borgmatic/tmp1234',
         '/run/user/0/borgmatic/tmp1234',
@@ -405,6 +413,10 @@ def test_restore_single_dump_with_directory_dump_error_cleans_up_temporary_direc
         object,
         object,
         object,
         object,
         object,
         object,
+        object,
+        object,
+        object,
+        object,
     ).and_return({'postgresql': flexmock()})
     ).and_return({'postgresql': flexmock()})
     flexmock(module.tempfile).should_receive('mkdtemp').once().and_return(
     flexmock(module.tempfile).should_receive('mkdtemp').once().and_return(
         '/run/user/0/borgmatic/tmp1234',
         '/run/user/0/borgmatic/tmp1234',
@@ -452,6 +464,10 @@ def test_restore_single_dump_with_directory_dump_and_dry_run_skips_directory_mov
         object,
         object,
         object,
         object,
         object,
         object,
+        object,
+        object,
+        object,
+        object,
     ).and_return({'postgresql': flexmock()})
     ).and_return({'postgresql': flexmock()})
     flexmock(module.tempfile).should_receive('mkdtemp').once().and_return('/run/borgmatic/tmp1234')
     flexmock(module.tempfile).should_receive('mkdtemp').once().and_return('/run/borgmatic/tmp1234')
     flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
     flexmock(module.borgmatic.hooks.data_source.dump).should_receive(

+ 1 - 1
tests/unit/hooks/data_source/test_dump.py

@@ -158,5 +158,5 @@ def test_remove_data_source_dumps_without_dump_path_present_skips_removal():
 def test_convert_glob_patterns_to_borg_pattern_makes_multipart_regular_expression():
 def test_convert_glob_patterns_to_borg_pattern_makes_multipart_regular_expression():
     assert (
     assert (
         module.convert_glob_patterns_to_borg_pattern(('/etc/foo/bar', '/bar/*/baz'))
         module.convert_glob_patterns_to_borg_pattern(('/etc/foo/bar', '/bar/*/baz'))
-        == 're:(?s:etc/foo/bar)|(?s:bar/.*/baz)'
+        == 're:(?s:etc/foo/bar)$|(?s:bar/.*/baz)$'
     )
     )