浏览代码

Actually pass the current configuration to credential hooks.

Dan Helfman 3 月之前
父节点
当前提交
b283e379d0

+ 1 - 1
borgmatic/borg/environment.py

@@ -41,7 +41,7 @@ def make_environment(config):
         value = config.get(option_name)
         value = config.get(option_name)
 
 
         if option_name in CREDENTIAL_OPTIONS and value is not None:
         if option_name in CREDENTIAL_OPTIONS and value is not None:
-            value = borgmatic.hooks.credential.parse.resolve_credential(value)
+            value = borgmatic.hooks.credential.parse.resolve_credential(value, config)
 
 
         if value is not None:
         if value is not None:
             environment[environment_variable_name] = str(value)
             environment[environment_variable_name] = str(value)

+ 87 - 7
borgmatic/hooks/credential/parse.py

@@ -7,17 +7,97 @@ import borgmatic.hooks.dispatch
 IS_A_HOOK = False
 IS_A_HOOK = False
 
 
 
 
+class Hash_adapter:
+    '''
+    A Hash_adapter instance wraps an unhashable object and pretends it's hashable. This is intended
+    for passing to a @functools.cache-decorated function to prevent it from complaining that an
+    argument is unhashable. It should only be used for arguments that you don't want to actually
+    impact the cache hashing, because Hash_adapter doesn't actually hash the object's contents.
+
+    Example usage:
+
+        @functools.cache
+        def func(a, b):
+            print(a, b.actual_value)
+            return a
+
+        func(5, Hash_adapter({1: 2, 3: 4}))  # Calls func(), prints, and returns.
+        func(5, Hash_adapter({1: 2, 3: 4}))  # Hits the cache and just returns the value.
+        func(5, Hash_adapter({5: 6, 7: 8}))  # Also uses cache, since the Hash_adapter is ignored.
+
+    In the above function, the "b" value is one that has been wrapped with Hash_adappter, and
+    therefore "b.actual_value" is necessary to access the original value.
+    '''
+
+    def __init__(self, actual_value):
+        self.actual_value = actual_value
+
+    def __eq__(self, other):
+        return True
+
+    def __hash__(self):
+        return 0
+
+
+UNHASHABLE_TYPES = (dict, list, set)
+
+
+def cache_ignoring_unhashable_arguments(function):
+    '''
+    A function decorator that caches calls to the decorated function but ignores any unhashable
+    arguments when performing cache lookups. This is intended to be a drop-in replacement for
+    functools.cache.
+
+    Example usage:
+
+        @cache_ignoring_unhashable_arguments
+        def func(a, b):
+            print(a, b)
+            return a
+
+        func(5, {1: 2, 3: 4})  # Calls func(), prints, and returns.
+        func(5, {1: 2, 3: 4})  # Hits the cache and just returns the value.
+        func(5, {5: 6, 7: 8})  # Also uses cache, since the unhashable value (the dict) is ignored.
+    '''
+
+    @functools.cache
+    def cached_function(*args, **kwargs):
+        return function(
+            *(arg.actual_value if isinstance(arg, Hash_adapter) else arg for arg in args),
+            **{
+                key: value.actual_value if isinstance(value, Hash_adapter) else value
+                for (key, value) in kwargs.items()
+            },
+        )
+
+    @functools.wraps(function)
+    def wrapper_function(*args, **kwargs):
+        return cached_function(
+            *(Hash_adapter(arg) if isinstance(arg, UNHASHABLE_TYPES) else arg for arg in args),
+            **{
+                key: Hash_adapter(value) if isinstance(value, UNHASHABLE_TYPES) else value
+                for (key, value) in kwargs.items()
+            },
+        )
+
+    wrapper_function.cache_clear = cached_function.cache_clear
+
+    return wrapper_function
+
+
 CREDENTIAL_PATTERN = re.compile(r'\{credential( +(?P<hook_and_parameters>.*))?\}')
 CREDENTIAL_PATTERN = re.compile(r'\{credential( +(?P<hook_and_parameters>.*))?\}')
 
 
 
 
-@functools.cache
-def resolve_credential(value):
+@cache_ignoring_unhashable_arguments
+def resolve_credential(value, config):
     '''
     '''
-    Given a configuration value containing a string like "{credential hookname credentialname}", resolve it by
-    calling the relevant hook to get the actual credential value. If the given value does not
-    actually contain a credential tag, then return it unchanged.
+    Given a configuration value containing a string like "{credential hookname credentialname}" and
+    a configuration dict, resolve the credential by calling the relevant hook to get the actual
+    credential value. If the given value does not actually contain a credential tag, then return it
+    unchanged.
 
 
-    Cache the value so repeated calls to this function don't need to load the credential repeatedly.
+    Cache the value (ignoring the config for purposes of caching), so repeated calls to this
+    function don't need to load the credential repeatedly.
 
 
     Raise ValueError if the config could not be parsed or the credential could not be loaded.
     Raise ValueError if the config could not be parsed or the credential could not be loaded.
     '''
     '''
@@ -40,5 +120,5 @@ def resolve_credential(value):
         raise ValueError(f'Cannot load credential with invalid syntax "{value}"')
         raise ValueError(f'Cannot load credential with invalid syntax "{value}"')
 
 
     return borgmatic.hooks.dispatch.call_hook(
     return borgmatic.hooks.dispatch.call_hook(
-        'load_credential', {}, hook_name, tuple(credential_parameters)
+        'load_credential', config, hook_name, tuple(credential_parameters)
     )
     )

+ 30 - 12
borgmatic/hooks/data_source/mariadb.py

@@ -26,11 +26,11 @@ def make_dump_path(base_directory):  # pragma: no cover
 SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys')
 SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys')
 
 
 
 
-def database_names_to_dump(database, extra_environment, dry_run):
+def database_names_to_dump(database, config, extra_environment, dry_run):
     '''
     '''
-    Given a requested database config, return the corresponding sequence of database names to dump.
-    In the case of "all", query for the names of databases on the configured host and return them,
-    excluding any system databases that will cause problems during restore.
+    Given a requested database config and a configuration dict, return the corresponding sequence of
+    database names to dump. In the case of "all", query for the names of databases on the configured
+    host and return them, excluding any system databases that will cause problems during restore.
     '''
     '''
     if database['name'] != 'all':
     if database['name'] != 'all':
         return (database['name'],)
         return (database['name'],)
@@ -47,7 +47,10 @@ def database_names_to_dump(database, extra_environment, dry_run):
         + (('--port', str(database['port'])) if 'port' in database else ())
         + (('--port', str(database['port'])) if 'port' in database else ())
         + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
         + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
         + (
         + (
-            ('--user', borgmatic.hooks.credential.parse.resolve_credential(database['username']))
+            (
+                '--user',
+                borgmatic.hooks.credential.parse.resolve_credential(database['username'], config),
+            )
             if 'username' in database
             if 'username' in database
             else ()
             else ()
         )
         )
@@ -67,7 +70,7 @@ def database_names_to_dump(database, extra_environment, dry_run):
 
 
 
 
 def execute_dump_command(
 def execute_dump_command(
-    database, dump_path, database_names, extra_environment, dry_run, dry_run_label
+    database, config, dump_path, database_names, extra_environment, dry_run, dry_run_label
 ):
 ):
     '''
     '''
     Kick off a dump for the given MariaDB database (provided as a configuration dict) to a named
     Kick off a dump for the given MariaDB database (provided as a configuration dict) to a named
