Fix for the "restore" action sometimes failing to find a database dump that was dumped with a default port (#1322).

This commit is contained in:
Dan Helfman
2026-06-15 11:56:04 -07:00
parent fa099b8471
commit beaeea25b3
9 changed files with 510 additions and 68 deletions
+2
View File
@@ -3,6 +3,8 @@
action. Borg >= 1.4.5 and < 2 only.
* #1319: Fix the ZFS hook's overzealous unmounting of snapshot paths when a source dataset is at
"/".
* #1322: Fix for the "restore" action sometimes failing to find a database dump that was dumped
with a default port.
* Fix a bug in which the "compact" action does not pass a compact threshold of zero to Borg.
2.1.6
+47 -17
View File
@@ -422,7 +422,7 @@ def make_data_source_dump_patterns(
port=None,
container=None,
label=None,
): # pragma: no cover
):
'''
Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
and a database name to match, return the corresponding glob patterns to match the database dump
@@ -431,24 +431,54 @@ def make_data_source_dump_patterns(
borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
return (
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'), name, hostname, port, container, label
*(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'), name, hostname, port, container, label
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_runtime_directory),
name,
hostname,
port,
container,
label,
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_source_directory),
name,
hostname,
port,
container,
label,
),
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_runtime_directory),
name,
hostname,
port,
container,
label,
*(
(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'),
name,
hostname,
port=None,
container=container,
label=label,
),
)
if port == get_default_port(databases, config)
else ()
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_source_directory),
name,
hostname,
port,
container,
label,
*(
(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'),
name,
hostname,
port=get_default_port(databases, config),
container=container,
label=label,
),
)
if port is None
else ()
),
)
+47 -17
View File
@@ -216,7 +216,7 @@ def make_data_source_dump_patterns(
port=None,
container=None,
label=None,
): # pragma: no cover
):
'''
Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
and a database name to match, return the corresponding glob patterns to match the database dump
@@ -225,24 +225,54 @@ def make_data_source_dump_patterns(
borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
return (
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'), name, hostname, port, container, label
*(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'), name, hostname, port, container, label
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_runtime_directory),
name,
hostname,
port,
container,
label,
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_source_directory),
name,
hostname,
port,
container,
label,
),
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_runtime_directory),
name,
hostname,
port,
container,
label,
*(
(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'),
name,
hostname,
port=None,
container=container,
label=label,
),
)
if port == get_default_port(databases, config)
else ()
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_source_directory),
name,
hostname,
port,
container,
label,
*(
(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'),
name,
hostname,
port=get_default_port(databases, config),
container=container,
label=label,
),
)
if port is None
else ()
),
)
+47 -17
View File
@@ -343,7 +343,7 @@ def make_data_source_dump_patterns(
port=None,
container=None,
label=None,
): # pragma: no cover
):
'''
Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
and a database name to match, return the corresponding glob patterns to match the database dump
@@ -352,24 +352,54 @@ def make_data_source_dump_patterns(
borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
return (
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'), name, hostname, port, container, label
*(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'), name, hostname, port, container, label
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_runtime_directory),
name,
hostname,
port,
container,
label,
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_source_directory),
name,
hostname,
port,
container,
label,
),
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_runtime_directory),
name,
hostname,
port,
container,
label,
*(
(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'),
name,
hostname,
port=None,
container=container,
label=label,
),
)
if port == get_default_port(databases, config)
else ()
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_source_directory),
name,
hostname,
port,
container,
label,
*(
(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'),
name,
hostname,
port=get_default_port(databases, config),
container=container,
label=label,
),
)
if port is None
else ()
),
)
+47 -17
View File
@@ -310,7 +310,7 @@ def make_data_source_dump_patterns(
port=None,
container=None,
label=None,
): # pragma: no cover
):
'''
Given a sequence of configurations dicts, a configuration dict, the borgmatic runtime directory,
and a database name to match, return the corresponding glob patterns to match the database dump
@@ -319,24 +319,54 @@ def make_data_source_dump_patterns(
borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
return (
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'), name, hostname, port, container, label
*(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'), name, hostname, port, container, label
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_runtime_directory),
name,
hostname,
port,
container,
label,
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_source_directory),
name,
hostname,
port,
container,
label,
),
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_runtime_directory),
name,
hostname,
port,
container,
label,
*(
(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'),
name,
hostname,
port=None,
container=container,
label=label,
),
)
if port == get_default_port(databases, config)
else ()
),
dump.make_data_source_dump_filename(
make_dump_path(borgmatic_source_directory),
name,
hostname,
port,
container,
label,
*(
(
dump.make_data_source_dump_filename(
make_dump_path('borgmatic'),
name,
hostname,
port=get_default_port(databases, config),
container=container,
label=label,
),
)
if port is None
else ()
),
)
@@ -1426,6 +1426,86 @@ def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
)
def test_make_data_source_dump_patterns_with_no_port_adds_pattern_with_default_port():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=None,
) == (
'borgmatic/host/db',
'run/host/db',
'.borgmatic/host/db',
'borgmatic/host:9999/db',
)
def test_make_data_source_dump_patterns_with_default_port_adds_pattern_with_no_port():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=9999,
) == (
'borgmatic/host:9999/db',
'run/host:9999/db',
'.borgmatic/host:9999/db',
'borgmatic/host/db',
)
def test_make_data_source_dump_patterns_with_non_default_port_adds_no_extra_patterns():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=1234,
) == (
'borgmatic/host:1234/db',
'run/host:1234/db',
'.borgmatic/host:1234/db',
)
def test_restore_data_source_dump_runs_mariadb_to_restore():
hook_config = [{'name': 'foo'}, {'name': 'bar'}]
extract_process = flexmock(stdout=flexmock())
@@ -378,6 +378,86 @@ def test_build_dump_command_with_username_injection_attack_gets_escaped():
assert "'bob; naughty-command'" in command
def test_make_data_source_dump_patterns_with_no_port_adds_pattern_with_default_port():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=None,
) == (
'borgmatic/host/db',
'run/host/db',
'.borgmatic/host/db',
'borgmatic/host:9999/db',
)
def test_make_data_source_dump_patterns_with_default_port_adds_pattern_with_no_port():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=9999,
) == (
'borgmatic/host:9999/db',
'run/host:9999/db',
'.borgmatic/host:9999/db',
'borgmatic/host/db',
)
def test_make_data_source_dump_patterns_with_non_default_port_adds_no_extra_patterns():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=1234,
) == (
'borgmatic/host:1234/db',
'run/host:1234/db',
'.borgmatic/host:1234/db',
)
def test_restore_data_source_dump_runs_mongorestore():
hook_config = [{'name': 'foo', 'schemas': None}, {'name': 'bar'}]
extract_process = flexmock(stdout=flexmock())
@@ -1239,6 +1239,86 @@ def test_execute_dump_command_with_dry_run_skips_mysqldump():
)
def test_make_data_source_dump_patterns_with_no_port_adds_pattern_with_default_port():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=None,
) == (
'borgmatic/host/db',
'run/host/db',
'.borgmatic/host/db',
'borgmatic/host:9999/db',
)
def test_make_data_source_dump_patterns_with_default_port_adds_pattern_with_no_port():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=9999,
) == (
'borgmatic/host:9999/db',
'run/host:9999/db',
'.borgmatic/host:9999/db',
'borgmatic/host/db',
)
def test_make_data_source_dump_patterns_with_non_default_port_adds_no_extra_patterns():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=1234,
) == (
'borgmatic/host:1234/db',
'run/host:1234/db',
'.borgmatic/host:1234/db',
)
def test_restore_data_source_dump_runs_mysql_to_restore():
hook_config = [{'name': 'foo'}, {'name': 'bar'}]
extract_process = flexmock(stdout=flexmock())
@@ -979,6 +979,86 @@ def test_dump_data_sources_runs_non_default_pg_dump():
) == [process]
def test_make_data_source_dump_patterns_with_no_port_adds_pattern_with_default_port():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=None,
) == (
'borgmatic/host/db',
'run/host/db',
'.borgmatic/host/db',
'borgmatic/host:9999/db',
)
def test_make_data_source_dump_patterns_with_default_port_adds_pattern_with_no_port():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=9999,
) == (
'borgmatic/host:9999/db',
'run/host:9999/db',
'.borgmatic/host:9999/db',
'borgmatic/host/db',
)
def test_make_data_source_dump_patterns_with_non_default_port_adds_no_extra_patterns():
flexmock(module.borgmatic.config.paths).should_receive(
'get_borgmatic_source_directory'
).and_return('.borgmatic')
flexmock(module).should_receive('make_dump_path').replace_with(lambda path: path)
flexmock(module.dump).should_receive('make_data_source_dump_filename').replace_with(
lambda dump_path, name, hostname, port, container, label: '/'.join(
(dump_path, f'{hostname}:{port}' if port else hostname, name)
)
)
flexmock(module).should_receive('get_default_port').and_return(9999)
assert module.make_data_source_dump_patterns(
databases=flexmock(),
config=flexmock(),
borgmatic_runtime_directory='run',
name='db',
hostname='host',
port=1234,
) == (
'borgmatic/host:1234/db',
'run/host:1234/db',
'.borgmatic/host:1234/db',
)
def test_restore_data_source_dump_runs_pg_restore():
hook_config = [{'name': 'foo', 'schemas': None}, {'name': 'bar'}]
extract_process = flexmock(stdout=flexmock())