From 8f5ea953480246d162cb1fec51cf7a1adf140198 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Sat, 16 Nov 2024 12:19:20 -0800 Subject: [PATCH] Fix use of borgmatic runtime directory in the restore action (#934). --- borgmatic/actions/restore.py | 3 ++- borgmatic/hooks/mariadb.py | 16 +++++++++++----- borgmatic/hooks/mongodb.py | 16 +++++++++++++--- borgmatic/hooks/mysql.py | 16 +++++++++++----- borgmatic/hooks/postgresql.py | 22 ++++++++++++++++------ borgmatic/hooks/sqlite.py | 16 +++++++++++----- tests/unit/actions/test_restore.py | 13 +++++-------- tests/unit/hooks/test_mariadb.py | 8 ++++++++ tests/unit/hooks/test_mongodb.py | 10 ++++++++++ tests/unit/hooks/test_mysql.py | 8 ++++++++ tests/unit/hooks/test_postgresql.py | 12 ++++++++++++ tests/unit/hooks/test_sqlite.py | 4 ++++ 12 files changed, 111 insertions(+), 33 deletions(-) diff --git a/borgmatic/actions/restore.py b/borgmatic/actions/restore.py index 0fc4e93f..b961a66d 100644 --- a/borgmatic/actions/restore.py +++ b/borgmatic/actions/restore.py @@ -171,6 +171,7 @@ def restore_single_data_source( dry_run=global_arguments.dry_run, extract_process=extract_process, connection_params=connection_params, + borgmatic_runtime_directory=borgmatic_runtime_directory, ) @@ -331,7 +332,6 @@ def run_restore( global_arguments, local_path, remote_path, - borgmatic_runtime_directory, ): ''' Run the "restore" action for the given repository, but only if the repository matches the @@ -375,6 +375,7 @@ def run_restore( global_arguments, local_path, remote_path, + borgmatic_runtime_directory, ) restore_names = find_data_sources_to_restore( restore_arguments.data_sources, archive_data_source_names diff --git a/borgmatic/hooks/mariadb.py b/borgmatic/hooks/mariadb.py index f15d0cd1..fa8e9960 100644 --- a/borgmatic/hooks/mariadb.py +++ b/borgmatic/hooks/mariadb.py @@ -216,14 +216,20 @@ def make_data_source_dump_patterns( def restore_data_source_dump( - hook_config, config, log_prefix, data_source, dry_run, extract_process, connection_params + hook_config, + config, + log_prefix, + data_source, + dry_run, + extract_process, + connection_params, + borgmatic_runtime_directory, ): ''' Restore a database from the given extract stream. The database is supplied as a data source - configuration dict, but the given hook configuration is ignored. The given configuration dict is - used to construct the destination path, and the given log prefix is used for any log entries. If - this is a dry run, then don't actually restore anything. Trigger the given active extract - process (an instance of subprocess.Popen) to produce output to consume. + configuration dict, but the given hook configuration is ignored. The given log prefix is used + for any log entries. If this is a dry run, then don't actually restore anything. Trigger the + given active extract process (an instance of subprocess.Popen) to produce output to consume. ''' dry_run_label = ' (dry run; not actually restoring anything)' if dry_run else '' hostname = connection_params['hostname'] or data_source.get( diff --git a/borgmatic/hooks/mongodb.py b/borgmatic/hooks/mongodb.py index 7ad0259f..49075823 100644 --- a/borgmatic/hooks/mongodb.py +++ b/borgmatic/hooks/mongodb.py @@ -27,7 +27,8 @@ def dump_data_sources(databases, config, log_prefix, borgmatic_runtime_directory ''' Dump the given MongoDB databases to a named pipe. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the borgmatic - runtime directory to construct the destination path and the given log prefix in any log entries. + runtime directory to construct the destination path (used for the directory format and the given + log prefix in any log entries. Return a sequence of subprocess.Popen instances for the dump processes ready to spew to a named pipe. But if this is a dry run, then don't actually dump anything and return an empty sequence. @@ -125,7 +126,14 @@ def make_data_source_dump_patterns( def restore_data_source_dump( - hook_config, config, log_prefix, data_source, dry_run, extract_process, connection_params + hook_config, + config, + log_prefix, + data_source, + dry_run, + extract_process, + connection_params, + borgmatic_runtime_directory, ): ''' Restore a database from the given extract stream. The database is supplied as a data source @@ -139,7 +147,9 @@ def restore_data_source_dump( ''' dry_run_label = ' (dry run; not actually restoring anything)' if dry_run else '' dump_filename = dump.make_data_source_dump_filename( - make_dump_path(config), data_source['name'], data_source.get('hostname') + make_dump_path(borgmatic_runtime_directory), + data_source['name'], + data_source.get('hostname'), ) restore_command = build_restore_command( extract_process, data_source, dump_filename, connection_params diff --git a/borgmatic/hooks/mysql.py b/borgmatic/hooks/mysql.py index 83b8c6e6..c8c89280 100644 --- a/borgmatic/hooks/mysql.py +++ b/borgmatic/hooks/mysql.py @@ -215,14 +215,20 @@ def make_data_source_dump_patterns( def restore_data_source_dump( - hook_config, config, log_prefix, data_source, dry_run, extract_process, connection_params + hook_config, + config, + log_prefix, + data_source, + dry_run, + extract_process, + connection_params, + borgmatic_runtime_directory, ): ''' Restore a database from the given extract stream. The database is supplied as a data source - configuration dict, but the given hook configuration is ignored. The given configuration dict is - used to construct the destination path, and the given log prefix is used for any log entries. If - this is a dry run, then don't actually restore anything. Trigger the given active extract - process (an instance of subprocess.Popen) to produce output to consume. + configuration dict, but the given hook configuration is ignored. The given log prefix is used + for any log entries. If this is a dry run, then don't actually restore anything. Trigger the + given active extract process (an instance of subprocess.Popen) to produce output to consume. ''' dry_run_label = ' (dry run; not actually restoring anything)' if dry_run else '' hostname = connection_params['hostname'] or data_source.get( diff --git a/borgmatic/hooks/postgresql.py b/borgmatic/hooks/postgresql.py index 8fda4baf..4067e09e 100644 --- a/borgmatic/hooks/postgresql.py +++ b/borgmatic/hooks/postgresql.py @@ -241,14 +241,22 @@ def make_data_source_dump_patterns( def restore_data_source_dump( - hook_config, config, log_prefix, data_source, dry_run, extract_process, connection_params + hook_config, + config, + log_prefix, + data_source, + dry_run, + extract_process, + connection_params, + borgmatic_runtime_directory, ): ''' Restore a database from the given extract stream. The database is supplied as a data source - configuration dict, but the given hook configuration is ignored. The given configuration dict is - used to construct the destination path, and the given log prefix is used for any log entries. If - this is a dry run, then don't actually restore anything. Trigger the given active extract - process (an instance of subprocess.Popen) to produce output to consume. + configuration dict, but the given hook configuration is ignored. The given borgmatic runtime + directory is used to construct the destination path (used for the directory format), and the + given log prefix is used for any log entries. If this is a dry run, then don't actually restore + anything. Trigger the given active extract process (an instance of subprocess.Popen) to produce + output to consume. If the extract process is None, then restore the dump from the filesystem rather than from an extract stream. @@ -269,7 +277,9 @@ def restore_data_source_dump( all_databases = bool(data_source['name'] == 'all') dump_filename = dump.make_data_source_dump_filename( - make_dump_path(config), data_source['name'], data_source.get('hostname') + make_dump_path(borgmatic_runtime_directory), + data_source['name'], + data_source.get('hostname'), ) psql_command = tuple( shlex.quote(part) for part in shlex.split(data_source.get('psql_command') or 'psql') diff --git a/borgmatic/hooks/sqlite.py b/borgmatic/hooks/sqlite.py index a7acb56f..f4ea6a72 100644 --- a/borgmatic/hooks/sqlite.py +++ b/borgmatic/hooks/sqlite.py @@ -111,14 +111,20 @@ def make_data_source_dump_patterns( def restore_data_source_dump( - hook_config, config, log_prefix, data_source, dry_run, extract_process, connection_params + hook_config, + config, + log_prefix, + data_source, + dry_run, + extract_process, + connection_params, + borgmatic_runtime_directory, ): ''' Restore a database from the given extract stream. The database is supplied as a data source - configuration dict, but the given hook configuration is ignored. The given configuration dict is - used to construct the destination path, and the given log prefix is used for any log entries. If - this is a dry run, then don't actually restore anything. Trigger the given active extract - process (an instance of subprocess.Popen) to produce output to consume. + configuration dict, but the given hook configuration is ignored. The given log prefix is used + for any log entries. If this is a dry run, then don't actually restore anything. Trigger the + given active extract process (an instance of subprocess.Popen) to produce output to consume. ''' dry_run_label = ' (dry run; not actually restoring anything)' if dry_run else '' database_path = connection_params['restore_path'] or data_source.get( diff --git a/tests/unit/actions/test_restore.py b/tests/unit/actions/test_restore.py index b06d6fb1..a9d5cb88 100644 --- a/tests/unit/actions/test_restore.py +++ b/tests/unit/actions/test_restore.py @@ -107,6 +107,7 @@ def test_restore_single_data_source_extracts_and_restores_single_file_dump(): dry_run=object, extract_process=object, connection_params=object, + borgmatic_runtime_directory=object, ).once() module.restore_single_data_source( @@ -148,6 +149,7 @@ def test_restore_single_data_source_extracts_and_restores_directory_dump(): dry_run=object, extract_process=object, connection_params=object, + borgmatic_runtime_directory='/run/borgmatic', ).once() module.restore_single_data_source( @@ -189,6 +191,7 @@ def test_restore_single_data_source_with_directory_dump_error_cleans_up_temporar dry_run=object, extract_process=object, connection_params=object, + borgmatic_runtime_directory='/run/user/0/borgmatic/tmp1234', ).never() with pytest.raises(ValueError): @@ -211,9 +214,7 @@ def test_restore_single_data_source_with_directory_dump_and_dry_run_skips_direct flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( 'make_data_source_dump_patterns', object, object, object, object, object ).and_return({'postgresql': flexmock()}) - flexmock(module.tempfile).should_receive('mkdtemp').once().and_return( - '/run/user/0/borgmatic/tmp1234' - ) + flexmock(module.tempfile).should_receive('mkdtemp').once().and_return('/run/borgmatic/tmp1234') flexmock(module.borgmatic.hooks.dump).should_receive( 'convert_glob_patterns_to_borg_pattern' ).and_return(flexmock()) @@ -231,6 +232,7 @@ def test_restore_single_data_source_with_directory_dump_and_dry_run_skips_direct dry_run=object, extract_process=object, connection_params=object, + borgmatic_runtime_directory='/run/borgmatic', ).once() module.restore_single_data_source( @@ -524,7 +526,6 @@ def test_run_restore_restores_each_data_source(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), - borgmatic_runtime_directory='/run/borgmatic', ) @@ -548,7 +549,6 @@ def test_run_restore_bails_for_non_matching_repository(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), - borgmatic_runtime_directory='/run/borgmatic', ) @@ -633,7 +633,6 @@ def test_run_restore_restores_data_source_configured_with_all_name(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), - borgmatic_runtime_directory='/run/borgmatic', ) @@ -718,7 +717,6 @@ def test_run_restore_skips_missing_data_source(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), - borgmatic_runtime_directory='/run/borgmatic', ) @@ -797,5 +795,4 @@ def test_run_restore_restores_data_sources_from_different_hooks(): global_arguments=flexmock(dry_run=False), local_path=flexmock(), remote_path=flexmock(), - borgmatic_runtime_directory='/run/borgmatic', ) diff --git a/tests/unit/hooks/test_mariadb.py b/tests/unit/hooks/test_mariadb.py index fd039f74..fb5612be 100644 --- a/tests/unit/hooks/test_mariadb.py +++ b/tests/unit/hooks/test_mariadb.py @@ -494,6 +494,7 @@ def test_restore_data_source_dump_runs_mariadb_to_restore(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -522,6 +523,7 @@ def test_restore_data_source_dump_runs_mariadb_with_options(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -552,6 +554,7 @@ def test_restore_data_source_dump_runs_non_default_mariadb_with_options(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -589,6 +592,7 @@ def test_restore_data_source_dump_runs_mariadb_with_hostname_and_port(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -617,6 +621,7 @@ def test_restore_data_source_dump_runs_mariadb_with_username_and_password(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -666,6 +671,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_ 'username': 'cliusername', 'password': 'clipassword', }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -717,6 +723,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_ 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -738,4 +745,5 @@ def test_restore_data_source_dump_with_dry_run_skips_restore(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) diff --git a/tests/unit/hooks/test_mongodb.py b/tests/unit/hooks/test_mongodb.py index 8b56c9e1..b08dfaa4 100644 --- a/tests/unit/hooks/test_mongodb.py +++ b/tests/unit/hooks/test_mongodb.py @@ -241,6 +241,7 @@ def test_restore_data_source_dump_runs_mongorestore(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -280,6 +281,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_hostname_and_port(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -327,6 +329,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_username_and_password() 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -382,6 +385,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_ 'username': 'cliusername', 'password': 'clipassword', }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -437,6 +441,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_ 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -466,6 +471,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_options(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -503,6 +509,7 @@ def test_restore_databases_dump_runs_mongorestore_with_schemas(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -532,6 +539,7 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -555,6 +563,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -583,4 +592,5 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) diff --git a/tests/unit/hooks/test_mysql.py b/tests/unit/hooks/test_mysql.py index 4d608bf6..496e6663 100644 --- a/tests/unit/hooks/test_mysql.py +++ b/tests/unit/hooks/test_mysql.py @@ -492,6 +492,7 @@ def test_restore_data_source_dump_runs_mysql_to_restore(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -520,6 +521,7 @@ def test_restore_data_source_dump_runs_mysql_with_options(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -548,6 +550,7 @@ def test_restore_data_source_dump_runs_non_default_mysql_with_options(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -585,6 +588,7 @@ def test_restore_data_source_dump_runs_mysql_with_hostname_and_port(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -613,6 +617,7 @@ def test_restore_data_source_dump_runs_mysql_with_username_and_password(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -662,6 +667,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_ 'username': 'cliusername', 'password': 'clipassword', }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -713,6 +719,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_ 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -734,4 +741,5 @@ def test_restore_data_source_dump_with_dry_run_skips_restore(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) diff --git a/tests/unit/hooks/test_postgresql.py b/tests/unit/hooks/test_postgresql.py index f0585258..229a94e4 100644 --- a/tests/unit/hooks/test_postgresql.py +++ b/tests/unit/hooks/test_postgresql.py @@ -620,6 +620,7 @@ def test_restore_data_source_dump_runs_pg_restore(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -682,6 +683,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_hostname_and_port(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -742,6 +744,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_username_and_password(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -821,6 +824,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_ 'username': 'cliusername', 'password': 'clipassword', }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -900,6 +904,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_ 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -961,6 +966,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_options(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1000,6 +1006,7 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1044,6 +1051,7 @@ def test_restore_data_source_dump_runs_psql_for_plain_database_dump(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1113,6 +1121,7 @@ def test_restore_data_source_dump_runs_non_default_pg_restore_and_psql(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1137,6 +1146,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1189,6 +1199,7 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -1245,4 +1256,5 @@ def test_restore_data_source_dump_with_schemas_restores_schemas(): 'username': None, 'password': None, }, + borgmatic_runtime_directory='/run/borgmatic', ) diff --git a/tests/unit/hooks/test_sqlite.py b/tests/unit/hooks/test_sqlite.py index 27a4aa31..460a08d9 100644 --- a/tests/unit/hooks/test_sqlite.py +++ b/tests/unit/hooks/test_sqlite.py @@ -182,6 +182,7 @@ def test_restore_data_source_dump_restores_database(): dry_run=False, extract_process=extract_process, connection_params={'restore_path': None}, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -211,6 +212,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_ dry_run=False, extract_process=extract_process, connection_params={'restore_path': 'cli/path/to/database'}, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -240,6 +242,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_ dry_run=False, extract_process=extract_process, connection_params={'restore_path': None}, + borgmatic_runtime_directory='/run/borgmatic', ) @@ -258,4 +261,5 @@ def test_restore_data_source_dump_does_not_restore_database_if_dry_run(): dry_run=True, extract_process=extract_process, connection_params={'restore_path': None}, + borgmatic_runtime_directory='/run/borgmatic', )