浏览代码

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

Dan Helfman 3 月之前
父节点
当前提交
54afe87a9f
共有 4 个文件被更改,包括 108 次插入0 次删除
  1. 1 0
      NEWS
  2. 11 0
      borgmatic/config/schema.yaml
  3. 2 0
      borgmatic/hooks/data_source/postgresql.py
  4. 94 0
      tests/unit/hooks/data_source/test_postgresql.py

+ 1 - 0
NEWS

@@ -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 - 0
borgmatic/config/schema.yaml

@@ -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',

+ 2 - 0
borgmatic/hooks/data_source/postgresql.py

@@ -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(' '))

+ 94 - 0
tests/unit/hooks/data_source/test_postgresql.py

@@ -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()