From 8660af745ec891e432c94ef1f8036550020a99cd Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Tue, 10 Dec 2019 16:04:34 -0800 Subject: [PATCH] Optionally change the internal database dump path via "borgmatic_source_directory" option in location configuration section (#259). --- NEWS | 4 +++ borgmatic/borg/create.py | 14 ++++++--- borgmatic/commands/borgmatic.py | 5 +++ borgmatic/config/schema.yaml | 8 +++++ borgmatic/hooks/dump.py | 13 ++++++++ borgmatic/hooks/mysql.py | 47 +++++++++++++++++++--------- borgmatic/hooks/postgresql.py | 47 +++++++++++++++++++--------- docs/how-to/backup-your-databases.md | 8 +++-- setup.py | 2 +- tests/unit/borg/test_create.py | 11 +++++-- tests/unit/hooks/test_dump.py | 8 +++++ tests/unit/hooks/test_mysql.py | 35 ++++++++++++++------- tests/unit/hooks/test_postgresql.py | 38 ++++++++++++++-------- 13 files changed, 175 insertions(+), 65 deletions(-) diff --git a/NEWS b/NEWS index bd671266..9d35de31 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +1.4.19 + * #259: Optionally change the internal database dump path via "borgmatic_source_directory" option + in location configuration section. + 1.4.18 * Fix "--repository" flag to accept relative paths. * Fix "borgmatic umount" so it only runs Borg once instead of once per repository / configuration diff --git a/borgmatic/borg/create.py b/borgmatic/borg/create.py index 2be2eb01..f582fb7b 100644 --- a/borgmatic/borg/create.py +++ b/borgmatic/borg/create.py @@ -104,16 +104,19 @@ def _make_exclude_flags(location_config, exclude_filename=None): ) -BORGMATIC_SOURCE_DIRECTORY = '~/.borgmatic' +DEFAULT_BORGMATIC_SOURCE_DIRECTORY = '~/.borgmatic' -def borgmatic_source_directories(): +def borgmatic_source_directories(borgmatic_source_directory): ''' Return a list of borgmatic-specific source directories used for state like database backups. ''' + if not borgmatic_source_directory: + borgmatic_source_directory = DEFAULT_BORGMATIC_SOURCE_DIRECTORY + return ( - [BORGMATIC_SOURCE_DIRECTORY] - if os.path.exists(os.path.expanduser(BORGMATIC_SOURCE_DIRECTORY)) + [borgmatic_source_directory] + if os.path.exists(os.path.expanduser(borgmatic_source_directory)) else [] ) @@ -134,7 +137,8 @@ def create_archive( storage config dict, create a Borg archive and return Borg's JSON output (if any). ''' sources = _expand_directories( - location_config['source_directories'] + borgmatic_source_directories() + location_config['source_directories'] + + borgmatic_source_directories(location_config.get('borgmatic_source_directory')) ) pattern_file = _write_pattern_file(location_config.get('patterns')) diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index a623db74..3b539764 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -75,6 +75,7 @@ def run_configuration(config_filename, config, arguments): hooks, config_filename, dump.DATABASE_HOOK_NAMES, + location, global_arguments.dry_run, ) except (OSError, CalledProcessError) as error: @@ -111,6 +112,7 @@ def run_configuration(config_filename, config, arguments): hooks, config_filename, dump.DATABASE_HOOK_NAMES, + location, global_arguments.dry_run, ) command.execute_hook( @@ -294,6 +296,7 @@ def run_actions( hooks, repository, dump.DATABASE_HOOK_NAMES, + location, restore_names, ) @@ -325,6 +328,7 @@ def run_actions( restore_databases, repository, dump.DATABASE_HOOK_NAMES, + location, global_arguments.dry_run, ) dispatch.call_hooks( @@ -332,6 +336,7 @@ def run_actions( restore_databases, repository, dump.DATABASE_HOOK_NAMES, + location, global_arguments.dry_run, ) if 'list' in arguments: diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 169b6432..c58ebdec 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -137,6 +137,14 @@ map: desc: | Exclude files with the NODUMP flag. Defaults to false. example: true + borgmatic_source_directory: + type: str + desc: | + Path for additional source files used for temporary internal state like + borgmatic database dumps. Note that changing this path prevents "borgmatic + restore" from finding any database dumps created before the change. Defaults + to ~/.borgmatic + example: /tmp/borgmatic storage: desc: | Repository storage options. See diff --git a/borgmatic/hooks/dump.py b/borgmatic/hooks/dump.py index 38905efd..54db1d26 100644 --- a/borgmatic/hooks/dump.py +++ b/borgmatic/hooks/dump.py @@ -2,11 +2,24 @@ import glob import logging import os +from borgmatic.borg.create import DEFAULT_BORGMATIC_SOURCE_DIRECTORY + logger = logging.getLogger(__name__) DATABASE_HOOK_NAMES = ('postgresql_databases', 'mysql_databases') +def make_database_dump_path(borgmatic_source_directory, database_hook_name): + ''' + Given a borgmatic source directory (or None) and a database hook name, construct a database dump + path. + ''' + if not borgmatic_source_directory: + borgmatic_source_directory = DEFAULT_BORGMATIC_SOURCE_DIRECTORY + + return os.path.join(borgmatic_source_directory, database_hook_name) + + def make_database_dump_filename(dump_path, name, hostname=None): ''' Based on the given dump directory path, database name, and hostname, return a filename to use diff --git a/borgmatic/hooks/mysql.py b/borgmatic/hooks/mysql.py index 5d17e1d6..b76e2bed 100644 --- a/borgmatic/hooks/mysql.py +++ b/borgmatic/hooks/mysql.py @@ -4,15 +4,24 @@ import os from borgmatic.execute import execute_command from borgmatic.hooks import dump -DUMP_PATH = '~/.borgmatic/mysql_databases' logger = logging.getLogger(__name__) -def dump_databases(databases, log_prefix, dry_run): +def make_dump_path(location_config): # pragma: no cover + ''' + Make the dump path from the given location configuration and the name of this hook. + ''' + return dump.make_database_dump_path( + location_config.get('borgmatic_source_directory'), 'mysql_databases' + ) + + +def dump_databases(databases, log_prefix, location_config, dry_run): ''' Dump the given MySQL/MariaDB databases to disk. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the given log - prefix in any log entries. If this is a dry run, then don't actually dump anything. + prefix in any log entries. Use the given location configuration dict to construct the + destination path. If this is a dry run, then don't actually dump anything. ''' dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else '' @@ -20,7 +29,9 @@ def dump_databases(databases, log_prefix, dry_run): for database in databases: name = database['name'] - dump_filename = dump.make_database_dump_filename(DUMP_PATH, name, database.get('hostname')) + dump_filename = dump.make_database_dump_filename( + make_dump_path(location_config), name, database.get('hostname') + ) command = ( ('mysqldump', '--add-drop-database') + (('--host', database['hostname']) if 'hostname' in database else ()) @@ -44,37 +55,43 @@ def dump_databases(databases, log_prefix, dry_run): ) -def remove_database_dumps(databases, log_prefix, dry_run): # pragma: no cover +def remove_database_dumps(databases, log_prefix, location_config, dry_run): # pragma: no cover ''' Remove the database dumps for the given databases. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the log prefix in - any log entries. If this is a dry run, then don't actually remove anything. + any log entries. Use the given location configuration dict to construct the destination path. If + this is a dry run, then don't actually remove anything. ''' - dump.remove_database_dumps(DUMP_PATH, databases, 'MySQL', log_prefix, dry_run) + dump.remove_database_dumps( + make_dump_path(location_config), databases, 'MySQL', log_prefix, dry_run + ) -def make_database_dump_patterns(databases, log_prefix, names): +def make_database_dump_patterns(databases, log_prefix, location_config, names): ''' - Given a sequence of configurations dicts, a prefix to log with, and a sequence of database - names to match, return the corresponding glob patterns to match the database dumps in an - archive. An empty sequence of names indicates that the patterns should match all dumps. + Given a sequence of configurations dicts, a prefix to log with, a location configuration dict, + and a sequence of database names to match, return the corresponding glob patterns to match the + database dumps in an archive. An empty sequence of names indicates that the patterns should + match all dumps. ''' return [ - dump.make_database_dump_filename(DUMP_PATH, name, hostname='*') for name in (names or ['*']) + dump.make_database_dump_filename(make_dump_path(location_config), name, hostname='*') + for name in (names or ['*']) ] -def restore_database_dumps(databases, log_prefix, dry_run): +def restore_database_dumps(databases, log_prefix, location_config, dry_run): ''' Restore the given MySQL/MariaDB databases from disk. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the given log - prefix in any log entries. If this is a dry run, then don't actually restore anything. + prefix in any log entries. Use the given location configuration dict to construct the + destination path. If this is a dry run, then don't actually restore anything. ''' dry_run_label = ' (dry run; not actually restoring anything)' if dry_run else '' for database in databases: dump_filename = dump.make_database_dump_filename( - DUMP_PATH, database['name'], database.get('hostname') + make_dump_path(location_config), database['name'], database.get('hostname') ) restore_command = ( ('mysql', '--batch') diff --git a/borgmatic/hooks/postgresql.py b/borgmatic/hooks/postgresql.py index a7a86942..7a46b268 100644 --- a/borgmatic/hooks/postgresql.py +++ b/borgmatic/hooks/postgresql.py @@ -4,15 +4,24 @@ import os from borgmatic.execute import execute_command from borgmatic.hooks import dump -DUMP_PATH = '~/.borgmatic/postgresql_databases' logger = logging.getLogger(__name__) -def dump_databases(databases, log_prefix, dry_run): +def make_dump_path(location_config): # pragma: no cover + ''' + Make the dump path from the given location configuration and the name of this hook. + ''' + return dump.make_database_dump_path( + location_config.get('borgmatic_source_directory'), 'postgresql_databases' + ) + + +def dump_databases(databases, log_prefix, location_config, dry_run): ''' Dump the given PostgreSQL databases to disk. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the given log prefix in - any log entries. If this is a dry run, then don't actually dump anything. + any log entries. Use the given location configuration dict to construct the destination path. If + this is a dry run, then don't actually dump anything. ''' dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else '' @@ -20,7 +29,9 @@ def dump_databases(databases, log_prefix, dry_run): for database in databases: name = database['name'] - dump_filename = dump.make_database_dump_filename(DUMP_PATH, name, database.get('hostname')) + dump_filename = dump.make_database_dump_filename( + make_dump_path(location_config), name, database.get('hostname') + ) all_databases = bool(name == 'all') command = ( ('pg_dumpall' if all_databases else 'pg_dump', '--no-password', '--clean') @@ -44,37 +55,43 @@ def dump_databases(databases, log_prefix, dry_run): execute_command(command, extra_environment=extra_environment) -def remove_database_dumps(databases, log_prefix, dry_run): # pragma: no cover +def remove_database_dumps(databases, log_prefix, location_config, dry_run): # pragma: no cover ''' Remove the database dumps for the given databases. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the log prefix in - any log entries. If this is a dry run, then don't actually remove anything. + any log entries. Use the given location configuration dict to construct the destination path. If + this is a dry run, then don't actually remove anything. ''' - dump.remove_database_dumps(DUMP_PATH, databases, 'PostgreSQL', log_prefix, dry_run) + dump.remove_database_dumps( + make_dump_path(location_config), databases, 'PostgreSQL', log_prefix, dry_run + ) -def make_database_dump_patterns(databases, log_prefix, names): +def make_database_dump_patterns(databases, log_prefix, location_config, names): ''' - Given a sequence of configurations dicts, a prefix to log with, and a sequence of database - names to match, return the corresponding glob patterns to match the database dumps in an - archive. An empty sequence of names indicates that the patterns should match all dumps. + Given a sequence of configurations dicts, a prefix to log with, a location configuration dict, + and a sequence of database names to match, return the corresponding glob patterns to match the + database dumps in an archive. An empty sequence of names indicates that the patterns should + match all dumps. ''' return [ - dump.make_database_dump_filename(DUMP_PATH, name, hostname='*') for name in (names or ['*']) + dump.make_database_dump_filename(make_dump_path(location_config), name, hostname='*') + for name in (names or ['*']) ] -def restore_database_dumps(databases, log_prefix, dry_run): +def restore_database_dumps(databases, log_prefix, location_config, dry_run): ''' Restore the given PostgreSQL databases from disk. The databases are supplied as a sequence of dicts, one dict describing each database as per the configuration schema. Use the given log - prefix in any log entries. If this is a dry run, then don't actually restore anything. + prefix in any log entries. Use the given location configuration dict to construct the + destination path. If this is a dry run, then don't actually restore anything. ''' dry_run_label = ' (dry run; not actually restoring anything)' if dry_run else '' for database in databases: dump_filename = dump.make_database_dump_filename( - DUMP_PATH, database['name'], database.get('hostname') + make_dump_path(location_config), database['name'], database.get('hostname') ) restore_command = ( ('pg_restore', '--no-password', '--clean', '--if-exists', '--exit-on-error') diff --git a/docs/how-to/backup-your-databases.md b/docs/how-to/backup-your-databases.md index de21192b..0ea771f9 100644 --- a/docs/how-to/backup-your-databases.md +++ b/docs/how-to/backup-your-databases.md @@ -23,8 +23,12 @@ hooks: ``` Prior to each backup, borgmatic dumps each configured database to a file -(located in `~/.borgmatic/`) and includes it in the backup. After the backup -completes, borgmatic removes the database dump files to recover disk space. +and includes it in the backup. After the backup completes, borgmatic removes +the database dump files to recover disk space. + +borgmatic creates these temporary dump files in `~/.borgmatic` by default. To +customize this path, set the `borgmatic_source_directory` option in the +`location` section of borgmatic's configuration. Here's a more involved example that connects to remote databases: diff --git a/setup.py b/setup.py index 9ff3ba3a..18c8ff87 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import find_packages, setup -VERSION = '1.4.18' +VERSION = '1.4.19' setup( diff --git a/tests/unit/borg/test_create.py b/tests/unit/borg/test_create.py index 5a1a3e82..f9044145 100644 --- a/tests/unit/borg/test_create.py +++ b/tests/unit/borg/test_create.py @@ -184,14 +184,21 @@ def test_borgmatic_source_directories_set_when_directory_exists(): flexmock(module.os.path).should_receive('exists').and_return(True) flexmock(module.os.path).should_receive('expanduser') - assert module.borgmatic_source_directories() == [module.BORGMATIC_SOURCE_DIRECTORY] + assert module.borgmatic_source_directories('/tmp') == ['/tmp'] def test_borgmatic_source_directories_empty_when_directory_does_not_exist(): flexmock(module.os.path).should_receive('exists').and_return(False) flexmock(module.os.path).should_receive('expanduser') - assert module.borgmatic_source_directories() == [] + assert module.borgmatic_source_directories('/tmp') == [] + + +def test_borgmatic_source_directories_defaults_when_directory_not_given(): + flexmock(module.os.path).should_receive('exists').and_return(True) + flexmock(module.os.path).should_receive('expanduser') + + assert module.borgmatic_source_directories(None) == [module.DEFAULT_BORGMATIC_SOURCE_DIRECTORY] DEFAULT_ARCHIVE_NAME = '{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}' diff --git a/tests/unit/hooks/test_dump.py b/tests/unit/hooks/test_dump.py index b74d5b7a..d36f8098 100644 --- a/tests/unit/hooks/test_dump.py +++ b/tests/unit/hooks/test_dump.py @@ -4,6 +4,14 @@ from flexmock import flexmock from borgmatic.hooks import dump as module +def test_make_database_dump_path_joins_arguments(): + assert module.make_database_dump_path('/tmp', 'super_databases') == '/tmp/super_databases' + + +def test_make_database_dump_path_defaults_without_source_directory(): + assert module.make_database_dump_path(None, 'super_databases') == '~/.borgmatic/super_databases' + + def test_make_database_dump_filename_uses_name_and_hostname(): flexmock(module.os.path).should_receive('expanduser').and_return('databases') diff --git a/tests/unit/hooks/test_mysql.py b/tests/unit/hooks/test_mysql.py index c1344131..6465d71d 100644 --- a/tests/unit/hooks/test_mysql.py +++ b/tests/unit/hooks/test_mysql.py @@ -8,6 +8,7 @@ from borgmatic.hooks import mysql as module def test_dump_databases_runs_mysqldump_for_each_database(): databases = [{'name': 'foo'}, {'name': 'bar'}] output_file = flexmock() + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ).and_return('databases/localhost/bar') @@ -21,23 +22,25 @@ def test_dump_databases_runs_mysqldump_for_each_database(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_with_dry_run_skips_mysqldump(): databases = [{'name': 'foo'}, {'name': 'bar'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ).and_return('databases/localhost/bar') flexmock(module.os).should_receive('makedirs').never() flexmock(module).should_receive('execute_command').never() - module.dump_databases(databases, 'test.yaml', dry_run=True) + module.dump_databases(databases, 'test.yaml', {}, dry_run=True) def test_dump_databases_runs_mysqldump_with_hostname_and_port(): databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}] output_file = flexmock() + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/database.example.org/foo' ) @@ -61,12 +64,13 @@ def test_dump_databases_runs_mysqldump_with_hostname_and_port(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_runs_mysqldump_with_username_and_password(): databases = [{'name': 'foo', 'username': 'root', 'password': 'trustsome1'}] output_file = flexmock() + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -79,12 +83,13 @@ def test_dump_databases_runs_mysqldump_with_username_and_password(): extra_environment={'MYSQL_PWD': 'trustsome1'}, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_runs_mysqldump_with_options(): databases = [{'name': 'foo', 'options': '--stuff=such'}] output_file = flexmock() + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -97,12 +102,13 @@ def test_dump_databases_runs_mysqldump_with_options(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_runs_mysqldump_for_all_databases(): databases = [{'name': 'all'}] output_file = flexmock() + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/all' ) @@ -115,30 +121,33 @@ def test_dump_databases_runs_mysqldump_for_all_databases(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_make_database_dump_patterns_converts_names_to_glob_paths(): + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/*/foo' ).and_return('databases/*/bar') - assert module.make_database_dump_patterns(flexmock(), flexmock(), ('foo', 'bar')) == [ + assert module.make_database_dump_patterns(flexmock(), flexmock(), {}, ('foo', 'bar')) == [ 'databases/*/foo', 'databases/*/bar', ] def test_make_database_dump_patterns_treats_empty_names_as_matching_all_databases(): + flexmock(module).should_receive('make_dump_path').and_return('/dump/path') flexmock(module.dump).should_receive('make_database_dump_filename').with_args( - module.DUMP_PATH, '*', '*' + '/dump/path', '*', '*' ).and_return('databases/*/*') - assert module.make_database_dump_patterns(flexmock(), flexmock(), ()) == ['databases/*/*'] + assert module.make_database_dump_patterns(flexmock(), flexmock(), {}, ()) == ['databases/*/*'] def test_restore_database_dumps_restores_each_database(): databases = [{'name': 'foo'}, {'name': 'bar'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ).and_return('databases/localhost/bar') @@ -153,11 +162,12 @@ def test_restore_database_dumps_restores_each_database(): ('mysql', '--batch'), input_file=input_file, extra_environment=None ).once() - module.restore_database_dumps(databases, 'test.yaml', dry_run=False) + module.restore_database_dumps(databases, 'test.yaml', {}, dry_run=False) def test_restore_database_dumps_runs_mysql_with_hostname_and_port(): databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -182,11 +192,12 @@ def test_restore_database_dumps_runs_mysql_with_hostname_and_port(): extra_environment=None, ).once() - module.restore_database_dumps(databases, 'test.yaml', dry_run=False) + module.restore_database_dumps(databases, 'test.yaml', {}, dry_run=False) def test_restore_database_dumps_runs_mysql_with_username_and_password(): databases = [{'name': 'foo', 'username': 'root', 'password': 'trustsome1'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -202,4 +213,4 @@ def test_restore_database_dumps_runs_mysql_with_username_and_password(): extra_environment={'MYSQL_PWD': 'trustsome1'}, ).once() - module.restore_database_dumps(databases, 'test.yaml', dry_run=False) + module.restore_database_dumps(databases, 'test.yaml', {}, dry_run=False) diff --git a/tests/unit/hooks/test_postgresql.py b/tests/unit/hooks/test_postgresql.py index 470a63e4..a7c23025 100644 --- a/tests/unit/hooks/test_postgresql.py +++ b/tests/unit/hooks/test_postgresql.py @@ -5,6 +5,7 @@ from borgmatic.hooks import postgresql as module def test_dump_databases_runs_pg_dump_for_each_database(): databases = [{'name': 'foo'}, {'name': 'bar'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ).and_return('databases/localhost/bar') @@ -25,22 +26,24 @@ def test_dump_databases_runs_pg_dump_for_each_database(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_with_dry_run_skips_pg_dump(): databases = [{'name': 'foo'}, {'name': 'bar'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ).and_return('databases/localhost/bar') flexmock(module.os).should_receive('makedirs').never() flexmock(module).should_receive('execute_command').never() - module.dump_databases(databases, 'test.yaml', dry_run=True) + module.dump_databases(databases, 'test.yaml', {}, dry_run=True) def test_dump_databases_runs_pg_dump_with_hostname_and_port(): databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/database.example.org/foo' ) @@ -64,11 +67,12 @@ def test_dump_databases_runs_pg_dump_with_hostname_and_port(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_runs_pg_dump_with_username_and_password(): databases = [{'name': 'foo', 'username': 'postgres', 'password': 'trustsome1'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -90,11 +94,12 @@ def test_dump_databases_runs_pg_dump_with_username_and_password(): extra_environment={'PGPASSWORD': 'trustsome1'}, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_runs_pg_dump_with_format(): databases = [{'name': 'foo', 'format': 'tar'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -114,11 +119,12 @@ def test_dump_databases_runs_pg_dump_with_format(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_runs_pg_dump_with_options(): databases = [{'name': 'foo', 'options': '--stuff=such'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -139,11 +145,12 @@ def test_dump_databases_runs_pg_dump_with_options(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_dump_databases_runs_pg_dumpall_for_all_databases(): databases = [{'name': 'all'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/all' ) @@ -154,30 +161,33 @@ def test_dump_databases_runs_pg_dumpall_for_all_databases(): extra_environment=None, ).once() - module.dump_databases(databases, 'test.yaml', dry_run=False) + module.dump_databases(databases, 'test.yaml', {}, dry_run=False) def test_make_database_dump_patterns_converts_names_to_glob_paths(): + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/*/foo' ).and_return('databases/*/bar') - assert module.make_database_dump_patterns(flexmock(), flexmock(), ('foo', 'bar')) == [ + assert module.make_database_dump_patterns(flexmock(), flexmock(), {}, ('foo', 'bar')) == [ 'databases/*/foo', 'databases/*/bar', ] def test_make_database_dump_patterns_treats_empty_names_as_matching_all_databases(): + flexmock(module).should_receive('make_dump_path').and_return('/dump/path') flexmock(module.dump).should_receive('make_database_dump_filename').with_args( - module.DUMP_PATH, '*', '*' + '/dump/path', '*', '*' ).and_return('databases/*/*') - assert module.make_database_dump_patterns(flexmock(), flexmock(), ()) == ['databases/*/*'] + assert module.make_database_dump_patterns(flexmock(), flexmock(), {}, ()) == ['databases/*/*'] def test_restore_database_dumps_restores_each_database(): databases = [{'name': 'foo'}, {'name': 'bar'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ).and_return('databases/localhost/bar') @@ -201,11 +211,12 @@ def test_restore_database_dumps_restores_each_database(): extra_environment=None, ).once() - module.restore_database_dumps(databases, 'test.yaml', dry_run=False) + module.restore_database_dumps(databases, 'test.yaml', {}, dry_run=False) def test_restore_database_dumps_runs_pg_restore_with_hostname_and_port(): databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -244,11 +255,12 @@ def test_restore_database_dumps_runs_pg_restore_with_hostname_and_port(): extra_environment=None, ).once() - module.restore_database_dumps(databases, 'test.yaml', dry_run=False) + module.restore_database_dumps(databases, 'test.yaml', {}, dry_run=False) def test_restore_database_dumps_runs_pg_restore_with_username_and_password(): databases = [{'name': 'foo', 'username': 'postgres', 'password': 'trustsome1'}] + flexmock(module).should_receive('make_dump_path').and_return('') flexmock(module.dump).should_receive('make_database_dump_filename').and_return( 'databases/localhost/foo' ) @@ -283,4 +295,4 @@ def test_restore_database_dumps_runs_pg_restore_with_username_and_password(): extra_environment={'PGPASSWORD': 'trustsome1'}, ).once() - module.restore_database_dumps(databases, 'test.yaml', dry_run=False) + module.restore_database_dumps(databases, 'test.yaml', {}, dry_run=False)