Fork a MariaDB database hook from the MySQL database hook (#727).
This commit is contained in:
parent
8a94b9e2f1
commit
193dd93de2
13 changed files with 1150 additions and 96 deletions
16
.drone.yml
16
.drone.yml
|
@ -16,16 +16,16 @@ services:
|
||||||
POSTGRES_USER: postgres2
|
POSTGRES_USER: postgres2
|
||||||
commands:
|
commands:
|
||||||
- docker-entrypoint.sh -p 5433
|
- docker-entrypoint.sh -p 5433
|
||||||
- name: mysql
|
- name: mariadb
|
||||||
image: docker.io/mariadb:10.5
|
image: docker.io/mariadb:10.11.4
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: test
|
MARIADB_ROOT_PASSWORD: test
|
||||||
MYSQL_DATABASE: test
|
MARIADB_DATABASE: test
|
||||||
- name: mysql2
|
- name: mariadb2
|
||||||
image: docker.io/mariadb:10.5
|
image: docker.io/mariadb:10.11.4
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: test2
|
MARIADB_ROOT_PASSWORD: test2
|
||||||
MYSQL_DATABASE: test
|
MARIADB_DATABASE: test
|
||||||
commands:
|
commands:
|
||||||
- docker-entrypoint.sh --port=3307
|
- docker-entrypoint.sh --port=3307
|
||||||
- name: mongodb
|
- name: mongodb
|
||||||
|
|
5
NEWS
5
NEWS
|
@ -1,3 +1,8 @@
|
||||||
|
1.8.2.dev0
|
||||||
|
* #727: Add a MariaDB database hook that uses native MariaDB commands instead of the deprecated
|
||||||
|
MySQL ones. Be aware though that any existing backups made with the "mysql_databases:" hook are
|
||||||
|
only restorable with a "mysql_databases:" configuration.
|
||||||
|
|
||||||
1.8.1
|
1.8.1
|
||||||
* #326: Add documentation for restoring a database to an alternate host:
|
* #326: Add documentation for restoring a database to an alternate host:
|
||||||
https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#restore-to-an-alternate-host
|
https://torsion.org/borgmatic/docs/how-to/backup-your-databases/#restore-to-an-alternate-host
|
||||||
|
|
|
@ -841,10 +841,121 @@ properties:
|
||||||
description: |
|
description: |
|
||||||
List of one or more PostgreSQL databases to dump before creating a
|
List of one or more PostgreSQL databases to dump before creating a
|
||||||
backup, run once per configuration file. The database dumps are
|
backup, run once per configuration file. The database dumps are
|
||||||
added to your source directories at runtime, backed up, and removed
|
added to your source directories at runtime and streamed directly
|
||||||
afterwards. Requires pg_dump/pg_dumpall/pg_restore commands. See
|
to Borg. Requires pg_dump/pg_dumpall/pg_restore commands. See
|
||||||
https://www.postgresql.org/docs/current/app-pgdump.html and
|
https://www.postgresql.org/docs/current/app-pgdump.html and
|
||||||
https://www.postgresql.org/docs/current/libpq-ssl.html for details.
|
https://www.postgresql.org/docs/current/libpq-ssl.html for
|
||||||
|
details.
|
||||||
|
mariadb_databases:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
required: ['name']
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Database name (required if using this hook). Or "all" to
|
||||||
|
dump all databases on the host. Note that using this
|
||||||
|
database hook implicitly enables both read_special and
|
||||||
|
one_file_system (see above) to support dump and restore
|
||||||
|
streaming.
|
||||||
|
example: users
|
||||||
|
hostname:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Database hostname to connect to. Defaults to connecting
|
||||||
|
via local Unix socket.
|
||||||
|
example: database.example.org
|
||||||
|
restore_hostname:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Database hostname to restore to. Defaults to the
|
||||||
|
"hostname" option.
|
||||||
|
example: database.example.org
|
||||||
|
port:
|
||||||
|
type: integer
|
||||||
|
description: Port to connect to. Defaults to 3306.
|
||||||
|
example: 3307
|
||||||
|
restore_port:
|
||||||
|
type: integer
|
||||||
|
description: |
|
||||||
|
Port to restore to. Defaults to the "port" option.
|
||||||
|
example: 5433
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Username with which to connect to the database. Defaults
|
||||||
|
to the username of the current user.
|
||||||
|
example: dbuser
|
||||||
|
restore_username:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Username with which to restore the database. Defaults to
|
||||||
|
the "username" option.
|
||||||
|
example: dbuser
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Password with which to connect to the database. Omitting
|
||||||
|
a password will only work if MariaDB is configured to
|
||||||
|
trust the configured username without a password.
|
||||||
|
example: trustsome1
|
||||||
|
restore_password:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Password with which to connect to the restore database.
|
||||||
|
Defaults to the "password" option.
|
||||||
|
example: trustsome1
|
||||||
|
format:
|
||||||
|
type: string
|
||||||
|
enum: ['sql']
|
||||||
|
description: |
|
||||||
|
Database dump output format. Currently only "sql" is
|
||||||
|
supported. Defaults to "sql" for a single database. Or,
|
||||||
|
when database name is "all" and format is blank, dumps
|
||||||
|
all databases to a single file. But if a format is
|
||||||
|
specified with an "all" database name, dumps each
|
||||||
|
database to a separate file of that format, allowing
|
||||||
|
more convenient restores of individual databases.
|
||||||
|
example: directory
|
||||||
|
add_drop_database:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Use the "--add-drop-database" flag with mariadb-dump,
|
||||||
|
causing the database to be dropped right before restore.
|
||||||
|
Defaults to true.
|
||||||
|
example: false
|
||||||
|
options:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Additional mariadb-dump options to pass directly to the
|
||||||
|
dump command, without performing any validation on them.
|
||||||
|
See mariadb-dump documentation for details.
|
||||||
|
example: --skip-comments
|
||||||
|
list_options:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Additional options to pass directly to the mariadb
|
||||||
|
command that lists available databases, without
|
||||||
|
performing any validation on them. See mariadb command
|
||||||
|
documentation for details.
|
||||||
|
example: --defaults-extra-file=mariadb.cnf
|
||||||
|
restore_options:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Additional options to pass directly to the mariadb
|
||||||
|
command that restores database dumps, without
|
||||||
|
performing any validation on them. See mariadb command
|
||||||
|
documentation for details.
|
||||||
|
example: --defaults-extra-file=mariadb.cnf
|
||||||
|
description: |
|
||||||
|
List of one or more MariaDB databases to dump before creating a
|
||||||
|
backup, run once per configuration file. The database dumps are
|
||||||
|
added to your source directories at runtime and streamed directly
|
||||||
|
to Borg. Requires mariadb-dump/mariadb commands. See
|
||||||
|
https://mariadb.com/kb/en/library/mysqldump/ for details.
|
||||||
mysql_databases:
|
mysql_databases:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
@ -893,7 +1004,7 @@ properties:
|
||||||
description: |
|
description: |
|
||||||
Username with which to restore the database. Defaults to
|
Username with which to restore the database. Defaults to
|
||||||
the "username" option.
|
the "username" option.
|
||||||
example: dbuser
|
example: dbuser
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
|
@ -906,7 +1017,7 @@ properties:
|
||||||
description: |
|
description: |
|
||||||
Password with which to connect to the restore database.
|
Password with which to connect to the restore database.
|
||||||
Defaults to the "password" option.
|
Defaults to the "password" option.
|
||||||
example: trustsome1
|
example: trustsome1
|
||||||
format:
|
format:
|
||||||
type: string
|
type: string
|
||||||
enum: ['sql']
|
enum: ['sql']
|
||||||
|
@ -936,26 +1047,26 @@ properties:
|
||||||
list_options:
|
list_options:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Additional mysql options to pass directly to the mysql
|
Additional options to pass directly to the mysql
|
||||||
command that lists available databases, without
|
command that lists available databases, without
|
||||||
performing any validation on them. See mysql
|
performing any validation on them. See mysql command
|
||||||
documentation for details.
|
documentation for details.
|
||||||
example: --defaults-extra-file=my.cnf
|
example: --defaults-extra-file=my.cnf
|
||||||
restore_options:
|
restore_options:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Additional mysql options to pass directly to the mysql
|
Additional options to pass directly to the mysql
|
||||||
command that restores database dumps, without performing
|
command that restores database dumps, without
|
||||||
any validation on them. See mysql documentation for
|
performing any validation on them. See mysql command
|
||||||
details.
|
documentation for details.
|
||||||
example: --defaults-extra-file=my.cnf
|
example: --defaults-extra-file=my.cnf
|
||||||
description: |
|
description: |
|
||||||
List of one or more MySQL/MariaDB databases to dump before creating
|
List of one or more MySQL databases to dump before creating a
|
||||||
a backup, run once per configuration file. The database dumps are
|
backup, run once per configuration file. The database dumps are
|
||||||
added to your source directories at runtime, backed up, and removed
|
added to your source directories at runtime and streamed directly
|
||||||
afterwards. Requires mysqldump/mysql commands (from either MySQL or
|
to Borg. Requires mysqldump/mysql commands. See
|
||||||
MariaDB). See https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html
|
https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html for
|
||||||
or https://mariadb.com/kb/en/library/mysqldump/ for details.
|
details.
|
||||||
sqlite_databases:
|
sqlite_databases:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
@ -1033,7 +1144,7 @@ properties:
|
||||||
description: |
|
description: |
|
||||||
Username with which to restore the database. Defaults to
|
Username with which to restore the database. Defaults to
|
||||||
the "username" option.
|
the "username" option.
|
||||||
example: dbuser
|
example: dbuser
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
|
@ -1080,8 +1191,8 @@ properties:
|
||||||
description: |
|
description: |
|
||||||
List of one or more MongoDB databases to dump before creating a
|
List of one or more MongoDB databases to dump before creating a
|
||||||
backup, run once per configuration file. The database dumps are
|
backup, run once per configuration file. The database dumps are
|
||||||
added to your source directories at runtime, backed up, and removed
|
added to your source directories at runtime and streamed directly
|
||||||
afterwards. Requires mongodump/mongorestore commands. See
|
to Borg. Requires mongodump/mongorestore commands. See
|
||||||
https://docs.mongodb.com/database-tools/mongodump/ and
|
https://docs.mongodb.com/database-tools/mongodump/ and
|
||||||
https://docs.mongodb.com/database-tools/mongorestore/ for details.
|
https://docs.mongodb.com/database-tools/mongorestore/ for details.
|
||||||
ntfy:
|
ntfy:
|
||||||
|
|
|
@ -4,6 +4,7 @@ from borgmatic.hooks import (
|
||||||
cronhub,
|
cronhub,
|
||||||
cronitor,
|
cronitor,
|
||||||
healthchecks,
|
healthchecks,
|
||||||
|
mariadb,
|
||||||
mongodb,
|
mongodb,
|
||||||
mysql,
|
mysql,
|
||||||
ntfy,
|
ntfy,
|
||||||
|
@ -18,6 +19,7 @@ HOOK_NAME_TO_MODULE = {
|
||||||
'cronhub': cronhub,
|
'cronhub': cronhub,
|
||||||
'cronitor': cronitor,
|
'cronitor': cronitor,
|
||||||
'healthchecks': healthchecks,
|
'healthchecks': healthchecks,
|
||||||
|
'mariadb_databases': mariadb,
|
||||||
'mongodb_databases': mongodb,
|
'mongodb_databases': mongodb,
|
||||||
'mysql_databases': mysql,
|
'mysql_databases': mysql,
|
||||||
'ntfy': ntfy,
|
'ntfy': ntfy,
|
||||||
|
|
|
@ -7,9 +7,10 @@ from borgmatic.borg.state import DEFAULT_BORGMATIC_SOURCE_DIRECTORY
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
DATABASE_HOOK_NAMES = (
|
DATABASE_HOOK_NAMES = (
|
||||||
'postgresql_databases',
|
'mariadb_databases',
|
||||||
'mysql_databases',
|
'mysql_databases',
|
||||||
'mongodb_databases',
|
'mongodb_databases',
|
||||||
|
'postgresql_databases',
|
||||||
'sqlite_databases',
|
'sqlite_databases',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
242
borgmatic/hooks/mariadb.py
Normal file
242
borgmatic/hooks/mariadb.py
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
import copy
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from borgmatic.execute import (
|
||||||
|
execute_command,
|
||||||
|
execute_command_and_capture_output,
|
||||||
|
execute_command_with_processes,
|
||||||
|
)
|
||||||
|
from borgmatic.hooks import dump
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def make_dump_path(config): # pragma: no cover
|
||||||
|
'''
|
||||||
|
Make the dump path from the given configuration dict and the name of this hook.
|
||||||
|
'''
|
||||||
|
return dump.make_database_dump_path(
|
||||||
|
config.get('borgmatic_source_directory'), 'mariadb_databases'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys')
|
||||||
|
|
||||||
|
|
||||||
|
def database_names_to_dump(database, extra_environment, log_prefix, dry_run):
|
||||||
|
'''
|
||||||
|
Given a requested database config, return the corresponding sequence of database names to dump.
|
||||||
|
In the case of "all", query for the names of databases on the configured host and return them,
|
||||||
|
excluding any system databases that will cause problems during restore.
|
||||||
|
'''
|
||||||
|
if database['name'] != 'all':
|
||||||
|
return (database['name'],)
|
||||||
|
if dry_run:
|
||||||
|
return ()
|
||||||
|
|
||||||
|
show_command = (
|
||||||
|
('mariadb',)
|
||||||
|
+ (tuple(database['list_options'].split(' ')) if 'list_options' in database else ())
|
||||||
|
+ (('--host', database['hostname']) if 'hostname' in database else ())
|
||||||
|
+ (('--port', str(database['port'])) if 'port' in database else ())
|
||||||
|
+ (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
|
||||||
|
+ (('--user', database['username']) if 'username' in database else ())
|
||||||
|
+ ('--skip-column-names', '--batch')
|
||||||
|
+ ('--execute', 'show schemas')
|
||||||
|
)
|
||||||
|
logger.debug(f'{log_prefix}: Querying for "all" MariaDB databases to dump')
|
||||||
|
show_output = execute_command_and_capture_output(
|
||||||
|
show_command, extra_environment=extra_environment
|
||||||
|
)
|
||||||
|
|
||||||
|
return tuple(
|
||||||
|
show_name
|
||||||
|
for show_name in show_output.strip().splitlines()
|
||||||
|
if show_name not in SYSTEM_DATABASE_NAMES
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_dump_command(
|
||||||
|
database, log_prefix, dump_path, database_names, extra_environment, dry_run, dry_run_label
|
||||||
|
):
|
||||||
|
'''
|
||||||
|
Kick off a dump for the given MariaDB database (provided as a configuration dict) to a named
|
||||||
|
pipe constructed from the given dump path and database names. Use the given log prefix in any
|
||||||
|
log entries.
|
||||||
|
|
||||||
|
Return a subprocess.Popen instance for the dump process ready to spew to a named pipe. But if
|
||||||
|
this is a dry run, then don't actually dump anything and return None.
|
||||||
|
'''
|
||||||
|
database_name = database['name']
|
||||||
|
dump_filename = dump.make_database_dump_filename(
|
||||||
|
dump_path, database['name'], database.get('hostname')
|
||||||
|
)
|
||||||
|
if os.path.exists(dump_filename):
|
||||||
|
logger.warning(
|
||||||
|
f'{log_prefix}: Skipping duplicate dump of MariaDB database "{database_name}" to {dump_filename}'
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
dump_command = (
|
||||||
|
('mariadb-dump',)
|
||||||
|
+ (tuple(database['options'].split(' ')) if 'options' in database else ())
|
||||||
|
+ (('--add-drop-database',) if database.get('add_drop_database', True) else ())
|
||||||
|
+ (('--host', database['hostname']) if 'hostname' in database else ())
|
||||||
|
+ (('--port', str(database['port'])) if 'port' in database else ())
|
||||||
|
+ (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
|
||||||
|
+ (('--user', database['username']) if 'username' in database else ())
|
||||||
|
+ ('--databases',)
|
||||||
|
+ database_names
|
||||||
|
+ ('--result-file', dump_filename)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f'{log_prefix}: Dumping MariaDB database "{database_name}" to {dump_filename}{dry_run_label}'
|
||||||
|
)
|
||||||
|
if dry_run:
|
||||||
|
return None
|
||||||
|
|
||||||
|
dump.create_named_pipe_for_dump(dump_filename)
|
||||||
|
|
||||||
|
return execute_command(
|
||||||
|
dump_command,
|
||||||
|
extra_environment=extra_environment,
|
||||||
|
run_to_completion=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dump_databases(databases, config, log_prefix, dry_run):
|
||||||
|
'''
|
||||||
|
Dump the given MariaDB 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 given
|
||||||
|
configuration dict to construct the destination path 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.
|
||||||
|
'''
|
||||||
|
dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
|
||||||
|
processes = []
|
||||||
|
|
||||||
|
logger.info(f'{log_prefix}: Dumping MariaDB databases{dry_run_label}')
|
||||||
|
|
||||||
|
for database in databases:
|
||||||
|
dump_path = make_dump_path(config)
|
||||||
|
extra_environment = {'MYSQL_PWD': database['password']} if 'password' in database else None
|
||||||
|
dump_database_names = database_names_to_dump(
|
||||||
|
database, extra_environment, log_prefix, dry_run
|
||||||
|
)
|
||||||
|
|
||||||
|
if not dump_database_names:
|
||||||
|
if dry_run:
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise ValueError('Cannot find any MariaDB databases to dump.')
|
||||||
|
|
||||||
|
if database['name'] == 'all' and database.get('format'):
|
||||||
|
for dump_name in dump_database_names:
|
||||||
|
renamed_database = copy.copy(database)
|
||||||
|
renamed_database['name'] = dump_name
|
||||||
|
processes.append(
|
||||||
|
execute_dump_command(
|
||||||
|
renamed_database,
|
||||||
|
log_prefix,
|
||||||
|
dump_path,
|
||||||
|
(dump_name,),
|
||||||
|
extra_environment,
|
||||||
|
dry_run,
|
||||||
|
dry_run_label,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
processes.append(
|
||||||
|
execute_dump_command(
|
||||||
|
database,
|
||||||
|
log_prefix,
|
||||||
|
dump_path,
|
||||||
|
dump_database_names,
|
||||||
|
extra_environment,
|
||||||
|
dry_run,
|
||||||
|
dry_run_label,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return [process for process in processes if process]
|
||||||
|
|
||||||
|
|
||||||
|
def remove_database_dumps(databases, config, log_prefix, dry_run): # pragma: no cover
|
||||||
|
'''
|
||||||
|
Remove all database dump files for this hook regardless of the given databases. Use the given
|
||||||
|
configuration dict to construct the destination path and the log prefix in any log entries. If
|
||||||
|
this is a dry run, then don't actually remove anything.
|
||||||
|
'''
|
||||||
|
dump.remove_database_dumps(make_dump_path(config), 'MariaDB', log_prefix, dry_run)
|
||||||
|
|
||||||
|
|
||||||
|
def make_database_dump_pattern(databases, config, log_prefix, name=None): # pragma: no cover
|
||||||
|
'''
|
||||||
|
Given a sequence of configurations dicts, a configuration dict, a prefix to log with, and a
|
||||||
|
database name to match, return the corresponding glob patterns to match the database dump in an
|
||||||
|
archive.
|
||||||
|
'''
|
||||||
|
return dump.make_database_dump_filename(make_dump_path(config), name, hostname='*')
|
||||||
|
|
||||||
|
|
||||||
|
def restore_database_dump(
|
||||||
|
databases_config, config, log_prefix, database_name, dry_run, extract_process, connection_params
|
||||||
|
):
|
||||||
|
'''
|
||||||
|
Restore the given MariaDB database from an extract stream. The databases are supplied as a
|
||||||
|
sequence containing one dict describing each database (as per the configuration schema), but
|
||||||
|
only the database corresponding to the given database name is restored. Use the given log prefix
|
||||||
|
in 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 ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
database = next(
|
||||||
|
database_config
|
||||||
|
for database_config in databases_config
|
||||||
|
if database_config.get('name') == database_name
|
||||||
|
)
|
||||||
|
except StopIteration:
|
||||||
|
raise ValueError(
|
||||||
|
f'A database named "{database_name}" could not be found in the configuration'
|
||||||
|
)
|
||||||
|
|
||||||
|
hostname = connection_params['hostname'] or database.get(
|
||||||
|
'restore_hostname', database.get('hostname')
|
||||||
|
)
|
||||||
|
port = str(connection_params['port'] or database.get('restore_port', database.get('port', '')))
|
||||||
|
username = connection_params['username'] or database.get(
|
||||||
|
'restore_username', database.get('username')
|
||||||
|
)
|
||||||
|
password = connection_params['password'] or database.get(
|
||||||
|
'restore_password', database.get('password')
|
||||||
|
)
|
||||||
|
|
||||||
|
restore_command = (
|
||||||
|
('mariadb', '--batch')
|
||||||
|
+ (tuple(database['restore_options'].split(' ')) if 'restore_options' in database else ())
|
||||||
|
+ (('--host', hostname) if hostname else ())
|
||||||
|
+ (('--port', str(port)) if port else ())
|
||||||
|
+ (('--protocol', 'tcp') if hostname or port else ())
|
||||||
|
+ (('--user', username) if username else ())
|
||||||
|
)
|
||||||
|
extra_environment = {'MYSQL_PWD': password} if password else None
|
||||||
|
|
||||||
|
logger.debug(f"{log_prefix}: Restoring MariaDB database {database['name']}{dry_run_label}")
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Don't give Borg local path so as to error on warnings, as "borg extract" only gives a warning
|
||||||
|
# if the restore paths don't exist in the archive.
|
||||||
|
execute_command_with_processes(
|
||||||
|
restore_command,
|
||||||
|
[extract_process],
|
||||||
|
output_log_level=logging.DEBUG,
|
||||||
|
input_file=extract_process.stdout,
|
||||||
|
extra_environment=extra_environment,
|
||||||
|
)
|
|
@ -59,26 +59,23 @@ def build_dump_command(database, dump_filename, dump_format):
|
||||||
Return the mongodump command from a single database configuration.
|
Return the mongodump command from a single database configuration.
|
||||||
'''
|
'''
|
||||||
all_databases = database['name'] == 'all'
|
all_databases = database['name'] == 'all'
|
||||||
command = ['mongodump']
|
|
||||||
if dump_format == 'directory':
|
return (
|
||||||
command.extend(('--out', dump_filename))
|
('mongodump',)
|
||||||
if 'hostname' in database:
|
+ (('--out', dump_filename) if dump_format == 'directory' else ())
|
||||||
command.extend(('--host', database['hostname']))
|
+ (('--host', database['hostname']) if 'hostname' in database else ())
|
||||||
if 'port' in database:
|
+ (('--port', str(database['port'])) if 'port' in database else ())
|
||||||
command.extend(('--port', str(database['port'])))
|
+ (('--username', database['username']) if 'username' in database else ())
|
||||||
if 'username' in database:
|
+ (('--password', database['password']) if 'password' in database else ())
|
||||||
command.extend(('--username', database['username']))
|
+ (
|
||||||
if 'password' in database:
|
('--authenticationDatabase', database['authentication_database'])
|
||||||
command.extend(('--password', database['password']))
|
if 'authentication_database' in database
|
||||||
if 'authentication_database' in database:
|
else ()
|
||||||
command.extend(('--authenticationDatabase', database['authentication_database']))
|
)
|
||||||
if not all_databases:
|
+ (('--db', database['name']) if not all_databases else ())
|
||||||
command.extend(('--db', database['name']))
|
+ (tuple(database['options'].split(' ')) if 'options' in database else ())
|
||||||
if 'options' in database:
|
+ (('--archive', '>', dump_filename) if dump_format != 'directory' else ())
|
||||||
command.extend(database['options'].split(' '))
|
)
|
||||||
if dump_format != 'directory':
|
|
||||||
command.extend(('--archive', '>', dump_filename))
|
|
||||||
return command
|
|
||||||
|
|
||||||
|
|
||||||
def remove_database_dumps(databases, config, log_prefix, dry_run): # pragma: no cover
|
def remove_database_dumps(databases, config, log_prefix, dry_run): # pragma: no cover
|
||||||
|
|
|
@ -15,7 +15,7 @@ consistent snapshot that is more suited for backups.
|
||||||
|
|
||||||
Fortunately, borgmatic includes built-in support for creating database dumps
|
Fortunately, borgmatic includes built-in support for creating database dumps
|
||||||
prior to running backups. For example, here is everything you need to dump and
|
prior to running backups. For example, here is everything you need to dump and
|
||||||
backup a couple of local PostgreSQL databases and a MySQL/MariaDB database.
|
backup a couple of local PostgreSQL databases and a MySQL database.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
postgresql_databases:
|
postgresql_databases:
|
||||||
|
@ -46,6 +46,16 @@ sqlite_databases:
|
||||||
path: /var/lib/sqlite3/mydb.sqlite
|
path: /var/lib/sqlite3/mydb.sqlite
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<span class="minilink minilink-addedin">New in version 1.8.2</span> If you're
|
||||||
|
using MariaDB, use the MariaDB database hook instead of `mysql_databases:` as
|
||||||
|
the MariaDB hook calls native MariaDB commands instead of the deprecated MySQL
|
||||||
|
ones. For instance:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
mariadb_databases:
|
||||||
|
- name: comments
|
||||||
|
```
|
||||||
|
|
||||||
As part of each backup, borgmatic streams a database dump for each configured
|
As part of each backup, borgmatic streams a database dump for each configured
|
||||||
database directly to Borg, so it's included in the backup without consuming
|
database directly to Borg, so it's included in the backup without consuming
|
||||||
additional disk space. (The exceptions are the PostgreSQL/MongoDB "directory"
|
additional disk space. (The exceptions are the PostgreSQL/MongoDB "directory"
|
||||||
|
@ -75,16 +85,23 @@ postgresql_databases:
|
||||||
password: trustsome1
|
password: trustsome1
|
||||||
format: tar
|
format: tar
|
||||||
options: "--role=someone"
|
options: "--role=someone"
|
||||||
|
mariadb_databases:
|
||||||
|
- name: photos
|
||||||
|
hostname: database3.example.org
|
||||||
|
port: 3307
|
||||||
|
username: root
|
||||||
|
password: trustsome1
|
||||||
|
options: "--skip-comments"
|
||||||
mysql_databases:
|
mysql_databases:
|
||||||
- name: posts
|
- name: posts
|
||||||
hostname: database3.example.org
|
hostname: database4.example.org
|
||||||
port: 3307
|
port: 3307
|
||||||
username: root
|
username: root
|
||||||
password: trustsome1
|
password: trustsome1
|
||||||
options: "--skip-comments"
|
options: "--skip-comments"
|
||||||
mongodb_databases:
|
mongodb_databases:
|
||||||
- name: messages
|
- name: messages
|
||||||
hostname: database4.example.org
|
hostname: database5.example.org
|
||||||
port: 27018
|
port: 27018
|
||||||
username: dbuser
|
username: dbuser
|
||||||
password: trustsome1
|
password: trustsome1
|
||||||
|
@ -108,6 +125,8 @@ If you want to dump all databases on a host, use `all` for the database name:
|
||||||
```yaml
|
```yaml
|
||||||
postgresql_databases:
|
postgresql_databases:
|
||||||
- name: all
|
- name: all
|
||||||
|
mariadb_databases:
|
||||||
|
- name: all
|
||||||
mysql_databases:
|
mysql_databases:
|
||||||
- name: all
|
- name: all
|
||||||
mongodb_databases:
|
mongodb_databases:
|
||||||
|
@ -123,15 +142,18 @@ The SQLite hook in particular does not consider "all" a special database name.
|
||||||
these options in the `hooks:` section of your configuration.
|
these options in the `hooks:` section of your configuration.
|
||||||
|
|
||||||
<span class="minilink minilink-addedin">New in version 1.7.6</span> With
|
<span class="minilink minilink-addedin">New in version 1.7.6</span> With
|
||||||
PostgreSQL and MySQL, you can optionally dump "all" databases to separate
|
PostgreSQL, MariaDB, and MySQL, you can optionally dump "all" databases to
|
||||||
files instead of one combined dump file, allowing more convenient restores of
|
separate files instead of one combined dump file, allowing more convenient
|
||||||
individual databases. Enable this by specifying your desired database dump
|
restores of individual databases. Enable this by specifying your desired
|
||||||
`format`:
|
database dump `format`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
postgresql_databases:
|
postgresql_databases:
|
||||||
- name: all
|
- name: all
|
||||||
format: custom
|
format: custom
|
||||||
|
mariadb_databases:
|
||||||
|
- name: all
|
||||||
|
format: sql
|
||||||
mysql_databases:
|
mysql_databases:
|
||||||
- name: all
|
- name: all
|
||||||
format: sql
|
format: sql
|
||||||
|
@ -222,10 +244,16 @@ to prepare for this situation, it's a good idea to include borgmatic's own
|
||||||
configuration files as part of your regular backups. That way, you can always
|
configuration files as part of your regular backups. That way, you can always
|
||||||
bring back any missing configuration files in order to restore a database.
|
bring back any missing configuration files in order to restore a database.
|
||||||
|
|
||||||
|
<span class="minilink minilink-addedin">New in version 1.7.15</span> borgmatic
|
||||||
|
automatically includes configuration files in your backup. See [the
|
||||||
|
documentation on the `config bootstrap`
|
||||||
|
action](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/#extract-the-configuration-files-used-to-create-an-archive)
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
|
||||||
## Supported databases
|
## Supported databases
|
||||||
|
|
||||||
As of now, borgmatic supports PostgreSQL, MySQL/MariaDB, MongoDB, and SQLite
|
As of now, borgmatic supports PostgreSQL, MariaDB, MySQL, MongoDB, and SQLite
|
||||||
databases directly. But see below about general-purpose preparation and
|
databases directly. But see below about general-purpose preparation and
|
||||||
cleanup hooks as a work-around with other database systems. Also, please [file
|
cleanup hooks as a work-around with other database systems. Also, please [file
|
||||||
a ticket](https://torsion.org/borgmatic/#issues) for additional database
|
a ticket](https://torsion.org/borgmatic/#issues) for additional database
|
||||||
|
@ -420,9 +448,9 @@ dumps with any database system.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### PostgreSQL/MySQL authentication errors
|
### Authentication errors
|
||||||
|
|
||||||
With PostgreSQL and MySQL/MariaDB, if you're getting authentication errors
|
With PostgreSQL, MariaDB, and MySQL, if you're getting authentication errors
|
||||||
when borgmatic tries to connect to your database, a natural reaction is to
|
when borgmatic tries to connect to your database, a natural reaction is to
|
||||||
increase your borgmatic verbosity with `--verbosity 2` and go looking in the
|
increase your borgmatic verbosity with `--verbosity 2` and go looking in the
|
||||||
logs. You'll notice though that your database password does not show up in the
|
logs. You'll notice though that your database password does not show up in the
|
||||||
|
@ -436,23 +464,24 @@ authenticated. For instance, with PostgreSQL, check your
|
||||||
[pg_hba.conf](https://www.postgresql.org/docs/current/auth-pg-hba-conf.html)
|
[pg_hba.conf](https://www.postgresql.org/docs/current/auth-pg-hba-conf.html)
|
||||||
file for that configuration.
|
file for that configuration.
|
||||||
|
|
||||||
Additionally, MySQL/MariaDB may be picking up some of your credentials from a
|
Additionally, MariaDB or MySQL may be picking up some of your credentials from
|
||||||
defaults file like `~/.my.cnf`. If that's the case, then it's possible
|
a defaults file like `~/mariadb.cnf` or `~/.my.cnf`. If that's the case, then
|
||||||
MySQL/MariaDB ends up using, say, a username from borgmatic's configuration
|
it's possible MariaDB or MySQL end up using, say, a username from borgmatic's
|
||||||
and a password from `~/.my.cnf`. This may result in authentication errors if
|
configuration and a password from `~/mariadb.cnf` or `~/.my.cnf`. This may
|
||||||
this combination of credentials is not what you intend.
|
result in authentication errors if this combination of credentials is not what
|
||||||
|
you intend.
|
||||||
|
|
||||||
|
|
||||||
### MySQL table lock errors
|
### MariaDB or MySQL table lock errors
|
||||||
|
|
||||||
If you encounter table lock errors during a database dump with MySQL/MariaDB,
|
If you encounter table lock errors during a database dump with MariaDB or
|
||||||
you may need to [use a
|
MySQL, you may need to [use a
|
||||||
transaction](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html#option_mysqldump_single-transaction).
|
transaction](https://mariadb.com/docs/skysql-dbaas/ref/mdb/cli/mariadb-dump/single-transaction/).
|
||||||
You can add any additional flags to the `options:` in your database
|
You can add any additional flags to the `options:` in your database
|
||||||
configuration. Here's an example:
|
configuration. Here's an example with MariaDB:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
mysql_databases:
|
mariadb_databases:
|
||||||
- name: posts
|
- name: posts
|
||||||
options: "--single-transaction --quick"
|
options: "--single-transaction --quick"
|
||||||
```
|
```
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -1,6 +1,6 @@
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
VERSION = '1.8.1'
|
VERSION = '1.8.2.dev0'
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
|
|
@ -12,16 +12,16 @@ services:
|
||||||
POSTGRES_DB: test
|
POSTGRES_DB: test
|
||||||
POSTGRES_USER: postgres2
|
POSTGRES_USER: postgres2
|
||||||
command: docker-entrypoint.sh -p 5433
|
command: docker-entrypoint.sh -p 5433
|
||||||
mysql:
|
mariadb:
|
||||||
image: docker.io/mariadb:10.5
|
image: docker.io/mariadb:10.11.4
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: test
|
MARIADB_ROOT_PASSWORD: test
|
||||||
MYSQL_DATABASE: test
|
MARIADB_DATABASE: test
|
||||||
mysql2:
|
mariadb2:
|
||||||
image: docker.io/mariadb:10.5
|
image: docker.io/mariadb:10.11.4
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: test2
|
MARIADB_ROOT_PASSWORD: test2
|
||||||
MYSQL_DATABASE: test
|
MARIADB_DATABASE: test
|
||||||
command: docker-entrypoint.sh --port=3307
|
command: docker-entrypoint.sh --port=3307
|
||||||
mongodb:
|
mongodb:
|
||||||
image: docker.io/mongo:5.0.5
|
image: docker.io/mongo:5.0.5
|
||||||
|
@ -50,7 +50,7 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgresql
|
- postgresql
|
||||||
- postgresql2
|
- postgresql2
|
||||||
- mysql
|
- mariadb
|
||||||
- mysql2
|
- mariadb2
|
||||||
- mongodb
|
- mongodb
|
||||||
- mongodb2
|
- mongodb2
|
||||||
|
|
|
@ -45,18 +45,32 @@ postgresql_databases:
|
||||||
hostname: postgresql
|
hostname: postgresql
|
||||||
username: postgres
|
username: postgres
|
||||||
password: test
|
password: test
|
||||||
mysql_databases:
|
mariadb_databases:
|
||||||
- name: test
|
- name: test
|
||||||
hostname: mysql
|
hostname: mariadb
|
||||||
username: root
|
username: root
|
||||||
password: test
|
password: test
|
||||||
- name: all
|
- name: all
|
||||||
hostname: mysql
|
hostname: mariadb
|
||||||
username: root
|
username: root
|
||||||
password: test
|
password: test
|
||||||
- name: all
|
- name: all
|
||||||
format: sql
|
format: sql
|
||||||
hostname: mysql
|
hostname: mariadb
|
||||||
|
username: root
|
||||||
|
password: test
|
||||||
|
mysql_databases:
|
||||||
|
- name: test
|
||||||
|
hostname: mariadb
|
||||||
|
username: root
|
||||||
|
password: test
|
||||||
|
- name: all
|
||||||
|
hostname: mariadb
|
||||||
|
username: root
|
||||||
|
password: test
|
||||||
|
- name: all
|
||||||
|
format: sql
|
||||||
|
hostname: mariadb
|
||||||
username: root
|
username: root
|
||||||
password: test
|
password: test
|
||||||
mongodb_databases:
|
mongodb_databases:
|
||||||
|
@ -111,12 +125,21 @@ postgresql_databases:
|
||||||
restore_port: 5433
|
restore_port: 5433
|
||||||
restore_username: postgres2
|
restore_username: postgres2
|
||||||
restore_password: test2
|
restore_password: test2
|
||||||
mysql_databases:
|
mariadb_databases:
|
||||||
- name: test
|
- name: test
|
||||||
hostname: mysql
|
hostname: mariadb
|
||||||
username: root
|
username: root
|
||||||
password: test
|
password: test
|
||||||
restore_hostname: mysql2
|
restore_hostname: mariadb2
|
||||||
|
restore_port: 3307
|
||||||
|
restore_username: root
|
||||||
|
restore_password: test2
|
||||||
|
mysql_databases:
|
||||||
|
- name: test
|
||||||
|
hostname: mariadb
|
||||||
|
username: root
|
||||||
|
password: test
|
||||||
|
restore_hostname: mariadb2
|
||||||
restore_port: 3307
|
restore_port: 3307
|
||||||
restore_username: root
|
restore_username: root
|
||||||
restore_password: test2
|
restore_password: test2
|
||||||
|
|
644
tests/unit/hooks/test_mariadb.py
Normal file
644
tests/unit/hooks/test_mariadb.py
Normal file
|
@ -0,0 +1,644 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flexmock import flexmock
|
||||||
|
|
||||||
|
from borgmatic.hooks import mariadb as module
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_names_to_dump_passes_through_name():
|
||||||
|
extra_environment = flexmock()
|
||||||
|
log_prefix = ''
|
||||||
|
|
||||||
|
names = module.database_names_to_dump(
|
||||||
|
{'name': 'foo'}, extra_environment, log_prefix, dry_run=False
|
||||||
|
)
|
||||||
|
|
||||||
|
assert names == ('foo',)
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_names_to_dump_bails_for_dry_run():
|
||||||
|
extra_environment = flexmock()
|
||||||
|
log_prefix = ''
|
||||||
|
flexmock(module).should_receive('execute_command_and_capture_output').never()
|
||||||
|
|
||||||
|
names = module.database_names_to_dump(
|
||||||
|
{'name': 'all'}, extra_environment, log_prefix, dry_run=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert names == ()
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_names_to_dump_queries_mariadb_for_database_names():
|
||||||
|
extra_environment = flexmock()
|
||||||
|
log_prefix = ''
|
||||||
|
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||||
|
('mariadb', '--skip-column-names', '--batch', '--execute', 'show schemas'),
|
||||||
|
extra_environment=extra_environment,
|
||||||
|
).and_return('foo\nbar\nmysql\n').once()
|
||||||
|
|
||||||
|
names = module.database_names_to_dump(
|
||||||
|
{'name': 'all'}, extra_environment, log_prefix, dry_run=False
|
||||||
|
)
|
||||||
|
|
||||||
|
assert names == ('foo', 'bar')
|
||||||
|
|
||||||
|
|
||||||
|
def test_dump_databases_dumps_each_database():
|
||||||
|
databases = [{'name': 'foo'}, {'name': 'bar'}]
|
||||||
|
processes = [flexmock(), flexmock()]
|
||||||
|
flexmock(module).should_receive('make_dump_path').and_return('')
|
||||||
|
flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
|
||||||
|
('bar',)
|
||||||
|
)
|
||||||
|
|
||||||
|
for name, process in zip(('foo', 'bar'), processes):
|
||||||
|
flexmock(module).should_receive('execute_dump_command').with_args(
|
||||||
|
database={'name': name},
|
||||||
|
log_prefix=object,
|
||||||
|
dump_path=object,
|
||||||
|
database_names=(name,),
|
||||||
|
extra_environment=object,
|
||||||
|
dry_run=object,
|
||||||
|
dry_run_label=object,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == processes
|
||||||
|
|
||||||
|
|
||||||
|
def test_dump_databases_dumps_with_password():
|
||||||
|
database = {'name': 'foo', 'username': 'root', 'password': 'trustsome1'}
|
||||||
|
process = flexmock()
|
||||||
|
flexmock(module).should_receive('make_dump_path').and_return('')
|
||||||
|
flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
|
||||||
|
('bar',)
|
||||||
|
)
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_dump_command').with_args(
|
||||||
|
database=database,
|
||||||
|
log_prefix=object,
|
||||||
|
dump_path=object,
|
||||||
|
database_names=('foo',),
|
||||||
|
extra_environment={'MYSQL_PWD': 'trustsome1'},
|
||||||
|
dry_run=object,
|
||||||
|
dry_run_label=object,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert module.dump_databases([database], {}, 'test.yaml', dry_run=False) == [process]
|
||||||
|
|
||||||
|
|
||||||
|
def test_dump_databases_dumps_all_databases_at_once():
|
||||||
|
databases = [{'name': 'all'}]
|
||||||
|
process = flexmock()
|
||||||
|
flexmock(module).should_receive('make_dump_path').and_return('')
|
||||||
|
flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
|
||||||
|
flexmock(module).should_receive('execute_dump_command').with_args(
|
||||||
|
database={'name': 'all'},
|
||||||
|
log_prefix=object,
|
||||||
|
dump_path=object,
|
||||||
|
database_names=('foo', 'bar'),
|
||||||
|
extra_environment=object,
|
||||||
|
dry_run=object,
|
||||||
|
dry_run_label=object,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
|
||||||
|
|
||||||
|
|
||||||
|
def test_dump_databases_dumps_all_databases_separately_when_format_configured():
|
||||||
|
databases = [{'name': 'all', 'format': 'sql'}]
|
||||||
|
processes = [flexmock(), flexmock()]
|
||||||
|
flexmock(module).should_receive('make_dump_path').and_return('')
|
||||||
|
flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
|
||||||
|
|
||||||
|
for name, process in zip(('foo', 'bar'), processes):
|
||||||
|
flexmock(module).should_receive('execute_dump_command').with_args(
|
||||||
|
database={'name': name, 'format': 'sql'},
|
||||||
|
log_prefix=object,
|
||||||
|
dump_path=object,
|
||||||
|
database_names=(name,),
|
||||||
|
extra_environment=object,
|
||||||
|
dry_run=object,
|
||||||
|
dry_run_label=object,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == processes
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_names_to_dump_runs_mariadb_with_list_options():
|
||||||
|
database = {'name': 'all', 'list_options': '--defaults-extra-file=mariadb.cnf'}
|
||||||
|
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||||
|
(
|
||||||
|
'mariadb',
|
||||||
|
'--defaults-extra-file=mariadb.cnf',
|
||||||
|
'--skip-column-names',
|
||||||
|
'--batch',
|
||||||
|
'--execute',
|
||||||
|
'show schemas',
|
||||||
|
),
|
||||||
|
extra_environment=None,
|
||||||
|
).and_return(('foo\nbar')).once()
|
||||||
|
|
||||||
|
assert module.database_names_to_dump(database, None, 'test.yaml', '') == ('foo', 'bar')
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_dump_command_runs_mariadb_dump():
|
||||||
|
process = flexmock()
|
||||||
|
flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump')
|
||||||
|
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||||
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
|
(
|
||||||
|
'mariadb-dump',
|
||||||
|
'--add-drop-database',
|
||||||
|
'--databases',
|
||||||
|
'foo',
|
||||||
|
'--result-file',
|
||||||
|
'dump',
|
||||||
|
),
|
||||||
|
extra_environment=None,
|
||||||
|
run_to_completion=False,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
module.execute_dump_command(
|
||||||
|
database={'name': 'foo'},
|
||||||
|
log_prefix='log',
|
||||||
|
dump_path=flexmock(),
|
||||||
|
database_names=('foo',),
|
||||||
|
extra_environment=None,
|
||||||
|
dry_run=False,
|
||||||
|
dry_run_label='',
|
||||||
|
)
|
||||||
|
== process
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
|
||||||
|
process = flexmock()
|
||||||
|
flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump')
|
||||||
|
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||||
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
|
(
|
||||||
|
'mariadb-dump',
|
||||||
|
'--databases',
|
||||||
|
'foo',
|
||||||
|
'--result-file',
|
||||||
|
'dump',
|
||||||
|
),
|
||||||
|
extra_environment=None,
|
||||||
|
run_to_completion=False,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
module.execute_dump_command(
|
||||||
|
database={'name': 'foo', 'add_drop_database': False},
|
||||||
|
log_prefix='log',
|
||||||
|
dump_path=flexmock(),
|
||||||
|
database_names=('foo',),
|
||||||
|
extra_environment=None,
|
||||||
|
dry_run=False,
|
||||||
|
dry_run_label='',
|
||||||
|
)
|
||||||
|
== process
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
|
||||||
|
process = flexmock()
|
||||||
|
flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump')
|
||||||
|
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||||
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
|
(
|
||||||
|
'mariadb-dump',
|
||||||
|
'--add-drop-database',
|
||||||
|
'--host',
|
||||||
|
'database.example.org',
|
||||||
|
'--port',
|
||||||
|
'5433',
|
||||||
|
'--protocol',
|
||||||
|
'tcp',
|
||||||
|
'--databases',
|
||||||
|
'foo',
|
||||||
|
'--result-file',
|
||||||
|
'dump',
|
||||||
|
),
|
||||||
|
extra_environment=None,
|
||||||
|
run_to_completion=False,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
module.execute_dump_command(
|
||||||
|
database={'name': 'foo', 'hostname': 'database.example.org', 'port': 5433},
|
||||||
|
log_prefix='log',
|
||||||
|
dump_path=flexmock(),
|
||||||
|
database_names=('foo',),
|
||||||
|
extra_environment=None,
|
||||||
|
dry_run=False,
|
||||||
|
dry_run_label='',
|
||||||
|
)
|
||||||
|
== process
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
|
||||||
|
process = flexmock()
|
||||||
|
flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump')
|
||||||
|
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||||
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
|
(
|
||||||
|
'mariadb-dump',
|
||||||
|
'--add-drop-database',
|
||||||
|
'--user',
|
||||||
|
'root',
|
||||||
|
'--databases',
|
||||||
|
'foo',
|
||||||
|
'--result-file',
|
||||||
|
'dump',
|
||||||
|
),
|
||||||
|
extra_environment={'MYSQL_PWD': 'trustsome1'},
|
||||||
|
run_to_completion=False,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
module.execute_dump_command(
|
||||||
|
database={'name': 'foo', 'username': 'root', 'password': 'trustsome1'},
|
||||||
|
log_prefix='log',
|
||||||
|
dump_path=flexmock(),
|
||||||
|
database_names=('foo',),
|
||||||
|
extra_environment={'MYSQL_PWD': 'trustsome1'},
|
||||||
|
dry_run=False,
|
||||||
|
dry_run_label='',
|
||||||
|
)
|
||||||
|
== process
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_dump_command_runs_mariadb_dump_with_options():
|
||||||
|
process = flexmock()
|
||||||
|
flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump')
|
||||||
|
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||||
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
|
(
|
||||||
|
'mariadb-dump',
|
||||||
|
'--stuff=such',
|
||||||
|
'--add-drop-database',
|
||||||
|
'--databases',
|
||||||
|
'foo',
|
||||||
|
'--result-file',
|
||||||
|
'dump',
|
||||||
|
),
|
||||||
|
extra_environment=None,
|
||||||
|
run_to_completion=False,
|
||||||
|
).and_return(process).once()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
module.execute_dump_command(
|
||||||
|
database={'name': 'foo', 'options': '--stuff=such'},
|
||||||
|
log_prefix='log',
|
||||||
|
dump_path=flexmock(),
|
||||||
|
database_names=('foo',),
|
||||||
|
extra_environment=None,
|
||||||
|
dry_run=False,
|
||||||
|
dry_run_label='',
|
||||||
|
)
|
||||||
|
== process
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_dump_command_with_duplicate_dump_skips_mariadb_dump():
|
||||||
|
flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump')
|
||||||
|
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||||
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
|
||||||
|
flexmock(module).should_receive('execute_command').never()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
module.execute_dump_command(
|
||||||
|
database={'name': 'foo'},
|
||||||
|
log_prefix='log',
|
||||||
|
dump_path=flexmock(),
|
||||||
|
database_names=('foo',),
|
||||||
|
extra_environment=None,
|
||||||
|
dry_run=True,
|
||||||
|
dry_run_label='SO DRY',
|
||||||
|
)
|
||||||
|
is None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
|
||||||
|
flexmock(module.dump).should_receive('make_database_dump_filename').and_return('dump')
|
||||||
|
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||||
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command').never()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
module.execute_dump_command(
|
||||||
|
database={'name': 'foo'},
|
||||||
|
log_prefix='log',
|
||||||
|
dump_path=flexmock(),
|
||||||
|
database_names=('foo',),
|
||||||
|
extra_environment=None,
|
||||||
|
dry_run=True,
|
||||||
|
dry_run_label='SO DRY',
|
||||||
|
)
|
||||||
|
is None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dump_databases_errors_for_missing_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'
|
||||||
|
)
|
||||||
|
flexmock(module).should_receive('database_names_to_dump').and_return(())
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dump_databases_does_not_error_for_missing_all_databases_with_dry_run():
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
flexmock(module).should_receive('database_names_to_dump').and_return(())
|
||||||
|
|
||||||
|
assert module.dump_databases(databases, {}, 'test.yaml', dry_run=True) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_restore_database_dump_runs_mariadb_to_restore():
|
||||||
|
databases_config = [{'name': 'foo'}, {'name': 'bar'}]
|
||||||
|
extract_process = flexmock(stdout=flexmock())
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||||
|
('mariadb', '--batch'),
|
||||||
|
processes=[extract_process],
|
||||||
|
output_log_level=logging.DEBUG,
|
||||||
|
input_file=extract_process.stdout,
|
||||||
|
extra_environment=None,
|
||||||
|
).once()
|
||||||
|
|
||||||
|
module.restore_database_dump(
|
||||||
|
databases_config,
|
||||||
|
{},
|
||||||
|
'test.yaml',
|
||||||
|
database_name='foo',
|
||||||
|
dry_run=False,
|
||||||
|
extract_process=extract_process,
|
||||||
|
connection_params={
|
||||||
|
'hostname': None,
|
||||||
|
'port': None,
|
||||||
|
'username': None,
|
||||||
|
'password': None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_restore_database_dump_errors_when_database_missing_from_configuration():
|
||||||
|
databases_config = [{'name': 'foo'}, {'name': 'bar'}]
|
||||||
|
extract_process = flexmock(stdout=flexmock())
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command_with_processes').never()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
module.restore_database_dump(
|
||||||
|
databases_config,
|
||||||
|
{},
|
||||||
|
'test.yaml',
|
||||||
|
database_name='other',
|
||||||
|
dry_run=False,
|
||||||
|
extract_process=extract_process,
|
||||||
|
connection_params={
|
||||||
|
'hostname': None,
|
||||||
|
'port': None,
|
||||||
|
'username': None,
|
||||||
|
'password': None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_restore_database_dump_runs_mariadb_with_options():
|
||||||
|
databases_config = [{'name': 'foo', 'restore_options': '--harder'}]
|
||||||
|
extract_process = flexmock(stdout=flexmock())
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||||
|
('mariadb', '--batch', '--harder'),
|
||||||
|
processes=[extract_process],
|
||||||
|
output_log_level=logging.DEBUG,
|
||||||
|
input_file=extract_process.stdout,
|
||||||
|
extra_environment=None,
|
||||||
|
).once()
|
||||||
|
|
||||||
|
module.restore_database_dump(
|
||||||
|
databases_config,
|
||||||
|
{},
|
||||||
|
'test.yaml',
|
||||||
|
database_name='foo',
|
||||||
|
dry_run=False,
|
||||||
|
extract_process=extract_process,
|
||||||
|
connection_params={
|
||||||
|
'hostname': None,
|
||||||
|
'port': None,
|
||||||
|
'username': None,
|
||||||
|
'password': None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_restore_database_dump_runs_mariadb_with_hostname_and_port():
|
||||||
|
databases_config = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
|
||||||
|
extract_process = flexmock(stdout=flexmock())
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||||
|
(
|
||||||
|
'mariadb',
|
||||||
|
'--batch',
|
||||||
|
'--host',
|
||||||
|
'database.example.org',
|
||||||
|
'--port',
|
||||||
|
'5433',
|
||||||
|
'--protocol',
|
||||||
|
'tcp',
|
||||||
|
),
|
||||||
|
processes=[extract_process],
|
||||||
|
output_log_level=logging.DEBUG,
|
||||||
|
input_file=extract_process.stdout,
|
||||||
|
extra_environment=None,
|
||||||
|
).once()
|
||||||
|
|
||||||
|
module.restore_database_dump(
|
||||||
|
databases_config,
|
||||||
|
{},
|
||||||
|
'test.yaml',
|
||||||
|
database_name='foo',
|
||||||
|
dry_run=False,
|
||||||
|
extract_process=extract_process,
|
||||||
|
connection_params={
|
||||||
|
'hostname': None,
|
||||||
|
'port': None,
|
||||||
|
'username': None,
|
||||||
|
'password': None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_restore_database_dump_runs_mariadb_with_username_and_password():
|
||||||
|
databases_config = [{'name': 'foo', 'username': 'root', 'password': 'trustsome1'}]
|
||||||
|
extract_process = flexmock(stdout=flexmock())
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||||
|
('mariadb', '--batch', '--user', 'root'),
|
||||||
|
processes=[extract_process],
|
||||||
|
output_log_level=logging.DEBUG,
|
||||||
|
input_file=extract_process.stdout,
|
||||||
|
extra_environment={'MYSQL_PWD': 'trustsome1'},
|
||||||
|
).once()
|
||||||
|
|
||||||
|
module.restore_database_dump(
|
||||||
|
databases_config,
|
||||||
|
{},
|
||||||
|
'test.yaml',
|
||||||
|
database_name='foo',
|
||||||
|
dry_run=False,
|
||||||
|
extract_process=extract_process,
|
||||||
|
connection_params={
|
||||||
|
'hostname': None,
|
||||||
|
'port': None,
|
||||||
|
'username': None,
|
||||||
|
'password': None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_restore_database_dump_with_connection_params_uses_connection_params_for_restore():
|
||||||
|
databases_config = [
|
||||||
|
{
|
||||||
|
'name': 'foo',
|
||||||
|
'username': 'root',
|
||||||
|
'password': 'trustsome1',
|
||||||
|
'restore_hostname': 'restorehost',
|
||||||
|
'restore_port': 'restoreport',
|
||||||
|
'restore_username': 'restoreusername',
|
||||||
|
'restore_password': 'restorepassword',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
extract_process = flexmock(stdout=flexmock())
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||||
|
(
|
||||||
|
'mariadb',
|
||||||
|
'--batch',
|
||||||
|
'--host',
|
||||||
|
'clihost',
|
||||||
|
'--port',
|
||||||
|
'cliport',
|
||||||
|
'--protocol',
|
||||||
|
'tcp',
|
||||||
|
'--user',
|
||||||
|
'cliusername',
|
||||||
|
),
|
||||||
|
processes=[extract_process],
|
||||||
|
output_log_level=logging.DEBUG,
|
||||||
|
input_file=extract_process.stdout,
|
||||||
|
extra_environment={'MYSQL_PWD': 'clipassword'},
|
||||||
|
).once()
|
||||||
|
|
||||||
|
module.restore_database_dump(
|
||||||
|
databases_config,
|
||||||
|
{},
|
||||||
|
'test.yaml',
|
||||||
|
database_name='foo',
|
||||||
|
dry_run=False,
|
||||||
|
extract_process=extract_process,
|
||||||
|
connection_params={
|
||||||
|
'hostname': 'clihost',
|
||||||
|
'port': 'cliport',
|
||||||
|
'username': 'cliusername',
|
||||||
|
'password': 'clipassword',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_restore_database_dump_without_connection_params_uses_restore_params_in_config_for_restore():
|
||||||
|
databases_config = [
|
||||||
|
{
|
||||||
|
'name': 'foo',
|
||||||
|
'username': 'root',
|
||||||
|
'password': 'trustsome1',
|
||||||
|
'hostname': 'dbhost',
|
||||||
|
'port': 'dbport',
|
||||||
|
'restore_username': 'restoreuser',
|
||||||
|
'restore_password': 'restorepass',
|
||||||
|
'restore_hostname': 'restorehost',
|
||||||
|
'restore_port': 'restoreport',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
extract_process = flexmock(stdout=flexmock())
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||||
|
(
|
||||||
|
'mariadb',
|
||||||
|
'--batch',
|
||||||
|
'--host',
|
||||||
|
'restorehost',
|
||||||
|
'--port',
|
||||||
|
'restoreport',
|
||||||
|
'--protocol',
|
||||||
|
'tcp',
|
||||||
|
'--user',
|
||||||
|
'restoreuser',
|
||||||
|
),
|
||||||
|
processes=[extract_process],
|
||||||
|
output_log_level=logging.DEBUG,
|
||||||
|
input_file=extract_process.stdout,
|
||||||
|
extra_environment={'MYSQL_PWD': 'restorepass'},
|
||||||
|
).once()
|
||||||
|
|
||||||
|
module.restore_database_dump(
|
||||||
|
databases_config,
|
||||||
|
{},
|
||||||
|
'test.yaml',
|
||||||
|
database_name='foo',
|
||||||
|
dry_run=False,
|
||||||
|
extract_process=extract_process,
|
||||||
|
connection_params={
|
||||||
|
'hostname': None,
|
||||||
|
'port': None,
|
||||||
|
'username': None,
|
||||||
|
'password': None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_restore_database_dump_with_dry_run_skips_restore():
|
||||||
|
databases_config = [{'name': 'foo'}]
|
||||||
|
|
||||||
|
flexmock(module).should_receive('execute_command_with_processes').never()
|
||||||
|
|
||||||
|
module.restore_database_dump(
|
||||||
|
databases_config,
|
||||||
|
{},
|
||||||
|
'test.yaml',
|
||||||
|
database_name='foo',
|
||||||
|
dry_run=True,
|
||||||
|
extract_process=flexmock(),
|
||||||
|
connection_params={
|
||||||
|
'hostname': None,
|
||||||
|
'port': None,
|
||||||
|
'username': None,
|
||||||
|
'password': None,
|
||||||
|
},
|
||||||
|
)
|
|
@ -17,7 +17,7 @@ def test_dump_databases_runs_mongodump_for_each_database():
|
||||||
|
|
||||||
for name, process in zip(('foo', 'bar'), processes):
|
for name, process in zip(('foo', 'bar'), processes):
|
||||||
flexmock(module).should_receive('execute_command').with_args(
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
['mongodump', '--db', name, '--archive', '>', f'databases/localhost/{name}'],
|
('mongodump', '--db', name, '--archive', '>', f'databases/localhost/{name}'),
|
||||||
shell=True,
|
shell=True,
|
||||||
run_to_completion=False,
|
run_to_completion=False,
|
||||||
).and_return(process).once()
|
).and_return(process).once()
|
||||||
|
@ -47,7 +47,7 @@ def test_dump_databases_runs_mongodump_with_hostname_and_port():
|
||||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
flexmock(module).should_receive('execute_command').with_args(
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
[
|
(
|
||||||
'mongodump',
|
'mongodump',
|
||||||
'--host',
|
'--host',
|
||||||
'database.example.org',
|
'database.example.org',
|
||||||
|
@ -58,7 +58,7 @@ def test_dump_databases_runs_mongodump_with_hostname_and_port():
|
||||||
'--archive',
|
'--archive',
|
||||||
'>',
|
'>',
|
||||||
'databases/database.example.org/foo',
|
'databases/database.example.org/foo',
|
||||||
],
|
),
|
||||||
shell=True,
|
shell=True,
|
||||||
run_to_completion=False,
|
run_to_completion=False,
|
||||||
).and_return(process).once()
|
).and_return(process).once()
|
||||||
|
@ -83,7 +83,7 @@ def test_dump_databases_runs_mongodump_with_username_and_password():
|
||||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
flexmock(module).should_receive('execute_command').with_args(
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
[
|
(
|
||||||
'mongodump',
|
'mongodump',
|
||||||
'--username',
|
'--username',
|
||||||
'mongo',
|
'mongo',
|
||||||
|
@ -96,7 +96,7 @@ def test_dump_databases_runs_mongodump_with_username_and_password():
|
||||||
'--archive',
|
'--archive',
|
||||||
'>',
|
'>',
|
||||||
'databases/localhost/foo',
|
'databases/localhost/foo',
|
||||||
],
|
),
|
||||||
shell=True,
|
shell=True,
|
||||||
run_to_completion=False,
|
run_to_completion=False,
|
||||||
).and_return(process).once()
|
).and_return(process).once()
|
||||||
|
@ -114,7 +114,7 @@ def test_dump_databases_runs_mongodump_with_directory_format():
|
||||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
|
||||||
|
|
||||||
flexmock(module).should_receive('execute_command').with_args(
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
['mongodump', '--out', 'databases/localhost/foo', '--db', 'foo'],
|
('mongodump', '--out', 'databases/localhost/foo', '--db', 'foo'),
|
||||||
shell=True,
|
shell=True,
|
||||||
).and_return(flexmock()).once()
|
).and_return(flexmock()).once()
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ def test_dump_databases_runs_mongodump_with_options():
|
||||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
flexmock(module).should_receive('execute_command').with_args(
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
['mongodump', '--db', 'foo', '--stuff=such', '--archive', '>', 'databases/localhost/foo'],
|
('mongodump', '--db', 'foo', '--stuff=such', '--archive', '>', 'databases/localhost/foo'),
|
||||||
shell=True,
|
shell=True,
|
||||||
run_to_completion=False,
|
run_to_completion=False,
|
||||||
).and_return(process).once()
|
).and_return(process).once()
|
||||||
|
@ -149,7 +149,7 @@ def test_dump_databases_runs_mongodumpall_for_all_databases():
|
||||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||||
|
|
||||||
flexmock(module).should_receive('execute_command').with_args(
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
['mongodump', '--archive', '>', 'databases/localhost/all'],
|
('mongodump', '--archive', '>', 'databases/localhost/all'),
|
||||||
shell=True,
|
shell=True,
|
||||||
run_to_completion=False,
|
run_to_completion=False,
|
||||||
).and_return(process).once()
|
).and_return(process).once()
|
||||||
|
|
Loading…
Reference in a new issue