Add a "compression" option to the PostgreSQL database hook (#975).

This commit is contained in:
Dan Helfman
2025-03-01 17:29:16 -08:00
parent 25b6a49df7
commit 54afe87a9f
4 changed files with 108 additions and 0 deletions
+1
View File
@@ -1,4 +1,5 @@
1.9.13.dev0
* #975: Add a "compression" option to the PostgreSQL database hook.
* #1001: Fix a ZFS error during snapshot cleanup.
* #1003: In the Zabbix monitoring hook, support Zabbix 7.2's authentication changes.
* #1009: Send database passwords to MariaDB and MySQL via anonymous pipe, which is more secure than
+11
View File
@@ -1040,6 +1040,17 @@ properties:
individual databases. See the pg_dump documentation for
more about formats.
example: directory
compression:
type: ["string", "integer"]
description: |
Database dump compression level (integer) or method
("gzip", "lz4", "zstd", or "none") and optional
colon-separated detail. Defaults to moderate "gzip" for
"custom" and "directory" formats and no compression for
the "plain" format. Compression is not supported for the
"tar" format. Be aware that Borg does its own
compression as well, so you may not need it in both
places.
ssl_mode:
type: string
enum: ['disable', 'allow', 'prefer',
@@ -159,6 +159,7 @@ def dump_data_sources(
for database_name in dump_database_names:
dump_format = database.get('format', None if database_name == 'all' else 'custom')
compression = database.get('compression')
default_dump_command = 'pg_dumpall' if database_name == 'all' else 'pg_dump'
dump_command = tuple(
shlex.quote(part)
@@ -199,6 +200,7 @@ def dump_data_sources(
)
+ (('--no-owner',) if database.get('no_owner', False) else ())
+ (('--format', shlex.quote(dump_format)) if dump_format else ())
+ (('--compress', shlex.quote(str(compression))) if compression is not None else ())
+ (('--file', shlex.quote(dump_filename)) if dump_format == 'directory' else ())
+ (
tuple(shlex.quote(option) for option in database['options'].split(' '))
@@ -555,6 +555,100 @@ def test_dump_data_sources_runs_pg_dump_with_directory_format():
)
def test_dump_data_sources_runs_pg_dump_with_string_compression():
databases = [{'name': 'foo', 'compression': 'winrar'}]
processes = [flexmock()]
flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
flexmock(module).should_receive('make_dump_path').and_return('')
flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
'databases/localhost/foo'
)
flexmock(module.os.path).should_receive('exists').and_return(False)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential'
).replace_with(lambda value, config: value)
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
flexmock(module).should_receive('execute_command').with_args(
(
'pg_dump',
'--no-password',
'--clean',
'--if-exists',
'--format',
'custom',
'--compress',
'winrar',
'foo',
'>',
'databases/localhost/foo',
),
shell=True,
environment={'PGSSLMODE': 'disable'},
run_to_completion=False,
).and_return(processes[0]).once()
assert (
module.dump_data_sources(
databases,
{},
config_paths=('test.yaml',),
borgmatic_runtime_directory='/run/borgmatic',
patterns=[],
dry_run=False,
)
== processes
)
def test_dump_data_sources_runs_pg_dump_with_integer_compression():
databases = [{'name': 'foo', 'compression': 0}]
processes = [flexmock()]
flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
flexmock(module).should_receive('make_dump_path').and_return('')
flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
'databases/localhost/foo'
)
flexmock(module.os.path).should_receive('exists').and_return(False)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential'
).replace_with(lambda value, config: value)
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
flexmock(module).should_receive('execute_command').with_args(
(
'pg_dump',
'--no-password',
'--clean',
'--if-exists',
'--format',
'custom',
'--compress',
'0',
'foo',
'>',
'databases/localhost/foo',
),
shell=True,
environment={'PGSSLMODE': 'disable'},
run_to_completion=False,
).and_return(processes[0]).once()
assert (
module.dump_data_sources(
databases,
{},
config_paths=('test.yaml',),
borgmatic_runtime_directory='/run/borgmatic',
patterns=[],
dry_run=False,
)
== processes
)
def test_dump_data_sources_runs_pg_dump_with_options():
databases = [{'name': 'foo', 'options': '--stuff=such'}]
process = flexmock()