Browse Source

Remove the "dump_data_sources" command hook, as it doesn't really solve the use case and works differently than all the other command hooks (#790).

Dan Helfman 2 months ago
parent
commit
c2409d9968

+ 2 - 32
borgmatic/config/schema.yaml

@@ -959,7 +959,6 @@ properties:
                               - repository
                               - repository
                               - configuration
                               - configuration
                               - everything
                               - everything
-                              - dump_data_sources
                           description: |
                           description: |
                               Name for the point in borgmatic's execution that
                               Name for the point in borgmatic's execution that
                               the commands should be run before (required if
                               the commands should be run before (required if
@@ -972,19 +971,7 @@ properties:
                               repositories in the current configuration file.
                               repositories in the current configuration file.
                                * "everything" runs before all configuration
                                * "everything" runs before all configuration
                               files.
                               files.
-                               * "dump_data_sources" runs before each data
-                              source is dumped.
                           example: action
                           example: action
-                      hooks:
-                          type: array
-                          items:
-                              type: string
-                          description: |
-                              List of names of other hooks that this command
-                              hook applies to. Defaults to all hooks of the
-                              relevant type. Only supported for the
-                              "dump_data_sources" hook.
-                          example: postgresql
                       when:
                       when:
                           type: array
                           type: array
                           items:
                           items:
@@ -1013,9 +1000,7 @@ properties:
                                   - borg
                                   - borg
                           description: |
                           description: |
                               List of actions for which the commands will be
                               List of actions for which the commands will be
-                              run. Defaults to running for all actions. Ignored
-                              for "dump_data_sources", which by its nature only
-                              runs for "create".
+                              run. Defaults to running for all actions.
                           example: [create, prune, compact, check]
                           example: [create, prune, compact, check]
                       run:
                       run:
                           type: array
                           type: array
@@ -1037,7 +1022,6 @@ properties:
                               - configuration
                               - configuration
                               - everything
                               - everything
                               - error
                               - error
-                              - dump_data_sources
                           description: |
                           description: |
                               Name for the point in borgmatic's execution that
                               Name for the point in borgmatic's execution that
                               the commands should be run after (required if
                               the commands should be run after (required if
@@ -1051,19 +1035,7 @@ properties:
                                * "everything" runs after all configuration
                                * "everything" runs after all configuration
                               files.
                               files.
                                * "error" runs after an error occurs.
                                * "error" runs after an error occurs.
-                               * "dump_data_sources" runs after each data
-                              source is dumped.
                           example: action
                           example: action
-                      hooks:
-                          type: array
-                          items:
-                              type: string
-                          description: |
-                              List of names of other hooks that this command
-                              hook applies to. Defaults to all hooks of the
-                              relevant type. Only supported for the
-                              "dump_data_sources" hook.
-                          example: postgresql
                       when:
                       when:
                           type: array
                           type: array
                           items:
                           items:
@@ -1093,9 +1065,7 @@ properties:
                           description: |
                           description: |
                               Only trigger the hook when borgmatic is run with
                               Only trigger the hook when borgmatic is run with
                               particular actions listed here. Defaults to
                               particular actions listed here. Defaults to
-                              running for all actions. Ignored for
-                              "dump_data_sources", which by its nature only runs
-                              for "create".
+                              running for all actions.
                           example: [create, prune, compact, check]
                           example: [create, prune, compact, check]
                       run:
                       run:
                           type: array
                           type: array

+ 0 - 2
borgmatic/hooks/command.py

@@ -55,11 +55,9 @@ def filter_hooks(command_hooks, before=None, after=None, hook_name=None, action_
     return tuple(
     return tuple(
         hook_config
         hook_config
         for hook_config in command_hooks or ()
         for hook_config in command_hooks or ()
-        for config_hook_names in (hook_config.get('hooks'),)
         for config_action_names in (hook_config.get('when'),)
         for config_action_names in (hook_config.get('when'),)
         if before is None or hook_config.get('before') == before
         if before is None or hook_config.get('before') == before
         if after is None or hook_config.get('after') == after
         if after is None or hook_config.get('after') == after
-        if hook_name is None or config_hook_names is None or hook_name in config_hook_names
         if action_names is None
         if action_names is None
         or config_action_names is None
         or config_action_names is None
         or set(config_action_names or ()).intersection(set(action_names))
         or set(config_action_names or ()).intersection(set(action_names))

+ 27 - 35
borgmatic/hooks/data_source/bootstrap.py

@@ -6,7 +6,6 @@ import os
 
 
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
-import borgmatic.hooks.command
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -38,45 +37,38 @@ def dump_data_sources(
     if hook_config and hook_config.get('store_config_files') is False:
     if hook_config and hook_config.get('store_config_files') is False:
         return []
         return []
 
 
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='bootstrap',
-    ):
-        borgmatic_manifest_path = os.path.join(
-            borgmatic_runtime_directory, 'bootstrap', 'manifest.json'
+    borgmatic_manifest_path = os.path.join(
+        borgmatic_runtime_directory, 'bootstrap', 'manifest.json'
+    )
+
+    if dry_run:
+        return []
+
+    os.makedirs(os.path.dirname(borgmatic_manifest_path), exist_ok=True)
+
+    with open(borgmatic_manifest_path, 'w') as manifest_file:
+        json.dump(
+            {
+                'borgmatic_version': importlib.metadata.version('borgmatic'),
+                'config_paths': config_paths,
+            },
+            manifest_file,
         )
         )
 
 
-        if dry_run:
-            return []
-
-        os.makedirs(os.path.dirname(borgmatic_manifest_path), exist_ok=True)
-
-        with open(borgmatic_manifest_path, 'w') as manifest_file:
-            json.dump(
-                {
-                    'borgmatic_version': importlib.metadata.version('borgmatic'),
-                    'config_paths': config_paths,
-                },
-                manifest_file,
-            )
-
-        patterns.extend(
-            borgmatic.borg.pattern.Pattern(
-                config_path, source=borgmatic.borg.pattern.Pattern_source.HOOK
-            )
-            for config_path in config_paths
+    patterns.extend(
+        borgmatic.borg.pattern.Pattern(
+            config_path, source=borgmatic.borg.pattern.Pattern_source.HOOK
         )
         )
-        patterns.append(
-            borgmatic.borg.pattern.Pattern(
-                os.path.join(borgmatic_runtime_directory, 'bootstrap'),
-                source=borgmatic.borg.pattern.Pattern_source.HOOK,
-            )
+        for config_path in config_paths
+    )
+    patterns.append(
+        borgmatic.borg.pattern.Pattern(
+            os.path.join(borgmatic_runtime_directory, 'bootstrap'),
+            source=borgmatic.borg.pattern.Pattern_source.HOOK,
         )
         )
+    )
 
 
-        return []
+    return []
 
 
 
 
 def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, dry_run):
 def remove_data_source_dumps(hook_config, config, borgmatic_runtime_directory, dry_run):

+ 25 - 33
borgmatic/hooks/data_source/btrfs.py

@@ -9,7 +9,6 @@ import subprocess
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
 import borgmatic.execute
 import borgmatic.execute
-import borgmatic.hooks.command
 import borgmatic.hooks.data_source.snapshot
 import borgmatic.hooks.data_source.snapshot
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
@@ -250,48 +249,41 @@ def dump_data_sources(
 
 
     If this is a dry run, then don't actually snapshot anything.
     If this is a dry run, then don't actually snapshot anything.
     '''
     '''
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='btrfs',
-    ):
-        dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
-        logger.info(f'Snapshotting Btrfs subvolumes{dry_run_label}')
+    dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
+    logger.info(f'Snapshotting Btrfs subvolumes{dry_run_label}')
 
 
-        # Based on the configured patterns, determine Btrfs subvolumes to backup. Only consider those
-        # patterns that came from actual user configuration (as opposed to, say, other hooks).
-        btrfs_command = hook_config.get('btrfs_command', 'btrfs')
-        findmnt_command = hook_config.get('findmnt_command', 'findmnt')
-        subvolumes = get_subvolumes(btrfs_command, findmnt_command, patterns)
+    # Based on the configured patterns, determine Btrfs subvolumes to backup. Only consider those
+    # patterns that came from actual user configuration (as opposed to, say, other hooks).
+    btrfs_command = hook_config.get('btrfs_command', 'btrfs')
+    findmnt_command = hook_config.get('findmnt_command', 'findmnt')
+    subvolumes = get_subvolumes(btrfs_command, findmnt_command, patterns)
 
 
-        if not subvolumes:
-            logger.warning(f'No Btrfs subvolumes found to snapshot{dry_run_label}')
+    if not subvolumes:
+        logger.warning(f'No Btrfs subvolumes found to snapshot{dry_run_label}')
 
 
-        # Snapshot each subvolume, rewriting patterns to use their snapshot paths.
-        for subvolume in subvolumes:
-            logger.debug(f'Creating Btrfs snapshot for {subvolume.path} subvolume')
+    # Snapshot each subvolume, rewriting patterns to use their snapshot paths.
+    for subvolume in subvolumes:
+        logger.debug(f'Creating Btrfs snapshot for {subvolume.path} subvolume')
 
 
-            snapshot_path = make_snapshot_path(subvolume.path)
+        snapshot_path = make_snapshot_path(subvolume.path)
 
 
-            if dry_run:
-                continue
+        if dry_run:
+            continue
 
 
-            snapshot_subvolume(btrfs_command, subvolume.path, snapshot_path)
+        snapshot_subvolume(btrfs_command, subvolume.path, snapshot_path)
 
 
-            for pattern in subvolume.contained_patterns:
-                snapshot_pattern = make_borg_snapshot_pattern(subvolume.path, pattern)
+        for pattern in subvolume.contained_patterns:
+            snapshot_pattern = make_borg_snapshot_pattern(subvolume.path, pattern)
 
 
-                # Attempt to update the pattern in place, since pattern order matters to Borg.
-                try:
-                    patterns[patterns.index(pattern)] = snapshot_pattern
-                except ValueError:
-                    patterns.append(snapshot_pattern)
+            # Attempt to update the pattern in place, since pattern order matters to Borg.
+            try:
+                patterns[patterns.index(pattern)] = snapshot_pattern
+            except ValueError:
+                patterns.append(snapshot_pattern)
 
 
-            patterns.append(make_snapshot_exclude_pattern(subvolume.path))
+        patterns.append(make_snapshot_exclude_pattern(subvolume.path))
 
 
-        return []
+    return []
 
 
 
 
 def delete_snapshot(btrfs_command, snapshot_path):  # pragma: no cover
 def delete_snapshot(btrfs_command, snapshot_path):  # pragma: no cover

+ 62 - 70
borgmatic/hooks/data_source/lvm.py

@@ -10,7 +10,6 @@ import subprocess
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
 import borgmatic.execute
 import borgmatic.execute
-import borgmatic.hooks.command
 import borgmatic.hooks.data_source.snapshot
 import borgmatic.hooks.data_source.snapshot
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
@@ -198,84 +197,77 @@ def dump_data_sources(
 
 
     If this is a dry run, then don't actually snapshot anything.
     If this is a dry run, then don't actually snapshot anything.
     '''
     '''
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='lvm',
-    ):
-        dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
-        logger.info(f'Snapshotting LVM logical volumes{dry_run_label}')
-
-        # List logical volumes to get their mount points, but only consider those patterns that came
-        # from actual user configuration (as opposed to, say, other hooks).
-        lsblk_command = hook_config.get('lsblk_command', 'lsblk')
-        requested_logical_volumes = get_logical_volumes(lsblk_command, patterns)
-
-        # Snapshot each logical volume, rewriting source directories to use the snapshot paths.
-        snapshot_suffix = f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}'
-        normalized_runtime_directory = os.path.normpath(borgmatic_runtime_directory)
-
-        if not requested_logical_volumes:
-            logger.warning(f'No LVM logical volumes found to snapshot{dry_run_label}')
-
-        for logical_volume in requested_logical_volumes:
-            snapshot_name = f'{logical_volume.name}_{snapshot_suffix}'
-            logger.debug(
-                f'Creating LVM snapshot {snapshot_name} of {logical_volume.mount_point}{dry_run_label}'
-            )
+    dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
+    logger.info(f'Snapshotting LVM logical volumes{dry_run_label}')
 
 
-            if not dry_run:
-                snapshot_logical_volume(
-                    hook_config.get('lvcreate_command', 'lvcreate'),
-                    snapshot_name,
-                    logical_volume.device_path,
-                    hook_config.get('snapshot_size', DEFAULT_SNAPSHOT_SIZE),
-                )
+    # List logical volumes to get their mount points, but only consider those patterns that came
+    # from actual user configuration (as opposed to, say, other hooks).
+    lsblk_command = hook_config.get('lsblk_command', 'lsblk')
+    requested_logical_volumes = get_logical_volumes(lsblk_command, patterns)
 
 
-            # Get the device path for the snapshot we just created.
-            try:
-                snapshot = get_snapshots(
-                    hook_config.get('lvs_command', 'lvs'), snapshot_name=snapshot_name
-                )[0]
-            except IndexError:
-                raise ValueError(f'Cannot find LVM snapshot {snapshot_name}')
-
-            # Mount the snapshot into a particular named temporary directory so that the snapshot ends
-            # up in the Borg archive at the "original" logical volume mount point path.
-            snapshot_mount_path = os.path.join(
-                normalized_runtime_directory,
-                'lvm_snapshots',
-                hashlib.shake_256(logical_volume.mount_point.encode('utf-8')).hexdigest(
-                    MOUNT_POINT_HASH_LENGTH
-                ),
-                logical_volume.mount_point.lstrip(os.path.sep),
-            )
+    # Snapshot each logical volume, rewriting source directories to use the snapshot paths.
+    snapshot_suffix = f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}'
+    normalized_runtime_directory = os.path.normpath(borgmatic_runtime_directory)
 
 
-            logger.debug(
-                f'Mounting LVM snapshot {snapshot_name} at {snapshot_mount_path}{dry_run_label}'
-            )
+    if not requested_logical_volumes:
+        logger.warning(f'No LVM logical volumes found to snapshot{dry_run_label}')
 
 
-            if dry_run:
-                continue
+    for logical_volume in requested_logical_volumes:
+        snapshot_name = f'{logical_volume.name}_{snapshot_suffix}'
+        logger.debug(
+            f'Creating LVM snapshot {snapshot_name} of {logical_volume.mount_point}{dry_run_label}'
+        )
 
 
-            mount_snapshot(
-                hook_config.get('mount_command', 'mount'), snapshot.device_path, snapshot_mount_path
+        if not dry_run:
+            snapshot_logical_volume(
+                hook_config.get('lvcreate_command', 'lvcreate'),
+                snapshot_name,
+                logical_volume.device_path,
+                hook_config.get('snapshot_size', DEFAULT_SNAPSHOT_SIZE),
             )
             )
 
 
-            for pattern in logical_volume.contained_patterns:
-                snapshot_pattern = make_borg_snapshot_pattern(
-                    pattern, logical_volume, normalized_runtime_directory
-                )
+        # Get the device path for the snapshot we just created.
+        try:
+            snapshot = get_snapshots(
+                hook_config.get('lvs_command', 'lvs'), snapshot_name=snapshot_name
+            )[0]
+        except IndexError:
+            raise ValueError(f'Cannot find LVM snapshot {snapshot_name}')
+
+        # Mount the snapshot into a particular named temporary directory so that the snapshot ends
+        # up in the Borg archive at the "original" logical volume mount point path.
+        snapshot_mount_path = os.path.join(
+            normalized_runtime_directory,
+            'lvm_snapshots',
+            hashlib.shake_256(logical_volume.mount_point.encode('utf-8')).hexdigest(
+                MOUNT_POINT_HASH_LENGTH
+            ),
+            logical_volume.mount_point.lstrip(os.path.sep),
+        )
+
+        logger.debug(
+            f'Mounting LVM snapshot {snapshot_name} at {snapshot_mount_path}{dry_run_label}'
+        )
+
+        if dry_run:
+            continue
 
 
-                # Attempt to update the pattern in place, since pattern order matters to Borg.
-                try:
-                    patterns[patterns.index(pattern)] = snapshot_pattern
-                except ValueError:
-                    patterns.append(snapshot_pattern)
+        mount_snapshot(
+            hook_config.get('mount_command', 'mount'), snapshot.device_path, snapshot_mount_path
+        )
+
+        for pattern in logical_volume.contained_patterns:
+            snapshot_pattern = make_borg_snapshot_pattern(
+                pattern, logical_volume, normalized_runtime_directory
+            )
+
+            # Attempt to update the pattern in place, since pattern order matters to Borg.
+            try:
+                patterns[patterns.index(pattern)] = snapshot_pattern
+            except ValueError:
+                patterns.append(snapshot_pattern)
 
 
-        return []
+    return []
 
 
 
 
 def unmount_snapshot(umount_command, snapshot_mount_path):  # pragma: no cover
 def unmount_snapshot(umount_command, snapshot_mount_path):  # pragma: no cover

+ 50 - 58
borgmatic/hooks/data_source/mariadb.py

@@ -6,7 +6,6 @@ import shlex
 
 
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
-import borgmatic.hooks.command
 import borgmatic.hooks.credential.parse
 import borgmatic.hooks.credential.parse
 from borgmatic.execute import (
 from borgmatic.execute import (
     execute_command,
     execute_command,
@@ -243,78 +242,71 @@ def dump_data_sources(
     Also append the the parent directory of the database dumps to the given patterns list, so the
     Also append the the parent directory of the database dumps to the given patterns list, so the
     dumps actually get backed up.
     dumps actually get backed up.
     '''
     '''
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='mariadb',
-    ):
-        dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
-        processes = []
-
-        logger.info(f'Dumping MariaDB databases{dry_run_label}')
-
-        for database in databases:
-            dump_path = make_dump_path(borgmatic_runtime_directory)
-            username = borgmatic.hooks.credential.parse.resolve_credential(
-                database.get('username'), config
-            )
-            password = borgmatic.hooks.credential.parse.resolve_credential(
-                database.get('password'), config
-            )
-            environment = dict(os.environ)
-            dump_database_names = database_names_to_dump(
-                database, config, username, password, environment, dry_run
-            )
+    dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
+    processes = []
 
 
-            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,
-                            config,
-                            username,
-                            password,
-                            dump_path,
-                            (dump_name,),
-                            environment,
-                            dry_run,
-                            dry_run_label,
-                        )
-                    )
-            else:
+    logger.info(f'Dumping MariaDB databases{dry_run_label}')
+
+    for database in databases:
+        dump_path = make_dump_path(borgmatic_runtime_directory)
+        username = borgmatic.hooks.credential.parse.resolve_credential(
+            database.get('username'), config
+        )
+        password = borgmatic.hooks.credential.parse.resolve_credential(
+            database.get('password'), config
+        )
+        environment = dict(os.environ)
+        dump_database_names = database_names_to_dump(
+            database, config, username, password, environment, 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(
                 processes.append(
                     execute_dump_command(
                     execute_dump_command(
-                        database,
+                        renamed_database,
                         config,
                         config,
                         username,
                         username,
                         password,
                         password,
                         dump_path,
                         dump_path,
-                        dump_database_names,
+                        (dump_name,),
                         environment,
                         environment,
                         dry_run,
                         dry_run,
                         dry_run_label,
                         dry_run_label,
                     )
                     )
                 )
                 )
-
-        if not dry_run:
-            patterns.append(
-                borgmatic.borg.pattern.Pattern(
-                    os.path.join(borgmatic_runtime_directory, 'mariadb_databases'),
-                    source=borgmatic.borg.pattern.Pattern_source.HOOK,
+        else:
+            processes.append(
+                execute_dump_command(
+                    database,
+                    config,
+                    username,
+                    password,
+                    dump_path,
+                    dump_database_names,
+                    environment,
+                    dry_run,
+                    dry_run_label,
                 )
                 )
             )
             )
 
 
-        return [process for process in processes if process]
+    if not dry_run:
+        patterns.append(
+            borgmatic.borg.pattern.Pattern(
+                os.path.join(borgmatic_runtime_directory, 'mariadb_databases'),
+                source=borgmatic.borg.pattern.Pattern_source.HOOK,
+            )
+        )
+
+    return [process for process in processes if process]
 
 
 
 
 def remove_data_source_dumps(
 def remove_data_source_dumps(

+ 37 - 45
borgmatic/hooks/data_source/mongodb.py

@@ -4,7 +4,6 @@ import shlex
 
 
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
-import borgmatic.hooks.command
 import borgmatic.hooks.credential.parse
 import borgmatic.hooks.credential.parse
 from borgmatic.execute import execute_command, execute_command_with_processes
 from borgmatic.execute import execute_command, execute_command_with_processes
 from borgmatic.hooks.data_source import dump
 from borgmatic.hooks.data_source import dump
@@ -49,53 +48,46 @@ def dump_data_sources(
     Also append the the parent directory of the database dumps to the given patterns list, so the
     Also append the the parent directory of the database dumps to the given patterns list, so the
     dumps actually get backed up.
     dumps actually get backed up.
     '''
     '''
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='mongodb',
-    ):
-        dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
-
-        logger.info(f'Dumping MongoDB databases{dry_run_label}')
-
-        processes = []
-
-        for database in databases:
-            name = database['name']
-            dump_filename = dump.make_data_source_dump_filename(
-                make_dump_path(borgmatic_runtime_directory),
-                name,
-                database.get('hostname'),
-                database.get('port'),
-            )
-            dump_format = database.get('format', 'archive')
+    dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
 
 
-            logger.debug(
-                f'Dumping MongoDB database {name} to {dump_filename}{dry_run_label}',
-            )
-            if dry_run:
-                continue
-
-            command = build_dump_command(database, config, dump_filename, dump_format)
-
-            if dump_format == 'directory':
-                dump.create_parent_directory_for_dump(dump_filename)
-                execute_command(command, shell=True)
-            else:
-                dump.create_named_pipe_for_dump(dump_filename)
-                processes.append(execute_command(command, shell=True, run_to_completion=False))
-
-        if not dry_run:
-            patterns.append(
-                borgmatic.borg.pattern.Pattern(
-                    os.path.join(borgmatic_runtime_directory, 'mongodb_databases'),
-                    source=borgmatic.borg.pattern.Pattern_source.HOOK,
-                )
+    logger.info(f'Dumping MongoDB databases{dry_run_label}')
+
+    processes = []
+
+    for database in databases:
+        name = database['name']
+        dump_filename = dump.make_data_source_dump_filename(
+            make_dump_path(borgmatic_runtime_directory),
+            name,
+            database.get('hostname'),
+            database.get('port'),
+        )
+        dump_format = database.get('format', 'archive')
+
+        logger.debug(
+            f'Dumping MongoDB database {name} to {dump_filename}{dry_run_label}',
+        )
+        if dry_run:
+            continue
+
+        command = build_dump_command(database, config, dump_filename, dump_format)
+
+        if dump_format == 'directory':
+            dump.create_parent_directory_for_dump(dump_filename)
+            execute_command(command, shell=True)
+        else:
+            dump.create_named_pipe_for_dump(dump_filename)
+            processes.append(execute_command(command, shell=True, run_to_completion=False))
+
+    if not dry_run:
+        patterns.append(
+            borgmatic.borg.pattern.Pattern(
+                os.path.join(borgmatic_runtime_directory, 'mongodb_databases'),
+                source=borgmatic.borg.pattern.Pattern_source.HOOK,
             )
             )
+        )
 
 
-        return processes
+    return processes
 
 
 
 
 def make_password_config_file(password):
 def make_password_config_file(password):

+ 50 - 58
borgmatic/hooks/data_source/mysql.py

@@ -5,7 +5,6 @@ import shlex
 
 
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
-import borgmatic.hooks.command
 import borgmatic.hooks.credential.parse
 import borgmatic.hooks.credential.parse
 import borgmatic.hooks.data_source.mariadb
 import borgmatic.hooks.data_source.mariadb
 from borgmatic.execute import (
 from borgmatic.execute import (
@@ -170,78 +169,71 @@ def dump_data_sources(
     Also append the the parent directory of the database dumps to the given patterns list, so the
     Also append the the parent directory of the database dumps to the given patterns list, so the
     dumps actually get backed up.
     dumps actually get backed up.
     '''
     '''
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='mysql',
-    ):
-        dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
-        processes = []
-
-        logger.info(f'Dumping MySQL databases{dry_run_label}')
-
-        for database in databases:
-            dump_path = make_dump_path(borgmatic_runtime_directory)
-            username = borgmatic.hooks.credential.parse.resolve_credential(
-                database.get('username'), config
-            )
-            password = borgmatic.hooks.credential.parse.resolve_credential(
-                database.get('password'), config
-            )
-            environment = dict(os.environ)
-            dump_database_names = database_names_to_dump(
-                database, config, username, password, environment, dry_run
-            )
+    dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
+    processes = []
 
 
-            if not dump_database_names:
-                if dry_run:
-                    continue
-
-                raise ValueError('Cannot find any MySQL 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,
-                            config,
-                            username,
-                            password,
-                            dump_path,
-                            (dump_name,),
-                            environment,
-                            dry_run,
-                            dry_run_label,
-                        )
-                    )
-            else:
+    logger.info(f'Dumping MySQL databases{dry_run_label}')
+
+    for database in databases:
+        dump_path = make_dump_path(borgmatic_runtime_directory)
+        username = borgmatic.hooks.credential.parse.resolve_credential(
+            database.get('username'), config
+        )
+        password = borgmatic.hooks.credential.parse.resolve_credential(
+            database.get('password'), config
+        )
+        environment = dict(os.environ)
+        dump_database_names = database_names_to_dump(
+            database, config, username, password, environment, dry_run
+        )
+
+        if not dump_database_names:
+            if dry_run:
+                continue
+
+            raise ValueError('Cannot find any MySQL 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(
                 processes.append(
                     execute_dump_command(
                     execute_dump_command(
-                        database,
+                        renamed_database,
                         config,
                         config,
                         username,
                         username,
                         password,
                         password,
                         dump_path,
                         dump_path,
-                        dump_database_names,
+                        (dump_name,),
                         environment,
                         environment,
                         dry_run,
                         dry_run,
                         dry_run_label,
                         dry_run_label,
                     )
                     )
                 )
                 )
-
-        if not dry_run:
-            patterns.append(
-                borgmatic.borg.pattern.Pattern(
-                    os.path.join(borgmatic_runtime_directory, 'mysql_databases'),
-                    source=borgmatic.borg.pattern.Pattern_source.HOOK,
+        else:
+            processes.append(
+                execute_dump_command(
+                    database,
+                    config,
+                    username,
+                    password,
+                    dump_path,
+                    dump_database_names,
+                    environment,
+                    dry_run,
+                    dry_run_label,
                 )
                 )
             )
             )
 
 
-        return [process for process in processes if process]
+    if not dry_run:
+        patterns.append(
+            borgmatic.borg.pattern.Pattern(
+                os.path.join(borgmatic_runtime_directory, 'mysql_databases'),
+                source=borgmatic.borg.pattern.Pattern_source.HOOK,
+            )
+        )
+
+    return [process for process in processes if process]
 
 
 
 
 def remove_data_source_dumps(
 def remove_data_source_dumps(

+ 92 - 108
borgmatic/hooks/data_source/postgresql.py

@@ -7,7 +7,6 @@ import shlex
 
 
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
-import borgmatic.hooks.command
 import borgmatic.hooks.credential.parse
 import borgmatic.hooks.credential.parse
 from borgmatic.execute import (
 from borgmatic.execute import (
     execute_command,
     execute_command,
@@ -142,127 +141,112 @@ def dump_data_sources(
 
 
     Raise ValueError if the databases to dump cannot be determined.
     Raise ValueError if the databases to dump cannot be determined.
     '''
     '''
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='postgresql',
-    ):
-        dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
-        processes = []
-
-        logger.info(f'Dumping PostgreSQL databases{dry_run_label}')
-
-        for database in databases:
-            environment = make_environment(database, config)
-            dump_path = make_dump_path(borgmatic_runtime_directory)
-            dump_database_names = database_names_to_dump(database, config, environment, dry_run)
-
-            if not dump_database_names:
-                if dry_run:
-                    continue
-
-                raise ValueError('Cannot find any PostgreSQL databases to dump.')
-
-            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)
-                    for part in shlex.split(database.get('pg_dump_command') or default_dump_command)
+    dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
+    processes = []
+
+    logger.info(f'Dumping PostgreSQL databases{dry_run_label}')
+
+    for database in databases:
+        environment = make_environment(database, config)
+        dump_path = make_dump_path(borgmatic_runtime_directory)
+        dump_database_names = database_names_to_dump(database, config, environment, dry_run)
+
+        if not dump_database_names:
+            if dry_run:
+                continue
+
+            raise ValueError('Cannot find any PostgreSQL databases to dump.')
+
+        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)
+                for part in shlex.split(database.get('pg_dump_command') or default_dump_command)
+            )
+            dump_filename = dump.make_data_source_dump_filename(
+                dump_path,
+                database_name,
+                database.get('hostname'),
+                database.get('port'),
+            )
+            if os.path.exists(dump_filename):
+                logger.warning(
+                    f'Skipping duplicate dump of PostgreSQL database "{database_name}" to {dump_filename}'
                 )
                 )
-                dump_filename = dump.make_data_source_dump_filename(
-                    dump_path,
-                    database_name,
-                    database.get('hostname'),
-                    database.get('port'),
+                continue
+
+            command = (
+                dump_command
+                + (
+                    '--no-password',
+                    '--clean',
+                    '--if-exists',
                 )
                 )
-                if os.path.exists(dump_filename):
-                    logger.warning(
-                        f'Skipping duplicate dump of PostgreSQL database "{database_name}" to {dump_filename}'
-                    )
-                    continue
-
-                command = (
-                    dump_command
-                    + (
-                        '--no-password',
-                        '--clean',
-                        '--if-exists',
-                    )
-                    + (
-                        ('--host', shlex.quote(database['hostname']))
-                        if 'hostname' in database
-                        else ()
-                    )
-                    + (('--port', shlex.quote(str(database['port']))) if 'port' in database else ())
-                    + (
-                        (
-                            '--username',
-                            shlex.quote(
-                                borgmatic.hooks.credential.parse.resolve_credential(
-                                    database['username'], config
-                                )
-                            ),
-                        )
-                        if 'username' in database
-                        else ()
+                + (('--host', shlex.quote(database['hostname'])) if 'hostname' in database else ())
+                + (('--port', shlex.quote(str(database['port']))) if 'port' in database else ())
+                + (
+                    (
+                        '--username',
+                        shlex.quote(
+                            borgmatic.hooks.credential.parse.resolve_credential(
+                                database['username'], config
+                            )
+                        ),
                     )
                     )
-                    + (('--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(' '))
-                        if 'options' in database
-                        else ()
-                    )
-                    + (() if database_name == 'all' else (shlex.quote(database_name),))
-                    # Use shell redirection rather than the --file flag to sidestep synchronization issues
-                    # when pg_dump/pg_dumpall tries to write to a named pipe. But for the directory dump
-                    # format in a particular, a named destination is required, and redirection doesn't work.
-                    + (('>', shlex.quote(dump_filename)) if dump_format != 'directory' else ())
+                    if 'username' in database
+                    else ()
                 )
                 )
-
-                logger.debug(
-                    f'Dumping PostgreSQL database "{database_name}" to {dump_filename}{dry_run_label}'
+                + (('--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(' '))
+                    if 'options' in database
+                    else ()
                 )
                 )
-                if dry_run:
-                    continue
+                + (() if database_name == 'all' else (shlex.quote(database_name),))
+                # Use shell redirection rather than the --file flag to sidestep synchronization issues
+                # when pg_dump/pg_dumpall tries to write to a named pipe. But for the directory dump
+                # format in a particular, a named destination is required, and redirection doesn't work.
+                + (('>', shlex.quote(dump_filename)) if dump_format != 'directory' else ())
+            )
 
 
-                if dump_format == 'directory':
-                    dump.create_parent_directory_for_dump(dump_filename)
+            logger.debug(
+                f'Dumping PostgreSQL database "{database_name}" to {dump_filename}{dry_run_label}'
+            )
+            if dry_run:
+                continue
+
+            if dump_format == 'directory':
+                dump.create_parent_directory_for_dump(dump_filename)
+                execute_command(
+                    command,
+                    shell=True,
+                    environment=environment,
+                )
+            else:
+                dump.create_named_pipe_for_dump(dump_filename)
+                processes.append(
                     execute_command(
                     execute_command(
                         command,
                         command,
                         shell=True,
                         shell=True,
                         environment=environment,
                         environment=environment,
+                        run_to_completion=False,
                     )
                     )
-                else:
-                    dump.create_named_pipe_for_dump(dump_filename)
-                    processes.append(
-                        execute_command(
-                            command,
-                            shell=True,
-                            environment=environment,
-                            run_to_completion=False,
-                        )
-                    )
-
-        if not dry_run:
-            patterns.append(
-                borgmatic.borg.pattern.Pattern(
-                    os.path.join(borgmatic_runtime_directory, 'postgresql_databases'),
-                    source=borgmatic.borg.pattern.Pattern_source.HOOK,
                 )
                 )
+
+    if not dry_run:
+        patterns.append(
+            borgmatic.borg.pattern.Pattern(
+                os.path.join(borgmatic_runtime_directory, 'postgresql_databases'),
+                source=borgmatic.borg.pattern.Pattern_source.HOOK,
             )
             )
+        )
 
 
-        return processes
+    return processes
 
 
 
 
 def remove_data_source_dumps(
 def remove_data_source_dumps(

+ 47 - 56
borgmatic/hooks/data_source/sqlite.py

@@ -4,7 +4,6 @@ import shlex
 
 
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
-import borgmatic.hooks.command
 from borgmatic.execute import execute_command, execute_command_with_processes
 from borgmatic.execute import execute_command, execute_command_with_processes
 from borgmatic.hooks.data_source import dump
 from borgmatic.hooks.data_source import dump
 
 
@@ -48,66 +47,58 @@ def dump_data_sources(
     Also append the the parent directory of the database dumps to the given patterns list, so the
     Also append the the parent directory of the database dumps to the given patterns list, so the
     dumps actually get backed up.
     dumps actually get backed up.
     '''
     '''
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='sqlite',
-    ):
-        dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
-        processes = []
-
-        logger.info(f'Dumping SQLite databases{dry_run_label}')
-
-        for database in databases:
-            database_path = database['path']
-
-            if database['name'] == 'all':
-                logger.warning('The "all" database name has no meaning for SQLite databases')
-            if not os.path.exists(database_path):
-                logger.warning(
-                    f'No SQLite database at {database_path}; an empty database will be created and dumped'
-                )
-
-            dump_path = make_dump_path(borgmatic_runtime_directory)
-            dump_filename = dump.make_data_source_dump_filename(dump_path, database['name'])
-
-            if os.path.exists(dump_filename):
-                logger.warning(
-                    f'Skipping duplicate dump of SQLite database at {database_path} to {dump_filename}'
-                )
-                continue
-
-            sqlite_command = tuple(
-                shlex.quote(part)
-                for part in shlex.split(database.get('sqlite_command') or 'sqlite3')
-            )
-            command = sqlite_command + (
-                shlex.quote(database_path),
-                '.dump',
-                '>',
-                shlex.quote(dump_filename),
+    dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
+    processes = []
+
+    logger.info(f'Dumping SQLite databases{dry_run_label}')
+
+    for database in databases:
+        database_path = database['path']
+
+        if database['name'] == 'all':
+            logger.warning('The "all" database name has no meaning for SQLite databases')
+        if not os.path.exists(database_path):
+            logger.warning(
+                f'No SQLite database at {database_path}; an empty database will be created and dumped'
             )
             )
 
 
-            logger.debug(
-                f'Dumping SQLite database at {database_path} to {dump_filename}{dry_run_label}'
+        dump_path = make_dump_path(borgmatic_runtime_directory)
+        dump_filename = dump.make_data_source_dump_filename(dump_path, database['name'])
+
+        if os.path.exists(dump_filename):
+            logger.warning(
+                f'Skipping duplicate dump of SQLite database at {database_path} to {dump_filename}'
             )
             )
-            if dry_run:
-                continue
-
-            dump.create_named_pipe_for_dump(dump_filename)
-            processes.append(execute_command(command, shell=True, run_to_completion=False))
-
-        if not dry_run:
-            patterns.append(
-                borgmatic.borg.pattern.Pattern(
-                    os.path.join(borgmatic_runtime_directory, 'sqlite_databases'),
-                    source=borgmatic.borg.pattern.Pattern_source.HOOK,
-                )
+            continue
+
+        sqlite_command = tuple(
+            shlex.quote(part) for part in shlex.split(database.get('sqlite_command') or 'sqlite3')
+        )
+        command = sqlite_command + (
+            shlex.quote(database_path),
+            '.dump',
+            '>',
+            shlex.quote(dump_filename),
+        )
+
+        logger.debug(
+            f'Dumping SQLite database at {database_path} to {dump_filename}{dry_run_label}'
+        )
+        if dry_run:
+            continue
+
+        dump.create_named_pipe_for_dump(dump_filename)
+        processes.append(execute_command(command, shell=True, run_to_completion=False))
+
+    if not dry_run:
+        patterns.append(
+            borgmatic.borg.pattern.Pattern(
+                os.path.join(borgmatic_runtime_directory, 'sqlite_databases'),
+                source=borgmatic.borg.pattern.Pattern_source.HOOK,
             )
             )
+        )
 
 
-        return processes
+    return processes
 
 
 
 
 def remove_data_source_dumps(
 def remove_data_source_dumps(

+ 51 - 59
borgmatic/hooks/data_source/zfs.py

@@ -9,7 +9,6 @@ import subprocess
 import borgmatic.borg.pattern
 import borgmatic.borg.pattern
 import borgmatic.config.paths
 import borgmatic.config.paths
 import borgmatic.execute
 import borgmatic.execute
-import borgmatic.hooks.command
 import borgmatic.hooks.data_source.snapshot
 import borgmatic.hooks.data_source.snapshot
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
@@ -244,71 +243,64 @@ def dump_data_sources(
 
 
     If this is a dry run, then don't actually snapshot anything.
     If this is a dry run, then don't actually snapshot anything.
     '''
     '''
-    with borgmatic.hooks.command.Before_after_hooks(
-        command_hooks=config.get('commands'),
-        before_after='dump_data_sources',
-        umask=config.get('umask'),
-        dry_run=dry_run,
-        hook_name='zfs',
-    ):
-        dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
-        logger.info(f'Snapshotting ZFS datasets{dry_run_label}')
-
-        # List ZFS datasets to get their mount points, but only consider those patterns that came from
-        # actual user configuration (as opposed to, say, other hooks).
-        zfs_command = hook_config.get('zfs_command', 'zfs')
-        requested_datasets = get_datasets_to_backup(zfs_command, patterns)
-
-        # Snapshot each dataset, rewriting patterns to use the snapshot paths.
-        snapshot_name = f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}'
-        normalized_runtime_directory = os.path.normpath(borgmatic_runtime_directory)
-
-        if not requested_datasets:
-            logger.warning(f'No ZFS datasets found to snapshot{dry_run_label}')
-
-        for dataset in requested_datasets:
-            full_snapshot_name = f'{dataset.name}@{snapshot_name}'
-            logger.debug(
-                f'Creating ZFS snapshot {full_snapshot_name} of {dataset.mount_point}{dry_run_label}'
-            )
+    dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
+    logger.info(f'Snapshotting ZFS datasets{dry_run_label}')
 
 
-            if not dry_run:
-                snapshot_dataset(zfs_command, full_snapshot_name)
-
-            # Mount the snapshot into a particular named temporary directory so that the snapshot ends
-            # up in the Borg archive at the "original" dataset mount point path.
-            snapshot_mount_path = os.path.join(
-                normalized_runtime_directory,
-                'zfs_snapshots',
-                hashlib.shake_256(dataset.mount_point.encode('utf-8')).hexdigest(
-                    MOUNT_POINT_HASH_LENGTH
-                ),
-                dataset.mount_point.lstrip(os.path.sep),
-            )
+    # List ZFS datasets to get their mount points, but only consider those patterns that came from
+    # actual user configuration (as opposed to, say, other hooks).
+    zfs_command = hook_config.get('zfs_command', 'zfs')
+    requested_datasets = get_datasets_to_backup(zfs_command, patterns)
 
 
-            logger.debug(
-                f'Mounting ZFS snapshot {full_snapshot_name} at {snapshot_mount_path}{dry_run_label}'
-            )
+    # Snapshot each dataset, rewriting patterns to use the snapshot paths.
+    snapshot_name = f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}'
+    normalized_runtime_directory = os.path.normpath(borgmatic_runtime_directory)
 
 
-            if dry_run:
-                continue
+    if not requested_datasets:
+        logger.warning(f'No ZFS datasets found to snapshot{dry_run_label}')
 
 
-            mount_snapshot(
-                hook_config.get('mount_command', 'mount'), full_snapshot_name, snapshot_mount_path
-            )
+    for dataset in requested_datasets:
+        full_snapshot_name = f'{dataset.name}@{snapshot_name}'
+        logger.debug(
+            f'Creating ZFS snapshot {full_snapshot_name} of {dataset.mount_point}{dry_run_label}'
+        )
 
 
-            for pattern in dataset.contained_patterns:
-                snapshot_pattern = make_borg_snapshot_pattern(
-                    pattern, dataset, normalized_runtime_directory
-                )
+        if not dry_run:
+            snapshot_dataset(zfs_command, full_snapshot_name)
+
+        # Mount the snapshot into a particular named temporary directory so that the snapshot ends
+        # up in the Borg archive at the "original" dataset mount point path.
+        snapshot_mount_path = os.path.join(
+            normalized_runtime_directory,
+            'zfs_snapshots',
+            hashlib.shake_256(dataset.mount_point.encode('utf-8')).hexdigest(
+                MOUNT_POINT_HASH_LENGTH
+            ),
+            dataset.mount_point.lstrip(os.path.sep),
+        )
 
 
-                # Attempt to update the pattern in place, since pattern order matters to Borg.
-                try:
-                    patterns[patterns.index(pattern)] = snapshot_pattern
-                except ValueError:
-                    patterns.append(snapshot_pattern)
+        logger.debug(
+            f'Mounting ZFS snapshot {full_snapshot_name} at {snapshot_mount_path}{dry_run_label}'
+        )
+
+        if dry_run:
+            continue
+
+        mount_snapshot(
+            hook_config.get('mount_command', 'mount'), full_snapshot_name, snapshot_mount_path
+        )
+
+        for pattern in dataset.contained_patterns:
+            snapshot_pattern = make_borg_snapshot_pattern(
+                pattern, dataset, normalized_runtime_directory
+            )
+
+            # Attempt to update the pattern in place, since pattern order matters to Borg.
+            try:
+                patterns[patterns.index(pattern)] = snapshot_pattern
+            except ValueError:
+                patterns.append(snapshot_pattern)
 
 
-        return []
+    return []
 
 
 
 
 def unmount_snapshot(umount_command, snapshot_mount_path):  # pragma: no cover
 def unmount_snapshot(umount_command, snapshot_mount_path):  # pragma: no cover

+ 0 - 20
docs/how-to/add-preparation-and-cleanup-steps-to-backups.md

@@ -71,23 +71,6 @@ those two hooks. This allows you to perform cleanup steps that correspond to `be
 commands—even when something goes wrong. This is a departure from the way that the deprecated
 commands—even when something goes wrong. This is a departure from the way that the deprecated
 `after_*` hooks worked.
 `after_*` hooks worked.
 
 
-There's also another command hook that works a little differently:
-
-```yaml
-commands:
-    - before: dump_data_sources
-      hooks: [postgresql]
-      run:
-          - echo "Right before the PostgreSQL database dump!"
-```
-
-This command hook has the following options:
-
- * `before` or `after`: Name for the point in borgmatic's execution that the commands should be run before or after:
-   * `dump_data_sources` runs before or after data sources are dumped (databases dumped or filesystems snapshotted) for each hook named in `hooks`.
- * `hooks`: Names of other hooks that this command hook applies to, e.g. `postgresql`, `mariadb`, `zfs`, `btrfs`, etc. Defaults to all hooks of the relevant type.
- * `run`: One or more shell commands or scripts to run when this command hook is triggered.
-
 
 
 ### Order of execution
 ### Order of execution
 
 
@@ -102,9 +85,6 @@ borgmatic for the `create` and `prune` actions. Here's the order of execution:
     * Run `before: configuration` hooks (from the first configuration file).
     * Run `before: configuration` hooks (from the first configuration file).
         * Run `before: repository` hooks (for the first repository).
         * Run `before: repository` hooks (for the first repository).
             * Run `before: action` hooks for `create`.
             * Run `before: action` hooks for `create`.
-                * Run `before: dump_data_sources` hooks (e.g. for the PostgreSQL hook).
-                * Actually dump data sources (e.g. PostgreSQL databases).
-                * Run `after: dump_data_sources` hooks (e.g. for the PostgreSQL hook).
             * Actually run the `create` action (e.g. `borg create`).
             * Actually run the `create` action (e.g. `borg create`).
             * Run `after: action` hooks for `create`.
             * Run `after: action` hooks for `create`.
             * Run `before: action` hooks for `prune`.
             * Run `before: action` hooks for `prune`.

+ 0 - 7
tests/unit/hooks/data_source/test_bootstrap.py

@@ -6,9 +6,6 @@ from borgmatic.hooks.data_source import bootstrap as module
 
 
 
 
 def test_dump_data_sources_creates_manifest_file():
 def test_dump_data_sources_creates_manifest_file():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     flexmock(module.os).should_receive('makedirs')
     flexmock(module.os).should_receive('makedirs')
 
 
     flexmock(module.importlib.metadata).should_receive('version').and_return('1.0.0')
     flexmock(module.importlib.metadata).should_receive('version').and_return('1.0.0')
@@ -35,7 +32,6 @@ def test_dump_data_sources_creates_manifest_file():
 
 
 
 
 def test_dump_data_sources_with_store_config_files_false_does_not_create_manifest_file():
 def test_dump_data_sources_with_store_config_files_false_does_not_create_manifest_file():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').never()
     flexmock(module.os).should_receive('makedirs').never()
     flexmock(module.os).should_receive('makedirs').never()
     flexmock(module.json).should_receive('dump').never()
     flexmock(module.json).should_receive('dump').never()
     hook_config = {'store_config_files': False}
     hook_config = {'store_config_files': False}
@@ -51,9 +47,6 @@ def test_dump_data_sources_with_store_config_files_false_does_not_create_manifes
 
 
 
 
 def test_dump_data_sources_with_dry_run_does_not_create_manifest_file():
 def test_dump_data_sources_with_dry_run_does_not_create_manifest_file():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     flexmock(module.os).should_receive('makedirs').never()
     flexmock(module.os).should_receive('makedirs').never()
     flexmock(module.json).should_receive('dump').never()
     flexmock(module.json).should_receive('dump').never()
 
 

+ 0 - 18
tests/unit/hooks/data_source/test_btrfs.py

@@ -269,9 +269,6 @@ def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_
 
 
 
 
 def test_dump_data_sources_snapshots_each_subvolume_and_updates_patterns():
 def test_dump_data_sources_snapshots_each_subvolume_and_updates_patterns():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     config = {'btrfs': {}}
     config = {'btrfs': {}}
     flexmock(module).should_receive('get_subvolumes').and_return(
     flexmock(module).should_receive('get_subvolumes').and_return(
@@ -350,9 +347,6 @@ def test_dump_data_sources_snapshots_each_subvolume_and_updates_patterns():
 
 
 
 
 def test_dump_data_sources_uses_custom_btrfs_command_in_commands():
 def test_dump_data_sources_uses_custom_btrfs_command_in_commands():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     config = {'btrfs': {'btrfs_command': '/usr/local/bin/btrfs'}}
     config = {'btrfs': {'btrfs_command': '/usr/local/bin/btrfs'}}
     flexmock(module).should_receive('get_subvolumes').and_return(
     flexmock(module).should_receive('get_subvolumes').and_return(
@@ -406,9 +400,6 @@ def test_dump_data_sources_uses_custom_btrfs_command_in_commands():
 
 
 
 
 def test_dump_data_sources_uses_custom_findmnt_command_in_commands():
 def test_dump_data_sources_uses_custom_findmnt_command_in_commands():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     config = {'btrfs': {'findmnt_command': '/usr/local/bin/findmnt'}}
     config = {'btrfs': {'findmnt_command': '/usr/local/bin/findmnt'}}
     flexmock(module).should_receive('get_subvolumes').with_args(
     flexmock(module).should_receive('get_subvolumes').with_args(
@@ -464,9 +455,6 @@ def test_dump_data_sources_uses_custom_findmnt_command_in_commands():
 
 
 
 
 def test_dump_data_sources_with_dry_run_skips_snapshot_and_patterns_update():
 def test_dump_data_sources_with_dry_run_skips_snapshot_and_patterns_update():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     config = {'btrfs': {}}
     config = {'btrfs': {}}
     flexmock(module).should_receive('get_subvolumes').and_return(
     flexmock(module).should_receive('get_subvolumes').and_return(
@@ -495,9 +483,6 @@ def test_dump_data_sources_with_dry_run_skips_snapshot_and_patterns_update():
 
 
 
 
 def test_dump_data_sources_without_matching_subvolumes_skips_snapshot_and_patterns_update():
 def test_dump_data_sources_without_matching_subvolumes_skips_snapshot_and_patterns_update():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     config = {'btrfs': {}}
     config = {'btrfs': {}}
     flexmock(module).should_receive('get_subvolumes').and_return(())
     flexmock(module).should_receive('get_subvolumes').and_return(())
@@ -522,9 +507,6 @@ def test_dump_data_sources_without_matching_subvolumes_skips_snapshot_and_patter
 
 
 
 
 def test_dump_data_sources_snapshots_adds_to_existing_exclude_patterns():
 def test_dump_data_sources_snapshots_adds_to_existing_exclude_patterns():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     patterns = [Pattern('/foo'), Pattern('/mnt/subvol1')]
     config = {'btrfs': {}, 'exclude_patterns': ['/bar']}
     config = {'btrfs': {}, 'exclude_patterns': ['/bar']}
     flexmock(module).should_receive('get_subvolumes').and_return(
     flexmock(module).should_receive('get_subvolumes').and_return(

+ 0 - 21
tests/unit/hooks/data_source/test_lvm.py

@@ -282,9 +282,6 @@ def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_
 
 
 
 
 def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
 def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     config = {'lvm': {}}
     config = {'lvm': {}}
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     logical_volumes = (
     logical_volumes = (
@@ -354,9 +351,6 @@ def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
 
 
 
 
 def test_dump_data_sources_with_no_logical_volumes_skips_snapshots():
 def test_dump_data_sources_with_no_logical_volumes_skips_snapshots():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     config = {'lvm': {}}
     config = {'lvm': {}}
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     flexmock(module).should_receive('get_logical_volumes').and_return(())
     flexmock(module).should_receive('get_logical_volumes').and_return(())
@@ -379,9 +373,6 @@ def test_dump_data_sources_with_no_logical_volumes_skips_snapshots():
 
 
 
 
 def test_dump_data_sources_uses_snapshot_size_for_snapshot():
 def test_dump_data_sources_uses_snapshot_size_for_snapshot():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     config = {'lvm': {'snapshot_size': '1000PB'}}
     config = {'lvm': {'snapshot_size': '1000PB'}}
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     logical_volumes = (
     logical_volumes = (
@@ -457,9 +448,6 @@ def test_dump_data_sources_uses_snapshot_size_for_snapshot():
 
 
 
 
 def test_dump_data_sources_uses_custom_commands():
 def test_dump_data_sources_uses_custom_commands():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     config = {
     config = {
         'lvm': {
         'lvm': {
             'lsblk_command': '/usr/local/bin/lsblk',
             'lsblk_command': '/usr/local/bin/lsblk',
@@ -546,9 +534,6 @@ def test_dump_data_sources_uses_custom_commands():
 
 
 
 
 def test_dump_data_sources_with_dry_run_skips_snapshots_and_does_not_touch_patterns():
 def test_dump_data_sources_with_dry_run_skips_snapshots_and_does_not_touch_patterns():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     config = {'lvm': {}}
     config = {'lvm': {}}
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     flexmock(module).should_receive('get_logical_volumes').and_return(
     flexmock(module).should_receive('get_logical_volumes').and_return(
@@ -600,9 +585,6 @@ def test_dump_data_sources_with_dry_run_skips_snapshots_and_does_not_touch_patte
 
 
 
 
 def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained_patterns():
 def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained_patterns():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     config = {'lvm': {}}
     config = {'lvm': {}}
     patterns = [Pattern('/hmm')]
     patterns = [Pattern('/hmm')]
     logical_volumes = (
     logical_volumes = (
@@ -673,9 +655,6 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
 
 
 
 
 def test_dump_data_sources_with_missing_snapshot_errors():
 def test_dump_data_sources_with_missing_snapshot_errors():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     config = {'lvm': {}}
     config = {'lvm': {}}
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
     flexmock(module).should_receive('get_logical_volumes').and_return(
     flexmock(module).should_receive('get_logical_volumes').and_return(

+ 0 - 18
tests/unit/hooks/data_source/test_mariadb.py

@@ -237,9 +237,6 @@ def test_use_streaming_false_for_no_databases():
 
 
 
 
 def test_dump_data_sources_dumps_each_database():
 def test_dump_data_sources_dumps_each_database():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     processes = [flexmock(), flexmock()]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -281,9 +278,6 @@ def test_dump_data_sources_dumps_each_database():
 
 
 
 
 def test_dump_data_sources_dumps_with_password():
 def test_dump_data_sources_dumps_with_password():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     database = {'name': 'foo', 'username': 'root', 'password': 'trustsome1'}
     database = {'name': 'foo', 'username': 'root', 'password': 'trustsome1'}
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -318,9 +312,6 @@ def test_dump_data_sources_dumps_with_password():
 
 
 
 
 def test_dump_data_sources_dumps_all_databases_at_once():
 def test_dump_data_sources_dumps_all_databases_at_once():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all'}]
     databases = [{'name': 'all'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -352,9 +343,6 @@ def test_dump_data_sources_dumps_all_databases_at_once():
 
 
 
 
 def test_dump_data_sources_dumps_all_databases_separately_when_format_configured():
 def test_dump_data_sources_dumps_all_databases_separately_when_format_configured():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all', 'format': 'sql'}]
     databases = [{'name': 'all', 'format': 'sql'}]
     processes = [flexmock(), flexmock()]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -862,9 +850,6 @@ def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
 
 
 
 
 def test_dump_data_sources_errors_for_missing_all_databases():
 def test_dump_data_sources_errors_for_missing_all_databases():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all'}]
     databases = [{'name': 'all'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
@@ -888,9 +873,6 @@ def test_dump_data_sources_errors_for_missing_all_databases():
 
 
 
 
 def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run():
 def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all'}]
     databases = [{'name': 'all'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})

+ 0 - 21
tests/unit/hooks/data_source/test_mongodb.py

@@ -24,9 +24,6 @@ def test_use_streaming_false_for_no_databases():
 
 
 
 
 def test_dump_data_sources_runs_mongodump_for_each_database():
 def test_dump_data_sources_runs_mongodump_for_each_database():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     processes = [flexmock(), flexmock()]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -56,9 +53,6 @@ def test_dump_data_sources_runs_mongodump_for_each_database():
 
 
 
 
 def test_dump_data_sources_with_dry_run_skips_mongodump():
 def test_dump_data_sources_with_dry_run_skips_mongodump():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
@@ -81,9 +75,6 @@ def test_dump_data_sources_with_dry_run_skips_mongodump():
 
 
 
 
 def test_dump_data_sources_runs_mongodump_with_hostname_and_port():
 def test_dump_data_sources_runs_mongodump_with_hostname_and_port():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
     databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -120,9 +111,6 @@ def test_dump_data_sources_runs_mongodump_with_hostname_and_port():
 
 
 
 
 def test_dump_data_sources_runs_mongodump_with_username_and_password():
 def test_dump_data_sources_runs_mongodump_with_username_and_password():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [
     databases = [
         {
         {
             'name': 'foo',
             'name': 'foo',
@@ -174,9 +162,6 @@ def test_dump_data_sources_runs_mongodump_with_username_and_password():
 
 
 
 
 def test_dump_data_sources_runs_mongodump_with_directory_format():
 def test_dump_data_sources_runs_mongodump_with_directory_format():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'format': 'directory'}]
     databases = [{'name': 'foo', 'format': 'directory'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
@@ -204,9 +189,6 @@ def test_dump_data_sources_runs_mongodump_with_directory_format():
 
 
 
 
 def test_dump_data_sources_runs_mongodump_with_options():
 def test_dump_data_sources_runs_mongodump_with_options():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'options': '--stuff=such'}]
     databases = [{'name': 'foo', 'options': '--stuff=such'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -240,9 +222,6 @@ def test_dump_data_sources_runs_mongodump_with_options():
 
 
 
 
 def test_dump_data_sources_runs_mongodumpall_for_all_databases():
 def test_dump_data_sources_runs_mongodumpall_for_all_databases():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all'}]
     databases = [{'name': 'all'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')

+ 0 - 18
tests/unit/hooks/data_source/test_mysql.py

@@ -134,9 +134,6 @@ def test_use_streaming_false_for_no_databases():
 
 
 
 
 def test_dump_data_sources_dumps_each_database():
 def test_dump_data_sources_dumps_each_database():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     processes = [flexmock(), flexmock()]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -175,9 +172,6 @@ def test_dump_data_sources_dumps_each_database():
 
 
 
 
 def test_dump_data_sources_dumps_with_password():
 def test_dump_data_sources_dumps_with_password():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     database = {'name': 'foo', 'username': 'root', 'password': 'trustsome1'}
     database = {'name': 'foo', 'username': 'root', 'password': 'trustsome1'}
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -212,9 +206,6 @@ def test_dump_data_sources_dumps_with_password():
 
 
 
 
 def test_dump_data_sources_dumps_all_databases_at_once():
 def test_dump_data_sources_dumps_all_databases_at_once():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all'}]
     databases = [{'name': 'all'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -246,9 +237,6 @@ def test_dump_data_sources_dumps_all_databases_at_once():
 
 
 
 
 def test_dump_data_sources_dumps_all_databases_separately_when_format_configured():
 def test_dump_data_sources_dumps_all_databases_separately_when_format_configured():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all', 'format': 'sql'}]
     databases = [{'name': 'all', 'format': 'sql'}]
     processes = [flexmock(), flexmock()]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -774,9 +762,6 @@ def test_execute_dump_command_with_dry_run_skips_mysqldump():
 
 
 
 
 def test_dump_data_sources_errors_for_missing_all_databases():
 def test_dump_data_sources_errors_for_missing_all_databases():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all'}]
     databases = [{'name': 'all'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
@@ -800,9 +785,6 @@ def test_dump_data_sources_errors_for_missing_all_databases():
 
 
 
 
 def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run():
 def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all'}]
     databases = [{'name': 'all'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})

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

@@ -236,9 +236,6 @@ def test_use_streaming_false_for_no_databases():
 
 
 
 
 def test_dump_data_sources_runs_pg_dump_for_each_database():
 def test_dump_data_sources_runs_pg_dump_for_each_database():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     processes = [flexmock(), flexmock()]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
@@ -287,9 +284,6 @@ def test_dump_data_sources_runs_pg_dump_for_each_database():
 
 
 
 
 def test_dump_data_sources_raises_when_no_database_names_to_dump():
 def test_dump_data_sources_raises_when_no_database_names_to_dump():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -307,9 +301,6 @@ def test_dump_data_sources_raises_when_no_database_names_to_dump():
 
 
 
 
 def test_dump_data_sources_does_not_raise_when_no_database_names_to_dump():
 def test_dump_data_sources_does_not_raise_when_no_database_names_to_dump():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -326,9 +317,6 @@ def test_dump_data_sources_does_not_raise_when_no_database_names_to_dump():
 
 
 
 
 def test_dump_data_sources_with_duplicate_dump_skips_pg_dump():
 def test_dump_data_sources_with_duplicate_dump_skips_pg_dump():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -356,9 +344,6 @@ def test_dump_data_sources_with_duplicate_dump_skips_pg_dump():
 
 
 
 
 def test_dump_data_sources_with_dry_run_skips_pg_dump():
 def test_dump_data_sources_with_dry_run_skips_pg_dump():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -389,9 +374,6 @@ def test_dump_data_sources_with_dry_run_skips_pg_dump():
 
 
 
 
 def test_dump_data_sources_runs_pg_dump_with_hostname_and_port():
 def test_dump_data_sources_runs_pg_dump_with_hostname_and_port():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
     databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
@@ -438,9 +420,6 @@ def test_dump_data_sources_runs_pg_dump_with_hostname_and_port():
 
 
 
 
 def test_dump_data_sources_runs_pg_dump_with_username_and_password():
 def test_dump_data_sources_runs_pg_dump_with_username_and_password():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'username': 'postgres', 'password': 'trustsome1'}]
     databases = [{'name': 'foo', 'username': 'postgres', 'password': 'trustsome1'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_environment').and_return(
     flexmock(module).should_receive('make_environment').and_return(
@@ -487,9 +466,6 @@ def test_dump_data_sources_runs_pg_dump_with_username_and_password():
 
 
 
 
 def test_dump_data_sources_with_username_injection_attack_gets_escaped():
 def test_dump_data_sources_with_username_injection_attack_gets_escaped():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'username': 'postgres; naughty-command', 'password': 'trustsome1'}]
     databases = [{'name': 'foo', 'username': 'postgres; naughty-command', 'password': 'trustsome1'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_environment').and_return(
     flexmock(module).should_receive('make_environment').and_return(
@@ -536,9 +512,6 @@ def test_dump_data_sources_with_username_injection_attack_gets_escaped():
 
 
 
 
 def test_dump_data_sources_runs_pg_dump_with_directory_format():
 def test_dump_data_sources_runs_pg_dump_with_directory_format():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'format': 'directory'}]
     databases = [{'name': 'foo', 'format': 'directory'}]
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -583,9 +556,6 @@ def test_dump_data_sources_runs_pg_dump_with_directory_format():
 
 
 
 
 def test_dump_data_sources_runs_pg_dump_with_string_compression():
 def test_dump_data_sources_runs_pg_dump_with_string_compression():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'compression': 'winrar'}]
     databases = [{'name': 'foo', 'compression': 'winrar'}]
     processes = [flexmock()]
     processes = [flexmock()]
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
@@ -633,9 +603,6 @@ def test_dump_data_sources_runs_pg_dump_with_string_compression():
 
 
 
 
 def test_dump_data_sources_runs_pg_dump_with_integer_compression():
 def test_dump_data_sources_runs_pg_dump_with_integer_compression():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'compression': 0}]
     databases = [{'name': 'foo', 'compression': 0}]
     processes = [flexmock()]
     processes = [flexmock()]
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
@@ -683,9 +650,6 @@ def test_dump_data_sources_runs_pg_dump_with_integer_compression():
 
 
 
 
 def test_dump_data_sources_runs_pg_dump_with_options():
 def test_dump_data_sources_runs_pg_dump_with_options():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'options': '--stuff=such'}]
     databases = [{'name': 'foo', 'options': '--stuff=such'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
@@ -729,9 +693,6 @@ def test_dump_data_sources_runs_pg_dump_with_options():
 
 
 
 
 def test_dump_data_sources_runs_pg_dumpall_for_all_databases():
 def test_dump_data_sources_runs_pg_dumpall_for_all_databases():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'all'}]
     databases = [{'name': 'all'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
@@ -764,9 +725,6 @@ def test_dump_data_sources_runs_pg_dumpall_for_all_databases():
 
 
 
 
 def test_dump_data_sources_runs_non_default_pg_dump():
 def test_dump_data_sources_runs_non_default_pg_dump():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'name': 'foo', 'pg_dump_command': 'special_pg_dump --compress *'}]
     databases = [{'name': 'foo', 'pg_dump_command': 'special_pg_dump --compress *'}]
     process = flexmock()
     process = flexmock()
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})

+ 0 - 21
tests/unit/hooks/data_source/test_sqlite.py

@@ -17,9 +17,6 @@ def test_use_streaming_false_for_no_databases():
 
 
 
 
 def test_dump_data_sources_logs_and_skips_if_dump_already_exists():
 def test_dump_data_sources_logs_and_skips_if_dump_already_exists():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'path': '/path/to/database', 'name': 'database'}]
     databases = [{'path': '/path/to/database', 'name': 'database'}]
 
 
     flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic')
     flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic')
@@ -44,9 +41,6 @@ def test_dump_data_sources_logs_and_skips_if_dump_already_exists():
 
 
 
 
 def test_dump_data_sources_dumps_each_database():
 def test_dump_data_sources_dumps_each_database():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [
     databases = [
         {'path': '/path/to/database1', 'name': 'database1'},
         {'path': '/path/to/database1', 'name': 'database1'},
         {'path': '/path/to/database2', 'name': 'database2'},
         {'path': '/path/to/database2', 'name': 'database2'},
@@ -77,9 +71,6 @@ def test_dump_data_sources_dumps_each_database():
 
 
 
 
 def test_dump_data_sources_with_path_injection_attack_gets_escaped():
 def test_dump_data_sources_with_path_injection_attack_gets_escaped():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [
     databases = [
         {'path': '/path/to/database1; naughty-command', 'name': 'database1'},
         {'path': '/path/to/database1; naughty-command', 'name': 'database1'},
     ]
     ]
@@ -117,9 +108,6 @@ def test_dump_data_sources_with_path_injection_attack_gets_escaped():
 
 
 
 
 def test_dump_data_sources_runs_non_default_sqlite_with_path_injection_attack_gets_escaped():
 def test_dump_data_sources_runs_non_default_sqlite_with_path_injection_attack_gets_escaped():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [
     databases = [
         {
         {
             'path': '/path/to/database1; naughty-command',
             'path': '/path/to/database1; naughty-command',
@@ -162,9 +150,6 @@ def test_dump_data_sources_runs_non_default_sqlite_with_path_injection_attack_ge
 
 
 
 
 def test_dump_data_sources_with_non_existent_path_warns_and_dumps_database():
 def test_dump_data_sources_with_non_existent_path_warns_and_dumps_database():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [
     databases = [
         {'path': '/path/to/database1', 'name': 'database1'},
         {'path': '/path/to/database1', 'name': 'database1'},
     ]
     ]
@@ -193,9 +178,6 @@ def test_dump_data_sources_with_non_existent_path_warns_and_dumps_database():
 
 
 
 
 def test_dump_data_sources_with_name_all_warns_and_dumps_all_databases():
 def test_dump_data_sources_with_name_all_warns_and_dumps_all_databases():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [
     databases = [
         {'path': '/path/to/database1', 'name': 'all'},
         {'path': '/path/to/database1', 'name': 'all'},
     ]
     ]
@@ -226,9 +208,6 @@ def test_dump_data_sources_with_name_all_warns_and_dumps_all_databases():
 
 
 
 
 def test_dump_data_sources_does_not_dump_if_dry_run():
 def test_dump_data_sources_does_not_dump_if_dry_run():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     databases = [{'path': '/path/to/database', 'name': 'database'}]
     databases = [{'path': '/path/to/database', 'name': 'database'}]
 
 
     flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic')
     flexmock(module).should_receive('make_dump_path').and_return('/run/borgmatic')

+ 0 - 15
tests/unit/hooks/data_source/test_zfs.py

@@ -296,9 +296,6 @@ def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_
 
 
 
 
 def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
 def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     dataset = flexmock(
     dataset = flexmock(
         name='dataset',
         name='dataset',
         mount_point='/mnt/dataset',
         mount_point='/mnt/dataset',
@@ -341,9 +338,6 @@ def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
 
 
 
 
 def test_dump_data_sources_with_no_datasets_skips_snapshots():
 def test_dump_data_sources_with_no_datasets_skips_snapshots():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     flexmock(module).should_receive('get_datasets_to_backup').and_return(())
     flexmock(module).should_receive('get_datasets_to_backup').and_return(())
     flexmock(module.os).should_receive('getpid').and_return(1234)
     flexmock(module.os).should_receive('getpid').and_return(1234)
     flexmock(module).should_receive('snapshot_dataset').never()
     flexmock(module).should_receive('snapshot_dataset').never()
@@ -366,9 +360,6 @@ def test_dump_data_sources_with_no_datasets_skips_snapshots():
 
 
 
 
 def test_dump_data_sources_uses_custom_commands():
 def test_dump_data_sources_uses_custom_commands():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     dataset = flexmock(
     dataset = flexmock(
         name='dataset',
         name='dataset',
         mount_point='/mnt/dataset',
         mount_point='/mnt/dataset',
@@ -418,9 +409,6 @@ def test_dump_data_sources_uses_custom_commands():
 
 
 
 
 def test_dump_data_sources_with_dry_run_skips_commands_and_does_not_touch_patterns():
 def test_dump_data_sources_with_dry_run_skips_commands_and_does_not_touch_patterns():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     flexmock(module).should_receive('get_datasets_to_backup').and_return(
     flexmock(module).should_receive('get_datasets_to_backup').and_return(
         (flexmock(name='dataset', mount_point='/mnt/dataset'),)
         (flexmock(name='dataset', mount_point='/mnt/dataset'),)
     )
     )
@@ -445,9 +433,6 @@ def test_dump_data_sources_with_dry_run_skips_commands_and_does_not_touch_patter
 
 
 
 
 def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained_patterns():
 def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained_patterns():
-    flexmock(module.borgmatic.hooks.command).should_receive('Before_after_hooks').and_return(
-        flexmock()
-    )
     dataset = flexmock(
     dataset = flexmock(
         name='dataset',
         name='dataset',
         mount_point='/mnt/dataset',
         mount_point='/mnt/dataset',

+ 0 - 115
tests/unit/hooks/test_command.py

@@ -133,121 +133,6 @@ def test_make_environment_with_pyinstaller_and_LD_LIBRARY_PATH_ORIG_copies_it_in
                 },
                 },
             ),
             ),
         ),
         ),
-        (
-            (
-                {
-                    'before': 'dump_data_sources',
-                    'hooks': ['postgresql'],
-                    'run': ['foo'],
-                },
-                {
-                    'before': 'dump_data_sources',
-                    'hooks': ['lvm'],
-                    'run': ['bar'],
-                },
-                {
-                    'after': 'dump_data_sources',
-                    'hooks': ['lvm'],
-                    'run': ['baz'],
-                },
-            ),
-            {
-                'before': 'dump_data_sources',
-                'hook_name': 'lvm',
-            },
-            (
-                {
-                    'before': 'dump_data_sources',
-                    'hooks': ['lvm'],
-                    'run': ['bar'],
-                },
-            ),
-        ),
-        (
-            (
-                {
-                    'before': 'dump_data_sources',
-                    'run': ['foo'],
-                },
-                {
-                    'before': 'dump_data_sources',
-                    'run': ['bar'],
-                },
-                {
-                    'after': 'dump_data_sources',
-                    'run': ['baz'],
-                },
-            ),
-            {
-                'before': 'dump_data_sources',
-                'hook_name': 'lvm',
-            },
-            (
-                {
-                    'before': 'dump_data_sources',
-                    'run': ['foo'],
-                },
-                {
-                    'before': 'dump_data_sources',
-                    'run': ['bar'],
-                },
-            ),
-        ),
-        (
-            (
-                {
-                    'before': 'dump_data_sources',
-                    'hooks': ['postgresql', 'zfs', 'lvm'],
-                    'run': ['foo'],
-                },
-            ),
-            {
-                'before': 'dump_data_sources',
-                'hook_name': 'lvm',
-            },
-            (
-                {
-                    'before': 'dump_data_sources',
-                    'hooks': ['postgresql', 'zfs', 'lvm'],
-                    'run': ['foo'],
-                },
-            ),
-        ),
-        (
-            (
-                {
-                    'before': 'action',
-                    'when': ['create'],
-                    'run': ['foo'],
-                },
-                {
-                    'before': 'action',
-                    'when': ['prune'],
-                    'run': ['bar'],
-                },
-                {
-                    'before': 'action',
-                    'when': ['compact'],
-                    'run': ['baz'],
-                },
-            ),
-            {
-                'before': 'action',
-                'action_names': ['create', 'compact', 'extract'],
-            },
-            (
-                {
-                    'before': 'action',
-                    'when': ['create'],
-                    'run': ['foo'],
-                },
-                {
-                    'before': 'action',
-                    'when': ['compact'],
-                    'run': ['baz'],
-                },
-            ),
-        ),
         (
         (
             (
             (
                 {
                 {