@@ -102,7 +105,10 @@ def execute_dump_command(
         + (('--port', str(database['port'])) if 'port' in database else ())
         + (('--port', str(database['port'])) if 'port' in database else ())
         + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
         + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
         + (
         + (
-            ('--user', borgmatic.hooks.credential.parse.resolve_credential(database['username']))
+            (
+                '--user',
+                borgmatic.hooks.credential.parse.resolve_credential(database['username'], config),
+            )
             if 'username' in database
             if 'username' in database
             else ()
             else ()
         )
         )
@@ -162,7 +168,11 @@ def dump_data_sources(
     for database in databases:
     for database in databases:
         dump_path = make_dump_path(borgmatic_runtime_directory)
         dump_path = make_dump_path(borgmatic_runtime_directory)
         extra_environment = (
         extra_environment = (
-            {'MYSQL_PWD': borgmatic.hooks.credential.parse.resolve_credential(database['password'])}
+            {
+                'MYSQL_PWD': borgmatic.hooks.credential.parse.resolve_credential(
+                    database['password'], config
+                )
+            }
             if 'password' in database
             if 'password' in database
             else None
             else None
         )
         )
@@ -181,6 +191,7 @@ def dump_data_sources(
                 processes.append(
                 processes.append(
                     execute_dump_command(
                     execute_dump_command(
                         renamed_database,
                         renamed_database,
+                        config,
                         dump_path,
                         dump_path,
                         (dump_name,),
                         (dump_name,),
                         extra_environment,
                         extra_environment,
@@ -192,6 +203,7 @@ def dump_data_sources(
             processes.append(
             processes.append(
                 execute_dump_command(
                 execute_dump_command(
                     database,
                     database,
+                    config,
                     dump_path,
                     dump_path,
                     dump_database_names,
                     dump_database_names,
                     extra_environment,
                     extra_environment,
@@ -265,12 +277,18 @@ def restore_data_source_dump(
         connection_params['port'] or data_source.get('restore_port', data_source.get('port', ''))
         connection_params['port'] or data_source.get('restore_port', data_source.get('port', ''))
     )
     )
     username = borgmatic.hooks.credential.parse.resolve_credential(
     username = borgmatic.hooks.credential.parse.resolve_credential(
-        connection_params['username']
-        or data_source.get('restore_username', data_source.get('username'))
+        (
+            connection_params['username']
+            or data_source.get('restore_username', data_source.get('username'))
+        ),
+        config,
     )
     )
     password = borgmatic.hooks.credential.parse.resolve_credential(
     password = borgmatic.hooks.credential.parse.resolve_credential(
-        connection_params['password']
-        or data_source.get('restore_password', data_source.get('password'))
+        (
+            connection_params['password']
+            or data_source.get('restore_password', data_source.get('password'))
+        ),
+        config,
     )
     )
 
 
     mariadb_restore_command = tuple(
     mariadb_restore_command = tuple(

+ 20 - 8
borgmatic/hooks/data_source/mongodb.py

@@ -69,7 +69,7 @@ def dump_data_sources(
         if dry_run:
         if dry_run:
             continue
             continue
 
 
-        command = build_dump_command(database, dump_filename, dump_format)
+        command = build_dump_command(database, config, dump_filename, dump_format)
 
 
         if dump_format == 'directory':
         if dump_format == 'directory':
             dump.create_parent_directory_for_dump(dump_filename)
             dump.create_parent_directory_for_dump(dump_filename)
@@ -88,7 +88,7 @@ def dump_data_sources(
     return processes
     return processes
 
 
 
 
-def build_dump_command(database, dump_filename, dump_format):
+def build_dump_command(database, config, dump_filename, dump_format):
     '''
     '''
     Return the mongodump command from a single database configuration.
     Return the mongodump command from a single database configuration.
     '''
     '''
@@ -103,7 +103,9 @@ def build_dump_command(database, dump_filename, dump_format):
             (
             (
                 '--username',
                 '--username',
                 shlex.quote(
                 shlex.quote(
-                    borgmatic.hooks.credential.parse.resolve_credential(database['username'])
+                    borgmatic.hooks.credential.parse.resolve_credential(
+                        database['username'], config
+                    )
                 ),
                 ),
             )
             )
             if 'username' in database
             if 'username' in database
@@ -113,7 +115,9 @@ def build_dump_command(database, dump_filename, dump_format):
             (
             (
                 '--password',
                 '--password',
                 shlex.quote(
                 shlex.quote(
-                    borgmatic.hooks.credential.parse.resolve_credential(database['password'])
+                    borgmatic.hooks.credential.parse.resolve_credential(
+                        database['password'], config
+                    )
                 ),
                 ),
             )
             )
             if 'password' in database
             if 'password' in database
@@ -192,7 +196,7 @@ def restore_data_source_dump(
         data_source.get('hostname'),
         data_source.get('hostname'),
     )
     )
     restore_command = build_restore_command(
     restore_command = build_restore_command(
-        extract_process, data_source, dump_filename, connection_params
+        extract_process, data_source, config, dump_filename, connection_params
     )
     )
 
 
     logger.debug(f"Restoring MongoDB database {data_source['name']}{dry_run_label}")
     logger.debug(f"Restoring MongoDB database {data_source['name']}{dry_run_label}")
@@ -209,7 +213,7 @@ def restore_data_source_dump(
     )
     )
 
 
 
 
-def build_restore_command(extract_process, database, dump_filename, connection_params):
+def build_restore_command(extract_process, database, config, dump_filename, connection_params):
     '''
     '''
     Return the mongorestore command from a single database configuration.
     Return the mongorestore command from a single database configuration.
     '''
     '''
@@ -218,10 +222,18 @@ def build_restore_command(extract_process, database, dump_filename, connection_p
     )
     )
     port = str(connection_params['port'] or database.get('restore_port', database.get('port', '')))
     port = str(connection_params['port'] or database.get('restore_port', database.get('port', '')))
     username = borgmatic.hooks.credential.parse.resolve_credential(
     username = borgmatic.hooks.credential.parse.resolve_credential(
-        connection_params['username'] or database.get('restore_username', database.get('username'))
+        (
+            connection_params['username']
+            or database.get('restore_username', database.get('username'))
+        ),
+        config,
     )
     )
     password = borgmatic.hooks.credential.parse.resolve_credential(
     password = borgmatic.hooks.credential.parse.resolve_credential(
-        connection_params['password'] or database.get('restore_password', database.get('password'))
+        (
+            connection_params['password']
+            or database.get('restore_password', database.get('password'))
+        ),
+        config,
     )
     )
 
 
     command = ['mongorestore']
     command = ['mongorestore']

+ 31 - 13
borgmatic/hooks/data_source/mysql.py

@@ -26,11 +26,11 @@ def make_dump_path(base_directory):  # pragma: no cover
 SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys')
 SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys')
 
 
 
 
-def database_names_to_dump(database, extra_environment, dry_run):
+def database_names_to_dump(database, config, extra_environment, dry_run):
     '''
     '''
-    Given a requested database config, return the corresponding sequence of database names to dump.
-    In the case of "all", query for the names of databases on the configured host and return them,
-    excluding any system databases that will cause problems during restore.
+    Given a requested database config and a configuration dict, return the corresponding sequence of
+    database names to dump. In the case of "all", query for the names of databases on the configured
+    host and return them, excluding any system databases that will cause problems during restore.
     '''
     '''
     if database['name'] != 'all':
     if database['name'] != 'all':
         return (database['name'],)
         return (database['name'],)
@@ -47,7 +47,10 @@ def database_names_to_dump(database, extra_environment, dry_run):
         + (('--port', str(database['port'])) if 'port' in database else ())
         + (('--port', str(database['port'])) if 'port' in database else ())
         + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
         + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
         + (
         + (
-            ('--user', borgmatic.hooks.credential.parse.resolve_credential(database['username']))
+            (
+                '--user',
+                borgmatic.hooks.credential.parse.resolve_credential(database['username'], config),
+            )
             if 'username' in database
             if 'username' in database
             else ()
             else ()
         )
         )
@@ -67,7 +70,7 @@ def database_names_to_dump(database, extra_environment, dry_run):
 
 
 
 
 def execute_dump_command(
 def execute_dump_command(
-    database, dump_path, database_names, extra_environment, dry_run, dry_run_label
+    database, config, dump_path, database_names, extra_environment, dry_run, dry_run_label
 ):
 ):
     '''
     '''
     Kick off a dump for the given MySQL/MariaDB database (provided as a configuration dict) to a
     Kick off a dump for the given MySQL/MariaDB database (provided as a configuration dict) to a
@@ -101,7 +104,10 @@ def execute_dump_command(
         + (('--port', str(database['port'])) if 'port' in database else ())
         + (('--port', str(database['port'])) if 'port' in database else ())
         + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
         + (('--protocol', 'tcp') if 'hostname' in database or 'port' in database else ())
         + (
         + (
-            ('--user', borgmatic.hooks.credential.parse.resolve_credential(database['username']))
+            (
+                '--user',
+                borgmatic.hooks.credential.parse.resolve_credential(database['username'], config),
+            )
             if 'username' in database
             if 'username' in database
             else ()
             else ()
         )
         )
@@ -161,11 +167,15 @@ def dump_data_sources(
     for database in databases:
     for database in databases:
         dump_path = make_dump_path(borgmatic_runtime_directory)
         dump_path = make_dump_path(borgmatic_runtime_directory)
         extra_environment = (
         extra_environment = (
-            {'MYSQL_PWD': borgmatic.hooks.credential.parse.resolve_credential(database['password'])}
+            {
+                'MYSQL_PWD': borgmatic.hooks.credential.parse.resolve_credential(
+                    database['password'], config
+                )
+            }
             if 'password' in database
             if 'password' in database
             else None
             else None
         )
         )
-        dump_database_names = database_names_to_dump(database, extra_environment, dry_run)
+        dump_database_names = database_names_to_dump(database, config, extra_environment, dry_run)
 
 
         if not dump_database_names:
         if not dump_database_names:
             if dry_run:
             if dry_run:
@@ -180,6 +190,7 @@ def dump_data_sources(
                 processes.append(
                 processes.append(
                     execute_dump_command(
                     execute_dump_command(
                         renamed_database,
                         renamed_database,
+                        config,
                         dump_path,
                         dump_path,
                         (dump_name,),
                         (dump_name,),
                         extra_environment,
                         extra_environment,
@@ -191,6 +202,7 @@ def dump_data_sources(
             processes.append(
             processes.append(
                 execute_dump_command(
                 execute_dump_command(
                     database,
                     database,
+                    config,
                     dump_path,
                     dump_path,
                     dump_database_names,
                     dump_database_names,
                     extra_environment,
                     extra_environment,
@@ -264,12 +276,18 @@ def restore_data_source_dump(
         connection_params['port'] or data_source.get('restore_port', data_source.get('port', ''))
         connection_params['port'] or data_source.get('restore_port', data_source.get('port', ''))
     )
     )
     username = borgmatic.hooks.credential.parse.resolve_credential(
     username = borgmatic.hooks.credential.parse.resolve_credential(
-        connection_params['username']
-        or data_source.get('restore_username', data_source.get('username'))
+        (
+            connection_params['username']
+            or data_source.get('restore_username', data_source.get('username'))
+        ),
+        config,
     )
     )
     password = borgmatic.hooks.credential.parse.resolve_credential(
     password = borgmatic.hooks.credential.parse.resolve_credential(
-        connection_params['password']
-        or data_source.get('restore_password', data_source.get('password'))
+        (
+            connection_params['password']
+            or data_source.get('restore_password', data_source.get('password'))
+        ),
+        config,
     )
     )
 
 
     mysql_restore_command = tuple(
     mysql_restore_command = tuple(

+ 22 - 16
borgmatic/hooks/data_source/postgresql.py

@@ -25,7 +25,7 @@ def make_dump_path(base_directory):  # pragma: no cover
     return dump.make_data_source_dump_path(base_directory, 'postgresql_databases')
     return dump.make_data_source_dump_path(base_directory, 'postgresql_databases')
 
 
 
 
-def make_extra_environment(database, restore_connection_params=None):
+def make_extra_environment(database, config, restore_connection_params=None):
     '''
     '''
     Make the extra_environment dict from the given database configuration. If restore connection
     Make the extra_environment dict from the given database configuration. If restore connection
     params are given, this is for a restore operation.
     params are given, this is for a restore operation.
@@ -35,12 +35,15 @@ def make_extra_environment(database, restore_connection_params=None):
     try:
     try:
         if restore_connection_params:
         if restore_connection_params:
             extra['PGPASSWORD'] = borgmatic.hooks.credential.parse.resolve_credential(
             extra['PGPASSWORD'] = borgmatic.hooks.credential.parse.resolve_credential(
-                restore_connection_params.get('password')
-                or database.get('restore_password', database['password'])
+                (
+                    restore_connection_params.get('password')
+                    or database.get('restore_password', database['password'])
+                ),
+                config,
             )
             )
         else:
         else:
             extra['PGPASSWORD'] = borgmatic.hooks.credential.parse.resolve_credential(
             extra['PGPASSWORD'] = borgmatic.hooks.credential.parse.resolve_credential(
-                database['password']
+                database['password'], config
             )
             )
     except (AttributeError, KeyError):
     except (AttributeError, KeyError):
         pass
         pass
@@ -62,12 +65,12 @@ def make_extra_environment(database, restore_connection_params=None):
 EXCLUDED_DATABASE_NAMES = ('template0', 'template1')
 EXCLUDED_DATABASE_NAMES = ('template0', 'template1')
 
 
 
 
-def database_names_to_dump(database, extra_environment, dry_run):
+def database_names_to_dump(database, config, extra_environment, dry_run):
     '''
     '''
-    Given a requested database config, return the corresponding sequence of database names to dump.
-    In the case of "all" when a database format is given, query for the names of databases on the
-    configured host and return them. For "all" without a database format, just return a sequence
-    containing "all".
+    Given a requested database config and a configuration dict, return the corresponding sequence of
+    database names to dump. In the case of "all" when a database format is given, query for the
+    names of databases on the configured host and return them. For "all" without a database format,
+    just return a sequence containing "all".
     '''
     '''
     requested_name = database['name']
     requested_name = database['name']
 
 
@@ -89,7 +92,7 @@ def database_names_to_dump(database, extra_environment, dry_run):
         + (
         + (
             (
             (
                 '--username',
                 '--username',
-                borgmatic.hooks.credential.parse.resolve_credential(database['username']),
+                borgmatic.hooks.credential.parse.resolve_credential(database['username'], config),
             )
             )
             if 'username' in database
             if 'username' in database
             else ()
             else ()
@@ -146,9 +149,9 @@ def dump_data_sources(
     logger.info(f'Dumping PostgreSQL databases{dry_run_label}')
     logger.info(f'Dumping PostgreSQL databases{dry_run_label}')
 
 
     for database in databases:
     for database in databases:
-        extra_environment = make_extra_environment(database)
+        extra_environment = make_extra_environment(database, config)
         dump_path = make_dump_path(borgmatic_runtime_directory)
         dump_path = make_dump_path(borgmatic_runtime_directory)
-        dump_database_names = database_names_to_dump(database, extra_environment, dry_run)
+        dump_database_names = database_names_to_dump(database, config, extra_environment, dry_run)
 
 
         if not dump_database_names:
         if not dump_database_names:
             if dry_run:
             if dry_run:
@@ -189,7 +192,7 @@ def dump_data_sources(
                         '--username',
                         '--username',
                         shlex.quote(
                         shlex.quote(
                             borgmatic.hooks.credential.parse.resolve_credential(
                             borgmatic.hooks.credential.parse.resolve_credential(
-                                database['username']
+                                database['username'], config
                             )
                             )
                         ),
                         ),
                     )
                     )
@@ -309,8 +312,11 @@ def restore_data_source_dump(
         connection_params['port'] or data_source.get('restore_port', data_source.get('port', ''))
         connection_params['port'] or data_source.get('restore_port', data_source.get('port', ''))
     )
     )
     username = borgmatic.hooks.credential.parse.resolve_credential(
     username = borgmatic.hooks.credential.parse.resolve_credential(
-        connection_params['username']
-        or data_source.get('restore_username', data_source.get('username'))
+        (
+            connection_params['username']
+            or data_source.get('restore_username', data_source.get('username'))
+        ),
+        config,
     )
     )
 
 
     all_databases = bool(data_source['name'] == 'all')
     all_databases = bool(data_source['name'] == 'all')
@@ -364,7 +370,7 @@ def restore_data_source_dump(
     )
     )
 
 
     extra_environment = make_extra_environment(
     extra_environment = make_extra_environment(
-        data_source, restore_connection_params=connection_params
+        data_source, config, restore_connection_params=connection_params
     )
     )
 
 
     logger.debug(f"Restoring PostgreSQL database {data_source['name']}{dry_run_label}")
     logger.debug(f"Restoring PostgreSQL database {data_source['name']}{dry_run_label}")

+ 3 - 3
borgmatic/hooks/monitoring/ntfy.py

@@ -51,13 +51,13 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
 
 
         try:
         try:
             username = borgmatic.hooks.credential.parse.resolve_credential(
             username = borgmatic.hooks.credential.parse.resolve_credential(
-                hook_config.get('username')
+                hook_config.get('username'), config
             )
             )
             password = borgmatic.hooks.credential.parse.resolve_credential(
             password = borgmatic.hooks.credential.parse.resolve_credential(
-                hook_config.get('password')
+                hook_config.get('password'), config
             )
             )
             access_token = borgmatic.hooks.credential.parse.resolve_credential(
             access_token = borgmatic.hooks.credential.parse.resolve_credential(
-                hook_config.get('access_token')
+                hook_config.get('access_token'), config
             )
             )
         except ValueError as error:
         except ValueError as error:
             logger.warning(f'Ntfy credential error: {error}')
             logger.warning(f'Ntfy credential error: {error}')

+ 1 - 1
borgmatic/hooks/monitoring/pagerduty.py

@@ -42,7 +42,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
 
 
     try:
     try:
         integration_key = borgmatic.hooks.credential.parse.resolve_credential(
         integration_key = borgmatic.hooks.credential.parse.resolve_credential(
-            hook_config.get('integration_key')
+            hook_config.get('integration_key'), config
         )
         )
     except ValueError as error:
     except ValueError as error:
         logger.warning(f'PagerDuty credential error: {error}')
         logger.warning(f'PagerDuty credential error: {error}')

+ 4 - 2
borgmatic/hooks/monitoring/pushover.py

@@ -35,8 +35,10 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
     state_config = hook_config.get(state.name.lower(), {})
     state_config = hook_config.get(state.name.lower(), {})
 
 
     try:
     try:
-        token = borgmatic.hooks.credential.parse.resolve_credential(hook_config.get('token'))
-        user = borgmatic.hooks.credential.parse.resolve_credential(hook_config.get('user'))
+        token = borgmatic.hooks.credential.parse.resolve_credential(
+            hook_config.get('token'), config
+        )
+        user = borgmatic.hooks.credential.parse.resolve_credential(hook_config.get('user'), config)
     except ValueError as error:
     except ValueError as error:
         logger.warning(f'Pushover credential error: {error}')
         logger.warning(f'Pushover credential error: {error}')
         return
         return

+ 9 - 3
borgmatic/hooks/monitoring/zabbix.py

@@ -37,9 +37,15 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
     )
     )
 
 
     try:
     try:
-        username = borgmatic.hooks.credential.parse.resolve_credential(hook_config.get('username'))
-        password = borgmatic.hooks.credential.parse.resolve_credential(hook_config.get('password'))
-        api_key = borgmatic.hooks.credential.parse.resolve_credential(hook_config.get('api_key'))
+        username = borgmatic.hooks.credential.parse.resolve_credential(
+            hook_config.get('username'), config
+        )
+        password = borgmatic.hooks.credential.parse.resolve_credential(
+            hook_config.get('password'), config
+        )
+        api_key = borgmatic.hooks.credential.parse.resolve_credential(
+            hook_config.get('api_key'), config
+        )
     except ValueError as error:
     except ValueError as error:
         logger.warning(f'Zabbix credential error: {error}')
         logger.warning(f'Zabbix credential error: {error}')
         return
         return

+ 5 - 4
tests/unit/borg/test_environment.py

@@ -26,7 +26,7 @@ def test_make_environment_with_passphrase_should_set_environment():
     flexmock(module.os.environ).should_receive('get').and_return(None)
     flexmock(module.os.environ).should_receive('get').and_return(None)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
 
 
     environment = module.make_environment({'encryption_passphrase': 'pass'})
     environment = module.make_environment({'encryption_passphrase': 'pass'})
 
 
@@ -34,16 +34,17 @@ def test_make_environment_with_passphrase_should_set_environment():
 
 
 
 
 def test_make_environment_with_credential_tag_passphrase_should_load_it_and_set_environment():
 def test_make_environment_with_credential_tag_passphrase_should_load_it_and_set_environment():
+    config = {'encryption_passphrase': '{credential systemd pass}'}
     flexmock(module.borgmatic.borg.passcommand).should_receive(
     flexmock(module.borgmatic.borg.passcommand).should_receive(
         'get_passphrase_from_passcommand'
         'get_passphrase_from_passcommand'
     ).and_return(None)
     ).and_return(None)
     flexmock(module.os).should_receive('pipe').never()
     flexmock(module.os).should_receive('pipe').never()
     flexmock(module.os.environ).should_receive('get').and_return(None)
     flexmock(module.os.environ).should_receive('get').and_return(None)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
-        'resolve_credential'
-    ).with_args('{credential systemd pass}').and_return('pass')
+        'resolve_credential',
+    ).with_args('{credential systemd pass}', config).and_return('pass')
 
 
-    environment = module.make_environment({'encryption_passphrase': '{credential systemd pass}'})
+    environment = module.make_environment(config)
 
 
     assert environment.get('BORG_PASSPHRASE') == 'pass'
     assert environment.get('BORG_PASSPHRASE') == 'pass'
 
 

+ 38 - 7
tests/unit/hooks/credential/test_parse.py

@@ -4,18 +4,49 @@ from flexmock import flexmock
 from borgmatic.hooks.credential import parse as module
 from borgmatic.hooks.credential import parse as module
 
 
 
 
+def test_hash_adapter_is_always_equal():
+    assert module.Hash_adapter({1: 2}) == module.Hash_adapter({3: 4})
+
+
+def test_hash_adapter_alwaysh_hashes_the_same():
+    assert hash(module.Hash_adapter({1: 2})) == hash(module.Hash_adapter({3: 4}))
+
+
+def test_cache_ignoring_unhashable_arguments_caches_arguments_after_first_call():
+    hashable = 3
+    unhashable = {1, 2}
+    calls = 0
+
+    @module.cache_ignoring_unhashable_arguments
+    def function(first, second, third):
+        nonlocal calls
+        calls += 1
+
+        assert first == hashable
+        assert second == unhashable
+        assert third == unhashable
+
+        return first
+
+    assert function(hashable, unhashable, third=unhashable) == hashable
+    assert calls == 1
+
+    assert function(hashable, unhashable, third=unhashable) == hashable
+    assert calls == 1
+
+
 def test_resolve_credential_passes_through_string_without_credential():
 def test_resolve_credential_passes_through_string_without_credential():
     module.resolve_credential.cache_clear()
     module.resolve_credential.cache_clear()
     flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').never()
     flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').never()
 
 
-    assert module.resolve_credential('{no credentials here}') == '{no credentials here}'
+    assert module.resolve_credential('{no credentials here}', config={}) == '{no credentials here}'
 
 
 
 
 def test_resolve_credential_passes_through_none():
 def test_resolve_credential_passes_through_none():
     module.resolve_credential.cache_clear()
     module.resolve_credential.cache_clear()
     flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').never()
     flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').never()
 
 
-    assert module.resolve_credential(None) is None
+    assert module.resolve_credential(None, config={}) is None
 
 
 
 
 @pytest.mark.parametrize('invalid_value', ('{credential}', '{credential }', '{credential systemd}'))
 @pytest.mark.parametrize('invalid_value', ('{credential}', '{credential }', '{credential systemd}'))
@@ -24,7 +55,7 @@ def test_resolve_credential_with_invalid_credential_raises(invalid_value):
     flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').never()
     flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').never()
 
 
     with pytest.raises(ValueError):
     with pytest.raises(ValueError):
-        module.resolve_credential(invalid_value)
+        module.resolve_credential(invalid_value, config={})
 
 
 
 
 def test_resolve_credential_with_valid_credential_loads_credential():
 def test_resolve_credential_with_valid_credential_loads_credential():
@@ -36,7 +67,7 @@ def test_resolve_credential_with_valid_credential_loads_credential():
         ('mycredential',),
         ('mycredential',),
     ).and_return('result').once()
     ).and_return('result').once()
 
 
-    assert module.resolve_credential('{credential systemd mycredential}') == 'result'
+    assert module.resolve_credential('{credential systemd mycredential}', config={}) == 'result'
 
 
 
 
 def test_resolve_credential_with_valid_credential_and_quoted_parameters_loads_credential():
 def test_resolve_credential_with_valid_credential_and_quoted_parameters_loads_credential():
@@ -48,7 +79,7 @@ def test_resolve_credential_with_valid_credential_and_quoted_parameters_loads_cr
         ('my credential',),
         ('my credential',),
     ).and_return('result').once()
     ).and_return('result').once()
 
 
-    assert module.resolve_credential('{credential systemd "my credential"}') == 'result'
+    assert module.resolve_credential('{credential systemd "my credential"}', config={}) == 'result'
 
 
 
 
 def test_resolve_credential_caches_credential_after_first_call():
 def test_resolve_credential_caches_credential_after_first_call():
@@ -60,5 +91,5 @@ def test_resolve_credential_caches_credential_after_first_call():
         ('mycredential',),
         ('mycredential',),
     ).and_return('result').once()
     ).and_return('result').once()
 
 
-    assert module.resolve_credential('{credential systemd mycredential}') == 'result'
-    assert module.resolve_credential('{credential systemd mycredential}') == 'result'
+    assert module.resolve_credential('{credential systemd mycredential}', config={}) == 'result'
+    assert module.resolve_credential('{credential systemd mycredential}', config={}) == 'result'

+ 39 - 27
tests/unit/hooks/data_source/test_mariadb.py

@@ -9,7 +9,7 @@ from borgmatic.hooks.data_source import mariadb as module
 def test_database_names_to_dump_passes_through_name():
 def test_database_names_to_dump_passes_through_name():
     extra_environment = flexmock()
     extra_environment = flexmock()
 
 
-    names = module.database_names_to_dump({'name': 'foo'}, extra_environment, dry_run=False)
+    names = module.database_names_to_dump({'name': 'foo'}, {}, extra_environment, dry_run=False)
 
 
     assert names == ('foo',)
     assert names == ('foo',)
 
 
@@ -18,7 +18,7 @@ def test_database_names_to_dump_bails_for_dry_run():
     extra_environment = flexmock()
     extra_environment = flexmock()
     flexmock(module).should_receive('execute_command_and_capture_output').never()
     flexmock(module).should_receive('execute_command_and_capture_output').never()
 
 
-    names = module.database_names_to_dump({'name': 'all'}, extra_environment, dry_run=True)
+    names = module.database_names_to_dump({'name': 'all'}, {}, extra_environment, dry_run=True)
 
 
     assert names == ()
     assert names == ()
 
 
@@ -27,13 +27,13 @@ def test_database_names_to_dump_queries_mariadb_for_database_names():
     extra_environment = flexmock()
     extra_environment = flexmock()
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('mariadb', '--skip-column-names', '--batch', '--execute', 'show schemas'),
         ('mariadb', '--skip-column-names', '--batch', '--execute', 'show schemas'),
         extra_environment=extra_environment,
         extra_environment=extra_environment,
     ).and_return('foo\nbar\nmysql\n').once()
     ).and_return('foo\nbar\nmysql\n').once()
 
 
-    names = module.database_names_to_dump({'name': 'all'}, extra_environment, dry_run=False)
+    names = module.database_names_to_dump({'name': 'all'}, {}, extra_environment, dry_run=False)
 
 
     assert names == ('foo', 'bar')
     assert names == ('foo', 'bar')
 
 
@@ -55,7 +55,7 @@ def test_dump_data_sources_dumps_each_database():
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
         ('bar',)
         ('bar',)
     )
     )
@@ -63,6 +63,7 @@ def test_dump_data_sources_dumps_each_database():
     for name, process in zip(('foo', 'bar'), processes):
     for name, process in zip(('foo', 'bar'), processes):
         flexmock(module).should_receive('execute_dump_command').with_args(
         flexmock(module).should_receive('execute_dump_command').with_args(
             database={'name': name},
             database={'name': name},
+            config={},
             dump_path=object,
             dump_path=object,
             database_names=(name,),
             database_names=(name,),
             extra_environment=object,
             extra_environment=object,
@@ -89,13 +90,14 @@ def test_dump_data_sources_dumps_with_password():
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
         ('bar',)
         ('bar',)
     )
     )
 
 
     flexmock(module).should_receive('execute_dump_command').with_args(
     flexmock(module).should_receive('execute_dump_command').with_args(
         database=database,
         database=database,
+        config={},
         dump_path=object,
         dump_path=object,
         database_names=('foo',),
         database_names=('foo',),
         extra_environment={'MYSQL_PWD': 'trustsome1'},
         extra_environment={'MYSQL_PWD': 'trustsome1'},
@@ -119,10 +121,11 @@ def test_dump_data_sources_dumps_all_databases_at_once():
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
     flexmock(module).should_receive('execute_dump_command').with_args(
     flexmock(module).should_receive('execute_dump_command').with_args(
         database={'name': 'all'},
         database={'name': 'all'},
+        config={},
         dump_path=object,
         dump_path=object,
         database_names=('foo', 'bar'),
         database_names=('foo', 'bar'),
         extra_environment=object,
         extra_environment=object,
@@ -146,12 +149,13 @@ def test_dump_data_sources_dumps_all_databases_separately_when_format_configured
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
 
 
     for name, process in zip(('foo', 'bar'), processes):
     for name, process in zip(('foo', 'bar'), processes):
         flexmock(module).should_receive('execute_dump_command').with_args(
         flexmock(module).should_receive('execute_dump_command').with_args(
             database={'name': name, 'format': 'sql'},
             database={'name': name, 'format': 'sql'},
+            config={},
             dump_path=object,
             dump_path=object,
             database_names=(name,),
             database_names=(name,),
             extra_environment=object,
             extra_environment=object,
@@ -186,7 +190,7 @@ def test_database_names_to_dump_runs_mariadb_with_list_options():
         extra_environment=None,
         extra_environment=None,
     ).and_return(('foo\nbar')).once()
     ).and_return(('foo\nbar')).once()
 
 
-    assert module.database_names_to_dump(database, None, '') == ('foo', 'bar')
+    assert module.database_names_to_dump(database, {}, None, '') == ('foo', 'bar')
 
 
 
 
 def test_database_names_to_dump_runs_non_default_mariadb_with_list_options():
 def test_database_names_to_dump_runs_non_default_mariadb_with_list_options():
@@ -207,7 +211,7 @@ def test_database_names_to_dump_runs_non_default_mariadb_with_list_options():
         ),
         ),
     ).and_return(('foo\nbar')).once()
     ).and_return(('foo\nbar')).once()
 
 
-    assert module.database_names_to_dump(database, None, '') == ('foo', 'bar')
+    assert module.database_names_to_dump(database, {}, None, '') == ('foo', 'bar')
 
 
 
 
 def test_execute_dump_command_runs_mariadb_dump():
 def test_execute_dump_command_runs_mariadb_dump():
@@ -216,7 +220,7 @@ def test_execute_dump_command_runs_mariadb_dump():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -235,6 +239,7 @@ def test_execute_dump_command_runs_mariadb_dump():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo'},
             database={'name': 'foo'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -251,7 +256,7 @@ def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -269,6 +274,7 @@ def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo', 'add_drop_database': False},
             database={'name': 'foo', 'add_drop_database': False},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -285,7 +291,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -310,6 +316,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo', 'hostname': 'database.example.org', 'port': 5433},
             database={'name': 'foo', 'hostname': 'database.example.org', 'port': 5433},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -326,7 +333,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -347,6 +354,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo', 'username': 'root', 'password': 'trustsome1'},
             database={'name': 'foo', 'username': 'root', 'password': 'trustsome1'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment={'MYSQL_PWD': 'trustsome1'},
             extra_environment={'MYSQL_PWD': 'trustsome1'},
@@ -363,7 +371,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_options():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -383,6 +391,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_options():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo', 'options': '--stuff=such'},
             database={'name': 'foo', 'options': '--stuff=such'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -399,7 +408,7 @@ def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -423,6 +432,7 @@ def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
                 'mariadb_dump_command': 'custom_mariadb_dump',
                 'mariadb_dump_command': 'custom_mariadb_dump',
                 'options': '--stuff=such',
                 'options': '--stuff=such',
             },  # Custom MariaDB dump command specified
             },  # Custom MariaDB dump command specified
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -442,6 +452,7 @@ def test_execute_dump_command_with_duplicate_dump_skips_mariadb_dump():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo'},
             database={'name': 'foo'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -457,7 +468,7 @@ def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').never()
     flexmock(module).should_receive('execute_command').never()
@@ -465,6 +476,7 @@ def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo'},
             database={'name': 'foo'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -480,7 +492,7 @@ def test_dump_data_sources_errors_for_missing_all_databases():
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
         'databases/localhost/all'
         'databases/localhost/all'
     )
     )
@@ -502,7 +514,7 @@ def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
         'databases/localhost/all'
         'databases/localhost/all'
     )
     )
@@ -527,7 +539,7 @@ def test_restore_data_source_dump_runs_mariadb_to_restore():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mariadb', '--batch'),
         ('mariadb', '--batch'),
         processes=[extract_process],
         processes=[extract_process],
@@ -558,7 +570,7 @@ def test_restore_data_source_dump_runs_mariadb_with_options():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mariadb', '--batch', '--harder'),
         ('mariadb', '--batch', '--harder'),
         processes=[extract_process],
         processes=[extract_process],
@@ -591,7 +603,7 @@ def test_restore_data_source_dump_runs_non_default_mariadb_with_options():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('custom_mariadb', '--batch', '--harder'),
         ('custom_mariadb', '--batch', '--harder'),
         processes=[extract_process],
         processes=[extract_process],
@@ -622,7 +634,7 @@ def test_restore_data_source_dump_runs_mariadb_with_hostname_and_port():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
         (
             'mariadb',
             'mariadb',
@@ -662,7 +674,7 @@ def test_restore_data_source_dump_runs_mariadb_with_username_and_password():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mariadb', '--batch', '--user', 'root'),
         ('mariadb', '--batch', '--user', 'root'),
         processes=[extract_process],
         processes=[extract_process],
@@ -703,7 +715,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
         (
             'mariadb',
             'mariadb',
@@ -757,7 +769,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
         (
             'mariadb',
             'mariadb',
@@ -798,7 +810,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').never()
     flexmock(module).should_receive('execute_command_with_processes').never()
 
 
     module.restore_data_source_dump(
     module.restore_data_source_dump(

+ 13 - 13
tests/unit/hooks/data_source/test_mongodb.py

@@ -126,7 +126,7 @@ def test_dump_data_sources_runs_mongodump_with_username_and_password():
     )
     )
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -247,9 +247,9 @@ def test_build_dump_command_with_username_injection_attack_gets_escaped():
     database = {'name': 'test', 'username': 'bob; naughty-command'}
     database = {'name': 'test', 'username': 'bob; naughty-command'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
 
 
-    command = module.build_dump_command(database, dump_filename='test', dump_format='archive')
+    command = module.build_dump_command(database, {}, dump_filename='test', dump_format='archive')
 
 
     assert "'bob; naughty-command'" in command
     assert "'bob; naughty-command'" in command
 
 
@@ -262,7 +262,7 @@ def test_restore_data_source_dump_runs_mongorestore():
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ['mongorestore', '--archive', '--drop'],
         ['mongorestore', '--archive', '--drop'],
         processes=[extract_process],
         processes=[extract_process],
@@ -296,7 +296,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_hostname_and_port():
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         [
         [
             'mongorestore',
             'mongorestore',
@@ -344,7 +344,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_username_and_password()
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         [
         [
             'mongorestore',
             'mongorestore',
@@ -398,7 +398,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         [
         [
             'mongorestore',
             'mongorestore',
@@ -456,7 +456,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         [
         [
             'mongorestore',
             'mongorestore',
@@ -502,7 +502,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_options():
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ['mongorestore', '--archive', '--drop', '--harder'],
         ['mongorestore', '--archive', '--drop', '--harder'],
         processes=[extract_process],
         processes=[extract_process],
@@ -534,7 +534,7 @@ def test_restore_databases_dump_runs_mongorestore_with_schemas():
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         [
         [
             'mongorestore',
             'mongorestore',
@@ -574,7 +574,7 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump():
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ['mongorestore', '--archive'],
         ['mongorestore', '--archive'],
         processes=[extract_process],
         processes=[extract_process],
@@ -605,7 +605,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').never()
     flexmock(module).should_receive('execute_command_with_processes').never()
 
 
     module.restore_data_source_dump(
     module.restore_data_source_dump(
@@ -631,7 +631,7 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk():
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ['mongorestore', '--dir', '/dump/path', '--drop'],
         ['mongorestore', '--dir', '/dump/path', '--drop'],
         processes=[],
         processes=[],

+ 37 - 25
tests/unit/hooks/data_source/test_mysql.py

@@ -9,7 +9,7 @@ from borgmatic.hooks.data_source import mysql as module
 def test_database_names_to_dump_passes_through_name():
 def test_database_names_to_dump_passes_through_name():
     extra_environment = flexmock()
     extra_environment = flexmock()
 
 
-    names = module.database_names_to_dump({'name': 'foo'}, extra_environment, dry_run=False)
+    names = module.database_names_to_dump({'name': 'foo'}, {}, extra_environment, dry_run=False)
 
 
     assert names == ('foo',)
     assert names == ('foo',)
 
 
@@ -18,10 +18,10 @@ def test_database_names_to_dump_bails_for_dry_run():
     extra_environment = flexmock()
     extra_environment = flexmock()
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').never()
     flexmock(module).should_receive('execute_command_and_capture_output').never()
 
 
-    names = module.database_names_to_dump({'name': 'all'}, extra_environment, dry_run=True)
+    names = module.database_names_to_dump({'name': 'all'}, {}, extra_environment, dry_run=True)
 
 
     assert names == ()
     assert names == ()
 
 
@@ -30,13 +30,13 @@ def test_database_names_to_dump_queries_mysql_for_database_names():
     extra_environment = flexmock()
     extra_environment = flexmock()
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('mysql', '--skip-column-names', '--batch', '--execute', 'show schemas'),
         ('mysql', '--skip-column-names', '--batch', '--execute', 'show schemas'),
         extra_environment=extra_environment,
         extra_environment=extra_environment,
     ).and_return('foo\nbar\nmysql\n').once()
     ).and_return('foo\nbar\nmysql\n').once()
 
 
-    names = module.database_names_to_dump({'name': 'all'}, extra_environment, dry_run=False)
+    names = module.database_names_to_dump({'name': 'all'}, {}, extra_environment, dry_run=False)
 
 
     assert names == ('foo', 'bar')
     assert names == ('foo', 'bar')
 
 
@@ -63,6 +63,7 @@ def test_dump_data_sources_dumps_each_database():
     for name, process in zip(('foo', 'bar'), processes):
     for name, process in zip(('foo', 'bar'), processes):
         flexmock(module).should_receive('execute_dump_command').with_args(
         flexmock(module).should_receive('execute_dump_command').with_args(
             database={'name': name},
             database={'name': name},
+            config={},
             dump_path=object,
             dump_path=object,
             database_names=(name,),
             database_names=(name,),
             extra_environment=object,
             extra_environment=object,
@@ -89,13 +90,14 @@ def test_dump_data_sources_dumps_with_password():
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
         ('bar',)
         ('bar',)
     )
     )
 
 
     flexmock(module).should_receive('execute_dump_command').with_args(
     flexmock(module).should_receive('execute_dump_command').with_args(
         database=database,
         database=database,
+        config={},
         dump_path=object,
         dump_path=object,
         database_names=('foo',),
         database_names=('foo',),
         extra_environment={'MYSQL_PWD': 'trustsome1'},
         extra_environment={'MYSQL_PWD': 'trustsome1'},
@@ -120,6 +122,7 @@ def test_dump_data_sources_dumps_all_databases_at_once():
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
     flexmock(module).should_receive('execute_dump_command').with_args(
     flexmock(module).should_receive('execute_dump_command').with_args(
         database={'name': 'all'},
         database={'name': 'all'},
+        config={},
         dump_path=object,
         dump_path=object,
         database_names=('foo', 'bar'),
         database_names=('foo', 'bar'),
         extra_environment=object,
         extra_environment=object,
@@ -146,6 +149,7 @@ def test_dump_data_sources_dumps_all_databases_separately_when_format_configured
     for name, process in zip(('foo', 'bar'), processes):
     for name, process in zip(('foo', 'bar'), processes):
         flexmock(module).should_receive('execute_dump_command').with_args(
         flexmock(module).should_receive('execute_dump_command').with_args(
             database={'name': name, 'format': 'sql'},
             database={'name': name, 'format': 'sql'},
+            config={},
             dump_path=object,
             dump_path=object,
             database_names=(name,),
             database_names=(name,),
             extra_environment=object,
             extra_environment=object,
@@ -180,7 +184,7 @@ def test_database_names_to_dump_runs_mysql_with_list_options():
         extra_environment=None,
         extra_environment=None,
     ).and_return(('foo\nbar')).once()
     ).and_return(('foo\nbar')).once()
 
 
-    assert module.database_names_to_dump(database, None, '') == ('foo', 'bar')
+    assert module.database_names_to_dump(database, {}, None, '') == ('foo', 'bar')
 
 
 
 
 def test_database_names_to_dump_runs_non_default_mysql_with_list_options():
 def test_database_names_to_dump_runs_non_default_mysql_with_list_options():
@@ -201,7 +205,7 @@ def test_database_names_to_dump_runs_non_default_mysql_with_list_options():
         ),
         ),
     ).and_return(('foo\nbar')).once()
     ).and_return(('foo\nbar')).once()
 
 
-    assert module.database_names_to_dump(database, None, '') == ('foo', 'bar')
+    assert module.database_names_to_dump(database, {}, None, '') == ('foo', 'bar')
 
 
 
 
 def test_execute_dump_command_runs_mysqldump():
 def test_execute_dump_command_runs_mysqldump():
@@ -210,7 +214,7 @@ def test_execute_dump_command_runs_mysqldump():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -229,6 +233,7 @@ def test_execute_dump_command_runs_mysqldump():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo'},
             database={'name': 'foo'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -245,7 +250,7 @@ def test_execute_dump_command_runs_mysqldump_without_add_drop_database():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -263,6 +268,7 @@ def test_execute_dump_command_runs_mysqldump_without_add_drop_database():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo', 'add_drop_database': False},
             database={'name': 'foo', 'add_drop_database': False},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -279,7 +285,7 @@ def test_execute_dump_command_runs_mysqldump_with_hostname_and_port():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -304,6 +310,7 @@ def test_execute_dump_command_runs_mysqldump_with_hostname_and_port():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo', 'hostname': 'database.example.org', 'port': 5433},
             database={'name': 'foo', 'hostname': 'database.example.org', 'port': 5433},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -320,7 +327,7 @@ def test_execute_dump_command_runs_mysqldump_with_username_and_password():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -341,6 +348,7 @@ def test_execute_dump_command_runs_mysqldump_with_username_and_password():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo', 'username': 'root', 'password': 'trustsome1'},
             database={'name': 'foo', 'username': 'root', 'password': 'trustsome1'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment={'MYSQL_PWD': 'trustsome1'},
             extra_environment={'MYSQL_PWD': 'trustsome1'},
@@ -357,7 +365,7 @@ def test_execute_dump_command_runs_mysqldump_with_options():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -377,6 +385,7 @@ def test_execute_dump_command_runs_mysqldump_with_options():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo', 'options': '--stuff=such'},
             database={'name': 'foo', 'options': '--stuff=such'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -393,7 +402,7 @@ def test_execute_dump_command_runs_non_default_mysqldump():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -415,6 +424,7 @@ def test_execute_dump_command_runs_non_default_mysqldump():
                 'name': 'foo',
                 'name': 'foo',
                 'mysql_dump_command': 'custom_mysqldump',
                 'mysql_dump_command': 'custom_mysqldump',
             },  # Custom MySQL dump command specified
             },  # Custom MySQL dump command specified
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -434,6 +444,7 @@ def test_execute_dump_command_with_duplicate_dump_skips_mysqldump():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo'},
             database={'name': 'foo'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -449,7 +460,7 @@ def test_execute_dump_command_with_dry_run_skips_mysqldump():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').never()
     flexmock(module).should_receive('execute_command').never()
@@ -457,6 +468,7 @@ def test_execute_dump_command_with_dry_run_skips_mysqldump():
     assert (
     assert (
         module.execute_dump_command(
         module.execute_dump_command(
             database={'name': 'foo'},
             database={'name': 'foo'},
+            config={},
             dump_path=flexmock(),
             dump_path=flexmock(),
             database_names=('foo',),
             database_names=('foo',),
             extra_environment=None,
             extra_environment=None,
@@ -472,7 +484,7 @@ def test_dump_data_sources_errors_for_missing_all_databases():
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
         'databases/localhost/all'
         'databases/localhost/all'
     )
     )
@@ -494,7 +506,7 @@ def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
         'databases/localhost/all'
         'databases/localhost/all'
     )
     )
@@ -519,7 +531,7 @@ def test_restore_data_source_dump_runs_mysql_to_restore():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mysql', '--batch'),
         ('mysql', '--batch'),
         processes=[extract_process],
         processes=[extract_process],
@@ -550,7 +562,7 @@ def test_restore_data_source_dump_runs_mysql_with_options():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mysql', '--batch', '--harder'),
         ('mysql', '--batch', '--harder'),
         processes=[extract_process],
         processes=[extract_process],
@@ -581,7 +593,7 @@ def test_restore_data_source_dump_runs_non_default_mysql_with_options():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('custom_mysql', '--batch', '--harder'),
         ('custom_mysql', '--batch', '--harder'),
         processes=[extract_process],
         processes=[extract_process],
@@ -612,7 +624,7 @@ def test_restore_data_source_dump_runs_mysql_with_hostname_and_port():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
         (
             'mysql',
             'mysql',
@@ -652,7 +664,7 @@ def test_restore_data_source_dump_runs_mysql_with_username_and_password():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mysql', '--batch', '--user', 'root'),
         ('mysql', '--batch', '--user', 'root'),
         processes=[extract_process],
         processes=[extract_process],
@@ -693,7 +705,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
         (
             'mysql',
             'mysql',
@@ -747,7 +759,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').with_args(
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
         (
             'mysql',
             'mysql',
@@ -788,7 +800,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_with_processes').never()
     flexmock(module).should_receive('execute_command_with_processes').never()
 
 
     module.restore_data_source_dump(
     module.restore_data_source_dump(

+ 44 - 44
tests/unit/hooks/data_source/test_postgresql.py

@@ -26,9 +26,9 @@ def test_make_extra_environment_maps_options_to_environment():
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
 
 
-    extra_env = module.make_extra_environment(database)
+    extra_env = module.make_extra_environment(database, {})
 
 
     assert extra_env == expected
     assert extra_env == expected
 
 
@@ -37,10 +37,10 @@ def test_make_extra_environment_with_cli_password_sets_correct_password():
     database = {'name': 'foo', 'restore_password': 'trustsome1', 'password': 'anotherpassword'}
     database = {'name': 'foo', 'restore_password': 'trustsome1', 'password': 'anotherpassword'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
 
 
     extra = module.make_extra_environment(
     extra = module.make_extra_environment(
-        database, restore_connection_params={'password': 'clipassword'}
+        database, {}, restore_connection_params={'password': 'clipassword'}
     )
     )
 
 
     assert extra['PGPASSWORD'] == 'clipassword'
     assert extra['PGPASSWORD'] == 'clipassword'
@@ -50,7 +50,7 @@ def test_make_extra_environment_without_cli_password_or_configured_password_does
     database = {'name': 'foo'}
     database = {'name': 'foo'}
 
 
     extra = module.make_extra_environment(
     extra = module.make_extra_environment(
-        database, restore_connection_params={'username': 'someone'}
+        database, {}, restore_connection_params={'username': 'someone'}
     )
     )
 
 
     assert 'PGPASSWORD' not in extra
     assert 'PGPASSWORD' not in extra
@@ -59,7 +59,7 @@ def test_make_extra_environment_without_cli_password_or_configured_password_does
 def test_make_extra_environment_without_ssl_mode_does_not_set_ssl_mode():
 def test_make_extra_environment_without_ssl_mode_does_not_set_ssl_mode():
     database = {'name': 'foo'}
     database = {'name': 'foo'}
 
 
-    extra = module.make_extra_environment(database)
+    extra = module.make_extra_environment(database, {})
 
 
     assert 'PGSSLMODE' not in extra
     assert 'PGSSLMODE' not in extra
 
 
@@ -67,41 +67,41 @@ def test_make_extra_environment_without_ssl_mode_does_not_set_ssl_mode():
 def test_database_names_to_dump_passes_through_individual_database_name():
 def test_database_names_to_dump_passes_through_individual_database_name():
     database = {'name': 'foo'}
     database = {'name': 'foo'}
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == ('foo',)
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
 
 
 
 
 def test_database_names_to_dump_passes_through_individual_database_name_with_format():
 def test_database_names_to_dump_passes_through_individual_database_name_with_format():
     database = {'name': 'foo', 'format': 'custom'}
     database = {'name': 'foo', 'format': 'custom'}
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == ('foo',)
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
 
 
 
 
 def test_database_names_to_dump_passes_through_all_without_format():
 def test_database_names_to_dump_passes_through_all_without_format():
     database = {'name': 'all'}
     database = {'name': 'all'}
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == ('all',)
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('all',)
 
 
 
 
 def test_database_names_to_dump_with_all_and_format_and_dry_run_bails():
 def test_database_names_to_dump_with_all_and_format_and_dry_run_bails():
     database = {'name': 'all', 'format': 'custom'}
     database = {'name': 'all', 'format': 'custom'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').never()
     flexmock(module).should_receive('execute_command_and_capture_output').never()
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=True) == ()
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=True) == ()
 
 
 
 
 def test_database_names_to_dump_with_all_and_format_lists_databases():
 def test_database_names_to_dump_with_all_and_format_lists_databases():
     database = {'name': 'all', 'format': 'custom'}
     database = {'name': 'all', 'format': 'custom'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').and_return(
     flexmock(module).should_receive('execute_command_and_capture_output').and_return(
         'foo,test,\nbar,test,"stuff and such"'
         'foo,test,\nbar,test,"stuff and such"'
     )
     )
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == (
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
         'foo',
         'foo',
         'bar',
         'bar',
     )
     )
@@ -111,7 +111,7 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_hostnam
     database = {'name': 'all', 'format': 'custom', 'hostname': 'localhost', 'port': 1234}
     database = {'name': 'all', 'format': 'custom', 'hostname': 'localhost', 'port': 1234}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         (
         (
             'psql',
             'psql',
@@ -128,7 +128,7 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_hostnam
         extra_environment=object,
         extra_environment=object,
     ).and_return('foo,test,\nbar,test,"stuff and such"')
     ).and_return('foo,test,\nbar,test,"stuff and such"')
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == (
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
         'foo',
         'foo',
         'bar',
         'bar',
     )
     )
@@ -138,7 +138,7 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_usernam
     database = {'name': 'all', 'format': 'custom', 'username': 'postgres'}
     database = {'name': 'all', 'format': 'custom', 'username': 'postgres'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         (
         (
             'psql',
             'psql',
@@ -153,7 +153,7 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_usernam
         extra_environment=object,
         extra_environment=object,
     ).and_return('foo,test,\nbar,test,"stuff and such"')
     ).and_return('foo,test,\nbar,test,"stuff and such"')
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == (
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
         'foo',
         'foo',
         'bar',
         'bar',
     )
     )
@@ -163,13 +163,13 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_options
     database = {'name': 'all', 'format': 'custom', 'list_options': '--harder'}
     database = {'name': 'all', 'format': 'custom', 'list_options': '--harder'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('psql', '--list', '--no-password', '--no-psqlrc', '--csv', '--tuples-only', '--harder'),
         ('psql', '--list', '--no-password', '--no-psqlrc', '--csv', '--tuples-only', '--harder'),
         extra_environment=object,
         extra_environment=object,
     ).and_return('foo,test,\nbar,test,"stuff and such"')
     ).and_return('foo,test,\nbar,test,"stuff and such"')
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == (
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
         'foo',
         'foo',
         'bar',
         'bar',
     )
     )
@@ -179,12 +179,12 @@ def test_database_names_to_dump_with_all_and_format_excludes_particular_database
     database = {'name': 'all', 'format': 'custom'}
     database = {'name': 'all', 'format': 'custom'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').and_return(
     flexmock(module).should_receive('execute_command_and_capture_output').and_return(
         'foo,test,\ntemplate0,test,blah'
         'foo,test,\ntemplate0,test,blah'
     )
     )
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == ('foo',)
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
 
 
 
 
 def test_database_names_to_dump_with_all_and_psql_command_uses_custom_command():
 def test_database_names_to_dump_with_all_and_psql_command_uses_custom_command():
@@ -195,7 +195,7 @@ def test_database_names_to_dump_with_all_and_psql_command_uses_custom_command():
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         (
         (
             'docker',
             'docker',
@@ -213,7 +213,7 @@ def test_database_names_to_dump_with_all_and_psql_command_uses_custom_command():
         extra_environment=object,
         extra_environment=object,
     ).and_return('foo,text').once()
     ).and_return('foo,text').once()
 
 
-    assert module.database_names_to_dump(database, flexmock(), dry_run=False) == ('foo',)
+    assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
 
 
 
 
 def test_use_streaming_true_for_any_non_directory_format_databases():
 def test_use_streaming_true_for_any_non_directory_format_databases():
@@ -248,7 +248,7 @@ def test_dump_data_sources_runs_pg_dump_for_each_database():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     for name, process in zip(('foo', 'bar'), processes):
     for name, process in zip(('foo', 'bar'), processes):
@@ -355,7 +355,7 @@ def test_dump_data_sources_with_dry_run_skips_pg_dump():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
     flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
     flexmock(module).should_receive('execute_command').never()
     flexmock(module).should_receive('execute_command').never()
 
 
@@ -384,7 +384,7 @@ def test_dump_data_sources_runs_pg_dump_with_hostname_and_port():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -432,7 +432,7 @@ def test_dump_data_sources_runs_pg_dump_with_username_and_password():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -478,7 +478,7 @@ def test_dump_data_sources_with_username_injection_attack_gets_escaped():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -521,7 +521,7 @@ def test_dump_data_sources_runs_pg_dump_with_directory_format():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_parent_directory_for_dump')
     flexmock(module.dump).should_receive('create_parent_directory_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
     flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
 
 
@@ -566,7 +566,7 @@ def test_dump_data_sources_runs_pg_dump_with_options():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -609,7 +609,7 @@ def test_dump_data_sources_runs_pg_dumpall_for_all_databases():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -641,7 +641,7 @@ def test_dump_data_sources_runs_non_default_pg_dump():
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.os.path).should_receive('exists').and_return(False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
     flexmock(module.dump).should_receive('create_named_pipe_for_dump')
 
 
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -679,7 +679,7 @@ def test_restore_data_source_dump_runs_pg_restore():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
@@ -736,7 +736,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_hostname_and_port():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
@@ -801,7 +801,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_username_and_password():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return(
     flexmock(module).should_receive('make_extra_environment').and_return(
         {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
         {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
     )
     )
@@ -875,7 +875,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return(
     flexmock(module).should_receive('make_extra_environment').and_return(
         {'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'}
         {'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'}
     )
     )
@@ -957,7 +957,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return(
     flexmock(module).should_receive('make_extra_environment').and_return(
         {'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'}
         {'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'}
     )
     )
@@ -1033,7 +1033,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_options():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
@@ -1090,7 +1090,7 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
@@ -1132,7 +1132,7 @@ def test_restore_data_source_dump_runs_psql_for_plain_database_dump():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
@@ -1186,7 +1186,7 @@ def test_restore_data_source_dump_runs_non_default_pg_restore_and_psql():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
@@ -1250,7 +1250,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
@@ -1277,7 +1277,7 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
@@ -1332,7 +1332,7 @@ def test_restore_data_source_dump_with_schemas_restores_schemas():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')

+ 14 - 14
tests/unit/hooks/monitoring/test_ntfy.py

@@ -38,7 +38,7 @@ def test_ping_monitor_minimal_config_hits_hosted_ntfy_on_fail():
     hook_config = {'topic': topic}
     hook_config = {'topic': topic}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}',
         f'{default_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
@@ -62,7 +62,7 @@ def test_ping_monitor_with_access_token_hits_hosted_ntfy_on_fail():
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}',
         f'{default_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
@@ -88,7 +88,7 @@ def test_ping_monitor_with_username_password_and_access_token_ignores_username_p
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}',
         f'{default_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
@@ -114,7 +114,7 @@ def test_ping_monitor_with_username_password_hits_hosted_ntfy_on_fail():
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}',
         f'{default_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
@@ -135,7 +135,7 @@ def test_ping_monitor_with_password_but_no_username_warns():
     hook_config = {'topic': topic, 'password': 'fakepassword'}
     hook_config = {'topic': topic, 'password': 'fakepassword'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}',
         f'{default_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
@@ -157,7 +157,7 @@ def test_ping_monitor_with_username_but_no_password_warns():
     hook_config = {'topic': topic, 'username': 'testuser'}
     hook_config = {'topic': topic, 'username': 'testuser'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}',
         f'{default_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
@@ -179,7 +179,7 @@ def test_ping_monitor_minimal_config_does_not_hit_hosted_ntfy_on_start():
     hook_config = {'topic': topic}
     hook_config = {'topic': topic}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
     module.ping_monitor(
     module.ping_monitor(
@@ -196,7 +196,7 @@ def test_ping_monitor_minimal_config_does_not_hit_hosted_ntfy_on_finish():
     hook_config = {'topic': topic}
     hook_config = {'topic': topic}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
     module.ping_monitor(
     module.ping_monitor(
@@ -213,7 +213,7 @@ def test_ping_monitor_minimal_config_hits_selfhosted_ntfy_on_fail():
     hook_config = {'topic': topic, 'server': custom_base_url}
     hook_config = {'topic': topic, 'server': custom_base_url}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{custom_base_url}/{topic}',
         f'{custom_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
@@ -234,7 +234,7 @@ def test_ping_monitor_minimal_config_does_not_hit_hosted_ntfy_on_fail_dry_run():
     hook_config = {'topic': topic}
     hook_config = {'topic': topic}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
     module.ping_monitor(
     module.ping_monitor(
@@ -251,7 +251,7 @@ def test_ping_monitor_custom_message_hits_hosted_ntfy_on_fail():
     hook_config = {'topic': topic, 'fail': custom_message_config}
     hook_config = {'topic': topic, 'fail': custom_message_config}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}', headers=custom_message_headers, auth=None
         f'{default_base_url}/{topic}', headers=custom_message_headers, auth=None
     ).and_return(flexmock(ok=True)).once()
     ).and_return(flexmock(ok=True)).once()
@@ -270,7 +270,7 @@ def test_ping_monitor_custom_state_hits_hosted_ntfy_on_start():
     hook_config = {'topic': topic, 'states': ['start', 'fail']}
     hook_config = {'topic': topic, 'states': ['start', 'fail']}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}',
         f'{default_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.START),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.START),
@@ -291,7 +291,7 @@ def test_ping_monitor_with_connection_error_logs_warning():
     hook_config = {'topic': topic}
     hook_config = {'topic': topic}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{default_base_url}/{topic}',
         f'{default_base_url}/{topic}',
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
         headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL),
@@ -331,7 +331,7 @@ def test_ping_monitor_with_other_error_logs_warning():
     hook_config = {'topic': topic}
     hook_config = {'topic': topic}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     response = flexmock(ok=False)
     response = flexmock(ok=False)
     response.should_receive('raise_for_status').and_raise(
     response.should_receive('raise_for_status').and_raise(
         module.requests.exceptions.RequestException
         module.requests.exceptions.RequestException

+ 6 - 6
tests/unit/hooks/monitoring/test_pagerduty.py

@@ -6,7 +6,7 @@ from borgmatic.hooks.monitoring import pagerduty as module
 def test_ping_monitor_ignores_start_state():
 def test_ping_monitor_ignores_start_state():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
     module.ping_monitor(
     module.ping_monitor(
@@ -22,7 +22,7 @@ def test_ping_monitor_ignores_start_state():
 def test_ping_monitor_ignores_finish_state():
 def test_ping_monitor_ignores_finish_state():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
     module.ping_monitor(
     module.ping_monitor(
@@ -38,7 +38,7 @@ def test_ping_monitor_ignores_finish_state():
 def test_ping_monitor_calls_api_for_fail_state():
 def test_ping_monitor_calls_api_for_fail_state():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').and_return(flexmock(ok=True))
     flexmock(module.requests).should_receive('post').and_return(flexmock(ok=True))
 
 
     module.ping_monitor(
     module.ping_monitor(
@@ -54,7 +54,7 @@ def test_ping_monitor_calls_api_for_fail_state():
 def test_ping_monitor_dry_run_does_not_call_api():
 def test_ping_monitor_dry_run_does_not_call_api():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
     module.ping_monitor(
     module.ping_monitor(
@@ -70,7 +70,7 @@ def test_ping_monitor_dry_run_does_not_call_api():
 def test_ping_monitor_with_connection_error_logs_warning():
 def test_ping_monitor_with_connection_error_logs_warning():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').and_raise(
     flexmock(module.requests).should_receive('post').and_raise(
         module.requests.exceptions.ConnectionError
         module.requests.exceptions.ConnectionError
     )
     )
@@ -107,7 +107,7 @@ def test_ping_monitor_with_other_error_logs_warning():
     response = flexmock(ok=False)
     response = flexmock(ok=False)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     response.should_receive('raise_for_status').and_raise(
     response.should_receive('raise_for_status').and_raise(
         module.requests.exceptions.RequestException
         module.requests.exceptions.RequestException
     )
     )

+ 14 - 14
tests/unit/hooks/monitoring/test_pushover.py

@@ -13,7 +13,7 @@ def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_
     hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
     hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -43,7 +43,7 @@ def test_ping_monitor_config_with_minimum_config_start_state_backup_not_send_to_
     hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
     hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -71,7 +71,7 @@ def test_ping_monitor_start_state_backup_default_message_successfully_send_to_pu
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -107,7 +107,7 @@ def test_ping_monitor_start_state_backup_custom_message_successfully_send_to_pus
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -142,7 +142,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -180,7 +180,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -218,7 +218,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -259,7 +259,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_high_decl
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -314,7 +314,7 @@ def test_ping_monitor_start_state_backup_based_on_documentation_advanced_example
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -380,7 +380,7 @@ def test_ping_monitor_fail_state_backup_based_on_documentation_advanced_example_
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -451,7 +451,7 @@ def test_ping_monitor_finish_state_backup_based_on_documentation_advanced_exampl
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         'https://api.pushover.net/1/messages.json',
         'https://api.pushover.net/1/messages.json',
@@ -487,7 +487,7 @@ def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_
     hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
     hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').and_return(flexmock(ok=True)).never()
     flexmock(module.requests).should_receive('post').and_return(flexmock(ok=True)).never()
 
 
@@ -511,7 +511,7 @@ def test_ping_monitor_config_incorrect_state_exit_early():
     }
     }
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.logger).should_receive('warning').never()
     flexmock(module.requests).should_receive('post').and_return(flexmock(ok=True)).never()
     flexmock(module.requests).should_receive('post').and_return(flexmock(ok=True)).never()
 
 
@@ -537,7 +537,7 @@ def test_ping_monitor_push_post_error_bails():
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     push_response = flexmock(ok=False)
     push_response = flexmock(ok=False)
     push_response.should_receive('raise_for_status').and_raise(
     push_response.should_receive('raise_for_status').and_raise(
         module.requests.ConnectionError
         module.requests.ConnectionError

+ 18 - 18
tests/unit/hooks/monitoring/test_zabbix.py

@@ -77,7 +77,7 @@ def test_ping_monitor_config_with_api_key_only_bails():
     hook_config = {'api_key': API_KEY}
     hook_config = {'api_key': API_KEY}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -97,7 +97,7 @@ def test_ping_monitor_config_with_host_only_bails():
     hook_config = {'host': HOST}
     hook_config = {'host': HOST}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -117,7 +117,7 @@ def test_ping_monitor_config_with_key_only_bails():
     hook_config = {'key': KEY}
     hook_config = {'key': KEY}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -137,7 +137,7 @@ def test_ping_monitor_config_with_server_only_bails():
     hook_config = {'server': SERVER}
     hook_config = {'server': SERVER}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -156,7 +156,7 @@ def test_ping_monitor_config_user_password_no_zabbix_data_bails():
     hook_config = {'server': SERVER, 'username': USERNAME, 'password': PASSWORD}
     hook_config = {'server': SERVER, 'username': USERNAME, 'password': PASSWORD}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -175,7 +175,7 @@ def test_ping_monitor_config_api_key_no_zabbix_data_bails():
     hook_config = {'server': SERVER, 'api_key': API_KEY}
     hook_config = {'server': SERVER, 'api_key': API_KEY}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -195,7 +195,7 @@ def test_ping_monitor_config_itemid_no_auth_data_bails():
     hook_config = {'server': SERVER, 'itemid': ITEMID}
     hook_config = {'server': SERVER, 'itemid': ITEMID}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -215,7 +215,7 @@ def test_ping_monitor_config_host_and_key_no_auth_data_bails():
     hook_config = {'server': SERVER, 'host': HOST, 'key': KEY}
     hook_config = {'server': SERVER, 'host': HOST, 'key': KEY}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -235,7 +235,7 @@ def test_ping_monitor_config_host_and_key_with_api_key_auth_data_successful():
     hook_config = {'server': SERVER, 'host': HOST, 'key': KEY, 'api_key': API_KEY}
     hook_config = {'server': SERVER, 'host': HOST, 'key': KEY, 'api_key': API_KEY}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{SERVER}',
         f'{SERVER}',
         headers=AUTH_HEADERS_API_KEY,
         headers=AUTH_HEADERS_API_KEY,
@@ -257,7 +257,7 @@ def test_ping_monitor_config_host_and_missing_key_bails():
     hook_config = {'server': SERVER, 'host': HOST, 'api_key': API_KEY}
     hook_config = {'server': SERVER, 'host': HOST, 'api_key': API_KEY}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -275,7 +275,7 @@ def test_ping_monitor_config_key_and_missing_host_bails():
     hook_config = {'server': SERVER, 'key': KEY, 'api_key': API_KEY}
     hook_config = {'server': SERVER, 'key': KEY, 'api_key': API_KEY}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -302,7 +302,7 @@ def test_ping_monitor_config_host_and_key_with_username_password_auth_data_succe
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     auth_response = flexmock(ok=True)
     auth_response = flexmock(ok=True)
     auth_response.should_receive('json').and_return(
     auth_response.should_receive('json').and_return(
         {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
         {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
@@ -343,7 +343,7 @@ def test_ping_monitor_config_host_and_key_with_username_password_auth_data_and_a
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     auth_response = flexmock(ok=False)
     auth_response = flexmock(ok=False)
     auth_response.should_receive('json').and_return(
     auth_response.should_receive('json').and_return(
         {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
         {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
@@ -384,7 +384,7 @@ def test_ping_monitor_config_host_and_key_with_username_and_missing_password_bai
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -408,7 +408,7 @@ def test_ping_monitor_config_host_and_key_with_password_and_missing_username_bai
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.logger).should_receive('warning').once()
     flexmock(module.requests).should_receive('post').never()
     flexmock(module.requests).should_receive('post').never()
 
 
@@ -428,7 +428,7 @@ def test_ping_monitor_config_itemid_with_api_key_auth_data_successful():
     hook_config = {'server': SERVER, 'itemid': ITEMID, 'api_key': API_KEY}
     hook_config = {'server': SERVER, 'itemid': ITEMID, 'api_key': API_KEY}
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     flexmock(module.requests).should_receive('post').with_args(
     flexmock(module.requests).should_receive('post').with_args(
         f'{SERVER}',
         f'{SERVER}',
         headers=AUTH_HEADERS_API_KEY,
         headers=AUTH_HEADERS_API_KEY,
@@ -453,7 +453,7 @@ def test_ping_monitor_config_itemid_with_username_password_auth_data_successful(
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     auth_response = flexmock(ok=True)
     auth_response = flexmock(ok=True)
     auth_response.should_receive('json').and_return(
     auth_response.should_receive('json').and_return(
         {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
         {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
@@ -488,7 +488,7 @@ def test_ping_monitor_config_itemid_with_username_password_auth_data_and_push_po
 
 
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
         'resolve_credential'
-    ).replace_with(lambda value: value)
+    ).replace_with(lambda value, config: value)
     auth_response = flexmock(ok=True)
     auth_response = flexmock(ok=True)
     auth_response.should_receive('json').and_return(
     auth_response.should_receive('json').and_return(
         {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
         {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}