diff --git a/NEWS b/NEWS index 212ce5e5..c9a55a51 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,6 @@ 1.9.4.dev0 * #926: Fix library error when running within a PyInstaller bundle. + * Reorganize data source and monitoring hooks to make developing new hooks easier. 1.9.3 * #261 (beta): Add a ZFS hook for snapshotting and backing up ZFS datasets. See the documentation diff --git a/borgmatic/actions/check.py b/borgmatic/actions/check.py index 19b10d9a..3a14affe 100644 --- a/borgmatic/actions/check.py +++ b/borgmatic/actions/check.py @@ -364,7 +364,7 @@ def collect_spot_check_source_paths( 'use_streaming', config, repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE, ).values() ) diff --git a/borgmatic/actions/create.py b/borgmatic/actions/create.py index 11e2efa7..ef455cc4 100644 --- a/borgmatic/actions/create.py +++ b/borgmatic/actions/create.py @@ -10,7 +10,6 @@ import borgmatic.config.paths import borgmatic.config.validate import borgmatic.hooks.command import borgmatic.hooks.dispatch -import borgmatic.hooks.dump logger = logging.getLogger(__name__) @@ -186,7 +185,7 @@ def run_create( 'remove_data_source_dumps', config, repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE, borgmatic_runtime_directory, global_arguments.dry_run, ) @@ -195,7 +194,7 @@ def run_create( 'dump_data_sources', config, repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE, config_paths, borgmatic_runtime_directory, source_directories, @@ -232,7 +231,7 @@ def run_create( 'remove_data_source_dumps', config, config_filename, - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE, borgmatic_runtime_directory, global_arguments.dry_run, ) diff --git a/borgmatic/actions/restore.py b/borgmatic/actions/restore.py index 0010b250..109ac1e7 100644 --- a/borgmatic/actions/restore.py +++ b/borgmatic/actions/restore.py @@ -11,8 +11,8 @@ import borgmatic.borg.mount import borgmatic.borg.repo_list import borgmatic.config.paths import borgmatic.config.validate +import borgmatic.hooks.data_source.dump import borgmatic.hooks.dispatch -import borgmatic.hooks.dump logger = logging.getLogger(__name__) @@ -44,7 +44,8 @@ def get_configured_data_source( hooks_to_search = { hook_name: value for (hook_name, value) in config.items() - if hook_name in borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES + if hook_name.split('_databases')[0] + in borgmatic.hooks.dispatch.get_submodule_names(borgmatic.hooks.data_source) } else: try: @@ -123,10 +124,10 @@ def restore_single_data_source( 'make_data_source_dump_patterns', config, repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE, borgmatic_runtime_directory, data_source['name'], - )[hook_name] + )[hook_name.split('_databases')[0]] destination_path = ( tempfile.mkdtemp(dir=borgmatic_runtime_directory) @@ -141,7 +142,11 @@ def restore_single_data_source( dry_run=global_arguments.dry_run, repository=repository['path'], archive=archive_name, - paths=[borgmatic.hooks.dump.convert_glob_patterns_to_borg_pattern(dump_patterns)], + paths=[ + borgmatic.hooks.data_source.dump.convert_glob_patterns_to_borg_pattern( + dump_patterns + ) + ], config=config, local_borg_version=local_borg_version, global_arguments=global_arguments, @@ -162,11 +167,11 @@ def restore_single_data_source( shutil.rmtree(destination_path, ignore_errors=True) # Run a single data source restore, consuming the extract stdout (if any). - borgmatic.hooks.dispatch.call_hooks( + borgmatic.hooks.dispatch.call_hook( function_name='restore_data_source_dump', config=config, log_prefix=repository['path'], - hook_names=[hook_name], + hook_name=hook_name, data_source=data_source, dry_run=global_arguments.dry_run, extract_process=extract_process, @@ -206,7 +211,9 @@ def collect_archive_data_source_names( global_arguments, list_paths=[ 'sh:' - + borgmatic.hooks.dump.make_data_source_dump_path(base_directory, '*_databases/*/*') + + borgmatic.hooks.data_source.dump.make_data_source_dump_path( + base_directory, '*_databases/*/*' + ) for base_directory in ( 'borgmatic', borgmatic.config.paths.make_runtime_directory_glob(borgmatic_runtime_directory), @@ -354,7 +361,7 @@ def run_restore( 'remove_data_source_dumps', config, repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE, borgmatic_runtime_directory, global_arguments.dry_run, ) @@ -451,7 +458,7 @@ def run_restore( 'remove_data_source_dumps', config, repository['path'], - borgmatic.hooks.dump.DATA_SOURCE_HOOK_NAMES, + borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE, borgmatic_runtime_directory, global_arguments.dry_run, ) diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 95715223..746c06a3 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -39,7 +39,8 @@ from borgmatic.borg import umount as borg_umount from borgmatic.borg import version as borg_version from borgmatic.commands.arguments import parse_arguments from borgmatic.config import checks, collect, validate -from borgmatic.hooks import command, dispatch, monitor +from borgmatic.hooks import command, dispatch +from borgmatic.hooks.monitoring import monitor from borgmatic.logger import DISABLED, add_custom_log_levels, configure_logging, should_do_markup from borgmatic.signals import configure_signals from borgmatic.verbosity import verbosity_to_log_level @@ -103,7 +104,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'initialize_monitor', config, config_filename, - monitor.MONITOR_HOOK_NAMES, + dispatch.Hook_type.MONITORING, monitoring_log_level, global_arguments.dry_run, ) @@ -112,7 +113,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'ping_monitor', config, config_filename, - monitor.MONITOR_HOOK_NAMES, + dispatch.Hook_type.MONITORING, monitor.State.START, monitoring_log_level, global_arguments.dry_run, @@ -188,7 +189,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'ping_monitor', config, config_filename, - monitor.MONITOR_HOOK_NAMES, + dispatch.Hook_type.MONITORING, monitor.State.LOG, monitoring_log_level, global_arguments.dry_run, @@ -205,7 +206,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'ping_monitor', config, config_filename, - monitor.MONITOR_HOOK_NAMES, + dispatch.Hook_type.MONITORING, monitor.State.FINISH, monitoring_log_level, global_arguments.dry_run, @@ -214,7 +215,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'destroy_monitor', config, config_filename, - monitor.MONITOR_HOOK_NAMES, + dispatch.Hook_type.MONITORING, monitoring_log_level, global_arguments.dry_run, ) @@ -241,7 +242,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'ping_monitor', config, config_filename, - monitor.MONITOR_HOOK_NAMES, + dispatch.Hook_type.MONITORING, monitor.State.FAIL, monitoring_log_level, global_arguments.dry_run, @@ -250,7 +251,7 @@ def run_configuration(config_filename, config, config_paths, arguments): 'destroy_monitor', config, config_filename, - monitor.MONITOR_HOOK_NAMES, + dispatch.Hook_type.MONITORING, monitoring_log_level, global_arguments.dry_run, ) diff --git a/borgmatic/hooks/data_source/__init__.py b/borgmatic/hooks/data_source/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/borgmatic/hooks/bootstrap.py b/borgmatic/hooks/data_source/bootstrap.py similarity index 100% rename from borgmatic/hooks/bootstrap.py rename to borgmatic/hooks/data_source/bootstrap.py diff --git a/borgmatic/hooks/dump.py b/borgmatic/hooks/data_source/dump.py similarity index 93% rename from borgmatic/hooks/dump.py rename to borgmatic/hooks/data_source/dump.py index 228c12bb..1fe0c7b1 100644 --- a/borgmatic/hooks/dump.py +++ b/borgmatic/hooks/data_source/dump.py @@ -5,15 +5,7 @@ import shutil logger = logging.getLogger(__name__) -DATA_SOURCE_HOOK_NAMES = ( - 'bootstrap', - 'mariadb_databases', - 'mysql_databases', - 'mongodb_databases', - 'postgresql_databases', - 'sqlite_databases', - 'zfs', -) +IS_A_HOOK = False def make_data_source_dump_path(borgmatic_runtime_directory, data_source_hook_name): diff --git a/borgmatic/hooks/mariadb.py b/borgmatic/hooks/data_source/mariadb.py similarity index 99% rename from borgmatic/hooks/mariadb.py rename to borgmatic/hooks/data_source/mariadb.py index f63d1515..d9235d03 100644 --- a/borgmatic/hooks/mariadb.py +++ b/borgmatic/hooks/data_source/mariadb.py @@ -9,7 +9,7 @@ from borgmatic.execute import ( execute_command_and_capture_output, execute_command_with_processes, ) -from borgmatic.hooks import dump +from borgmatic.hooks.data_source import dump logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/mongodb.py b/borgmatic/hooks/data_source/mongodb.py similarity index 99% rename from borgmatic/hooks/mongodb.py rename to borgmatic/hooks/data_source/mongodb.py index 2bb5c257..e8b87988 100644 --- a/borgmatic/hooks/mongodb.py +++ b/borgmatic/hooks/data_source/mongodb.py @@ -4,7 +4,7 @@ import shlex import borgmatic.config.paths from borgmatic.execute import execute_command, execute_command_with_processes -from borgmatic.hooks import dump +from borgmatic.hooks.data_source import dump logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/mysql.py b/borgmatic/hooks/data_source/mysql.py similarity index 99% rename from borgmatic/hooks/mysql.py rename to borgmatic/hooks/data_source/mysql.py index a085fc0b..8ec6ad7a 100644 --- a/borgmatic/hooks/mysql.py +++ b/borgmatic/hooks/data_source/mysql.py @@ -9,7 +9,7 @@ from borgmatic.execute import ( execute_command_and_capture_output, execute_command_with_processes, ) -from borgmatic.hooks import dump +from borgmatic.hooks.data_source import dump logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/postgresql.py b/borgmatic/hooks/data_source/postgresql.py similarity index 99% rename from borgmatic/hooks/postgresql.py rename to borgmatic/hooks/data_source/postgresql.py index 337c0f88..263bf2e4 100644 --- a/borgmatic/hooks/postgresql.py +++ b/borgmatic/hooks/data_source/postgresql.py @@ -11,7 +11,7 @@ from borgmatic.execute import ( execute_command_and_capture_output, execute_command_with_processes, ) -from borgmatic.hooks import dump +from borgmatic.hooks.data_source import dump logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/sqlite.py b/borgmatic/hooks/data_source/sqlite.py similarity index 99% rename from borgmatic/hooks/sqlite.py rename to borgmatic/hooks/data_source/sqlite.py index f14cb94b..78e1c201 100644 --- a/borgmatic/hooks/sqlite.py +++ b/borgmatic/hooks/data_source/sqlite.py @@ -4,7 +4,7 @@ import shlex import borgmatic.config.paths from borgmatic.execute import execute_command, execute_command_with_processes -from borgmatic.hooks import dump +from borgmatic.hooks.data_source import dump logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/zfs.py b/borgmatic/hooks/data_source/zfs.py similarity index 100% rename from borgmatic/hooks/zfs.py rename to borgmatic/hooks/data_source/zfs.py diff --git a/borgmatic/hooks/dispatch.py b/borgmatic/hooks/dispatch.py index dd2e74cf..b716846c 100644 --- a/borgmatic/hooks/dispatch.py +++ b/borgmatic/hooks/dispatch.py @@ -1,75 +1,70 @@ +import enum +import importlib import logging +import pkgutil -from borgmatic.hooks import ( - apprise, - bootstrap, - cronhub, - cronitor, - healthchecks, - loki, - mariadb, - mongodb, - mysql, - ntfy, - pagerduty, - postgresql, - pushover, - sqlite, - uptimekuma, - zabbix, - zfs, -) +import borgmatic.hooks.data_source +import borgmatic.hooks.monitoring logger = logging.getLogger(__name__) -HOOK_NAME_TO_MODULE = { - 'apprise': apprise, - 'bootstrap': bootstrap, - 'cronhub': cronhub, - 'cronitor': cronitor, - 'healthchecks': healthchecks, - 'loki': loki, - 'mariadb_databases': mariadb, - 'mongodb_databases': mongodb, - 'mysql_databases': mysql, - 'ntfy': ntfy, - 'pagerduty': pagerduty, - 'postgresql_databases': postgresql, - 'pushover': pushover, - 'sqlite_databases': sqlite, - 'uptime_kuma': uptimekuma, - 'zabbix': zabbix, - 'zfs': zfs, -} + +class Hook_type(enum.Enum): + DATA_SOURCE = 'data_source' + MONITORING = 'monitoring' + + +def get_submodule_names(parent_module): # pragma: no cover + ''' + Given a parent module, return the names of its direct submodules as a tuple of strings. + ''' + return tuple(module_info.name for module_info in pkgutil.iter_modules(parent_module.__path__)) def call_hook(function_name, config, log_prefix, hook_name, *args, **kwargs): ''' Given a configuration dict and a prefix to use in log entries, call the requested function of the Python module corresponding to the given hook name. Supply that call with the configuration - for this hook (if any), the log prefix, and any given args and kwargs. Return any return value. + for this hook (if any), the log prefix, and any given args and kwargs. Return the return value + of that call or None if the module in question is not a hook. Raise ValueError if the hook name is unknown. Raise AttributeError if the function name is not found in the module. Raise anything else that the called function raises. ''' - hook_config = config.get(hook_name) or {} + hook_config = config.get(hook_name) or config.get(f'{hook_name}_databases') or {} + module_name = hook_name.split('_databases')[0] - try: - module = HOOK_NAME_TO_MODULE[hook_name] - except KeyError: + # Probe for a data source or monitoring hook module corresponding to the hook name. + for parent_module in (borgmatic.hooks.data_source, borgmatic.hooks.monitoring): + if module_name not in get_submodule_names(parent_module): + continue + + module = importlib.import_module(f'{parent_module.__name__}.{module_name}') + + # If this module is explicitly flagged as not a hook, bail. + if not getattr(module, 'IS_A_HOOK', True): + return None + + break + else: raise ValueError(f'Unknown hook name: {hook_name}') logger.debug(f'{log_prefix}: Calling {hook_name} hook function {function_name}') + return getattr(module, function_name)(hook_config, config, log_prefix, *args, **kwargs) -def call_hooks(function_name, config, log_prefix, hook_names, *args, **kwargs): +def call_hooks(function_name, config, log_prefix, hook_type, *args, **kwargs): ''' Given a configuration dict and a prefix to use in log entries, call the requested function of - the Python module corresponding to each given hook name. Supply each call with the configuration - for that hook, the log prefix, and any given args and kwargs. Collect any return values into a - dict from hook name to return value. + the Python module corresponding to each hook of the given hook type (either "data_source" or + "monitoring"). Supply each call with the configuration for that hook, the log prefix, and any + given args and kwargs. + + Collect any return values into a dict from module name to return value. Note that the module + name is the name of the hook module itself, which might be different from the hook configuration + option (e.g. "postgresql" for the former vs. "postgresql_databases" for the latter). If the hook name is not present in the hooks configuration, then don't call the function for it and omit it from the return values. @@ -80,22 +75,26 @@ def call_hooks(function_name, config, log_prefix, hook_names, *args, **kwargs): ''' return { hook_name: call_hook(function_name, config, log_prefix, hook_name, *args, **kwargs) - for hook_name in hook_names - if hook_name in config + for hook_name in get_submodule_names( + importlib.import_module(f'borgmatic.hooks.{hook_type.value}') + ) + if hook_name in config or f'{hook_name}_databases' in config } -def call_hooks_even_if_unconfigured(function_name, config, log_prefix, hook_names, *args, **kwargs): +def call_hooks_even_if_unconfigured(function_name, config, log_prefix, hook_type, *args, **kwargs): ''' Given a configuration dict and a prefix to use in log entries, call the requested function of - the Python module corresponding to each given hook name. Supply each call with the configuration - for that hook, the log prefix, and any given args and kwargs. Collect any return values into a - dict from hook name to return value. + the Python module corresponding to each hook of the given hook type (either "data_source" or + "monitoring"). Supply each call with the configuration for that hook, the log prefix, and any + given args and kwargs. Collect any return values into a dict from hook name to return value. Raise AttributeError if the function name is not found in the module. Raise anything else that a called function raises. An error stops calls to subsequent functions. ''' return { hook_name: call_hook(function_name, config, log_prefix, hook_name, *args, **kwargs) - for hook_name in hook_names + for hook_name in get_submodule_names( + importlib.import_module(f'borgmatic.hooks.{hook_type.value}') + ) } diff --git a/borgmatic/hooks/monitor.py b/borgmatic/hooks/monitor.py deleted file mode 100644 index db44b567..00000000 --- a/borgmatic/hooks/monitor.py +++ /dev/null @@ -1,21 +0,0 @@ -from enum import Enum - -MONITOR_HOOK_NAMES = ( - 'apprise', - 'cronhub', - 'cronitor', - 'healthchecks', - 'loki', - 'ntfy', - 'pagerduty', - 'pushover', - 'uptime_kuma', - 'zabbix', -) - - -class State(Enum): - START = 1 - FINISH = 2 - FAIL = 3 - LOG = 4 diff --git a/borgmatic/hooks/monitoring/__init__.py b/borgmatic/hooks/monitoring/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/borgmatic/hooks/apprise.py b/borgmatic/hooks/monitoring/apprise.py similarity index 84% rename from borgmatic/hooks/apprise.py rename to borgmatic/hooks/monitoring/apprise.py index d1a55fc4..9592d787 100644 --- a/borgmatic/hooks/apprise.py +++ b/borgmatic/hooks/monitoring/apprise.py @@ -1,8 +1,8 @@ import logging import operator -import borgmatic.hooks.logs -import borgmatic.hooks.monitor +import borgmatic.hooks.monitoring.logs +import borgmatic.hooks.monitoring.monitor logger = logging.getLogger(__name__) @@ -22,12 +22,12 @@ def initialize_monitor(hook_config, config, config_filename, monitoring_log_leve logs_size_limit = max( hook_config.get('logs_size_limit', DEFAULT_LOGS_SIZE_LIMIT_BYTES) - - len(borgmatic.hooks.logs.PAYLOAD_TRUNCATION_INDICATOR), + - len(borgmatic.hooks.monitoring.logs.PAYLOAD_TRUNCATION_INDICATOR), 0, ) - borgmatic.hooks.logs.add_handler( - borgmatic.hooks.logs.Forgetful_buffering_handler( + borgmatic.hooks.monitoring.logs.add_handler( + borgmatic.hooks.monitoring.logs.Forgetful_buffering_handler( HANDLER_IDENTIFIER, logs_size_limit, monitoring_log_level ) ) @@ -82,11 +82,13 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev body = state_config.get('body') if state in ( - borgmatic.hooks.monitor.State.FINISH, - borgmatic.hooks.monitor.State.FAIL, - borgmatic.hooks.monitor.State.LOG, + borgmatic.hooks.monitoring.monitor.State.FINISH, + borgmatic.hooks.monitoring.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.LOG, ): - formatted_logs = borgmatic.hooks.logs.format_buffered_logs_for_payload(HANDLER_IDENTIFIER) + formatted_logs = borgmatic.hooks.monitoring.logs.format_buffered_logs_for_payload( + HANDLER_IDENTIFIER + ) if formatted_logs: body += f'\n\n{formatted_logs}' @@ -106,4 +108,4 @@ def destroy_monitor(hook_config, config, config_filename, monitoring_log_level, Remove the monitor handler that was added to the root logger. This prevents the handler from getting reused by other instances of this monitor. ''' - borgmatic.hooks.logs.remove_handler(HANDLER_IDENTIFIER) + borgmatic.hooks.monitoring.logs.remove_handler(HANDLER_IDENTIFIER) diff --git a/borgmatic/hooks/cronhub.py b/borgmatic/hooks/monitoring/cronhub.py similarity index 97% rename from borgmatic/hooks/cronhub.py rename to borgmatic/hooks/monitoring/cronhub.py index bbdc19a8..fbb84c8b 100644 --- a/borgmatic/hooks/cronhub.py +++ b/borgmatic/hooks/monitoring/cronhub.py @@ -2,7 +2,7 @@ import logging import requests -from borgmatic.hooks import monitor +from borgmatic.hooks.monitoring import monitor logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/cronitor.py b/borgmatic/hooks/monitoring/cronitor.py similarity index 97% rename from borgmatic/hooks/cronitor.py rename to borgmatic/hooks/monitoring/cronitor.py index fe4cc1d8..87a2072e 100644 --- a/borgmatic/hooks/cronitor.py +++ b/borgmatic/hooks/monitoring/cronitor.py @@ -2,7 +2,7 @@ import logging import requests -from borgmatic.hooks import monitor +from borgmatic.hooks.monitoring import monitor logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/healthchecks.py b/borgmatic/hooks/monitoring/healthchecks.py similarity index 89% rename from borgmatic/hooks/healthchecks.py rename to borgmatic/hooks/monitoring/healthchecks.py index 73d4eec5..8483f98b 100644 --- a/borgmatic/hooks/healthchecks.py +++ b/borgmatic/hooks/monitoring/healthchecks.py @@ -3,8 +3,8 @@ import re import requests -import borgmatic.hooks.logs -from borgmatic.hooks import monitor +import borgmatic.hooks.monitoring.logs +from borgmatic.hooks.monitoring import monitor logger = logging.getLogger(__name__) @@ -30,12 +30,12 @@ def initialize_monitor(hook_config, config, config_filename, monitoring_log_leve ping_body_limit = max( hook_config.get('ping_body_limit', DEFAULT_PING_BODY_LIMIT_BYTES) - - len(borgmatic.hooks.logs.PAYLOAD_TRUNCATION_INDICATOR), + - len(borgmatic.hooks.monitoring.logs.PAYLOAD_TRUNCATION_INDICATOR), 0, ) - borgmatic.hooks.logs.add_handler( - borgmatic.hooks.logs.Forgetful_buffering_handler( + borgmatic.hooks.monitoring.logs.add_handler( + borgmatic.hooks.monitoring.logs.Forgetful_buffering_handler( HANDLER_IDENTIFIER, ping_body_limit, monitoring_log_level ) ) @@ -78,7 +78,9 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev logger.debug(f'{config_filename}: Using Healthchecks ping URL {ping_url}') if state in (monitor.State.FINISH, monitor.State.FAIL, monitor.State.LOG): - payload = borgmatic.hooks.logs.format_buffered_logs_for_payload(HANDLER_IDENTIFIER) + payload = borgmatic.hooks.monitoring.logs.format_buffered_logs_for_payload( + HANDLER_IDENTIFIER + ) else: payload = '' @@ -99,4 +101,4 @@ def destroy_monitor(hook_config, config, config_filename, monitoring_log_level, Remove the monitor handler that was added to the root logger. This prevents the handler from getting reused by other instances of this monitor. ''' - borgmatic.hooks.logs.remove_handler(HANDLER_IDENTIFIER) + borgmatic.hooks.monitoring.logs.remove_handler(HANDLER_IDENTIFIER) diff --git a/borgmatic/hooks/logs.py b/borgmatic/hooks/monitoring/logs.py similarity index 99% rename from borgmatic/hooks/logs.py rename to borgmatic/hooks/monitoring/logs.py index 3cc6e605..de1bef60 100644 --- a/borgmatic/hooks/logs.py +++ b/borgmatic/hooks/monitoring/logs.py @@ -1,5 +1,6 @@ import logging +IS_A_HOOK = False PAYLOAD_TRUNCATION_INDICATOR = '...\n' diff --git a/borgmatic/hooks/loki.py b/borgmatic/hooks/monitoring/loki.py similarity index 98% rename from borgmatic/hooks/loki.py rename to borgmatic/hooks/monitoring/loki.py index 9734ddc5..51e287c7 100644 --- a/borgmatic/hooks/loki.py +++ b/borgmatic/hooks/monitoring/loki.py @@ -6,7 +6,7 @@ import time import requests -from borgmatic.hooks import monitor +from borgmatic.hooks.monitoring import monitor logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/monitoring/monitor.py b/borgmatic/hooks/monitoring/monitor.py new file mode 100644 index 00000000..15c39174 --- /dev/null +++ b/borgmatic/hooks/monitoring/monitor.py @@ -0,0 +1,10 @@ +import enum + +IS_A_HOOK = False + + +class State(enum.Enum): + START = 1 + FINISH = 2 + FAIL = 3 + LOG = 4 diff --git a/borgmatic/hooks/ntfy.py b/borgmatic/hooks/monitoring/ntfy.py similarity index 100% rename from borgmatic/hooks/ntfy.py rename to borgmatic/hooks/monitoring/ntfy.py diff --git a/borgmatic/hooks/pagerduty.py b/borgmatic/hooks/monitoring/pagerduty.py similarity index 98% rename from borgmatic/hooks/pagerduty.py rename to borgmatic/hooks/monitoring/pagerduty.py index e9e34da0..2d5325de 100644 --- a/borgmatic/hooks/pagerduty.py +++ b/borgmatic/hooks/monitoring/pagerduty.py @@ -5,7 +5,7 @@ import platform import requests -from borgmatic.hooks import monitor +from borgmatic.hooks.monitoring import monitor logger = logging.getLogger(__name__) diff --git a/borgmatic/hooks/pushover.py b/borgmatic/hooks/monitoring/pushover.py similarity index 100% rename from borgmatic/hooks/pushover.py rename to borgmatic/hooks/monitoring/pushover.py diff --git a/borgmatic/hooks/uptimekuma.py b/borgmatic/hooks/monitoring/uptime_kuma.py similarity index 100% rename from borgmatic/hooks/uptimekuma.py rename to borgmatic/hooks/monitoring/uptime_kuma.py diff --git a/borgmatic/hooks/zabbix.py b/borgmatic/hooks/monitoring/zabbix.py similarity index 100% rename from borgmatic/hooks/zabbix.py rename to borgmatic/hooks/monitoring/zabbix.py diff --git a/docs/reference/source-code.md b/docs/reference/source-code.md index 9deadcfc..5a68e577 100644 --- a/docs/reference/source-code.md +++ b/docs/reference/source-code.md @@ -16,8 +16,10 @@ you get started. Starting at the top level, we have: * [actions](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/actions): borgmatic-specific logic for running each action (create, list, check, etc.). * [borg](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/borg): Lower-level code that's responsible for interacting with Borg to run each action. * [commands](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/commands): Looking to add a new flag or action? Start here. This contains borgmatic's entry point, argument parsing, and shell completion. - * [config](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/config): Code responsible for loading, normalizing, and validating borgmatic's configuration. - * [hooks](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/hooks): Looking to add a new database or monitoring integration? Start here. + * [config](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/config): Code responsible for loading, normalizing, and validating borgmatic's configuration. Interested in adding a new configuration option? Check out `schema.yaml` here. + * [hooks](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/hooks): Looking to add a new database, filesystem, or monitoring integration? Start here. + * [data_source](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/hooks): Database and filesystem hooks—anything that produces data or files to go into a backup archive. + * [monitoring](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/borgmatic/hooks): Monitoring hooks—integrations with third-party or self-hosted monitoring services. * [docs](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/docs): How-to and reference documentation, including the document you're reading now. * [sample](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/sample): Example configurations for cron and systemd. * [scripts](https://projects.torsion.org/borgmatic-collective/borgmatic/src/branch/main/scripts): Dev-facing scripts for things like building documentation and running end-to-end tests. diff --git a/tests/integration/hooks/__init__.py b/tests/integration/hooks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration/hooks/monitoring/__init__.py b/tests/integration/hooks/monitoring/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration/hooks/test_apprise.py b/tests/integration/hooks/monitoring/test_apprise.py similarity index 78% rename from tests/integration/hooks/test_apprise.py rename to tests/integration/hooks/monitoring/test_apprise.py index 075eacc6..f1bcdf67 100644 --- a/tests/integration/hooks/test_apprise.py +++ b/tests/integration/hooks/monitoring/test_apprise.py @@ -2,14 +2,14 @@ import logging from flexmock import flexmock -from borgmatic.hooks import apprise as module +from borgmatic.hooks.monitoring import apprise as module def test_destroy_monitor_removes_apprise_handler(): logger = logging.getLogger() original_handlers = list(logger.handlers) - module.borgmatic.hooks.logs.add_handler( - module.borgmatic.hooks.logs.Forgetful_buffering_handler( + module.borgmatic.hooks.monitoring.logs.add_handler( + module.borgmatic.hooks.monitoring.logs.Forgetful_buffering_handler( identifier=module.HANDLER_IDENTIFIER, byte_capacity=100, log_level=1 ) ) diff --git a/tests/integration/hooks/test_healthchecks.py b/tests/integration/hooks/monitoring/test_healthchecks.py similarity index 78% rename from tests/integration/hooks/test_healthchecks.py rename to tests/integration/hooks/monitoring/test_healthchecks.py index 5db77d96..9d6ceb4a 100644 --- a/tests/integration/hooks/test_healthchecks.py +++ b/tests/integration/hooks/monitoring/test_healthchecks.py @@ -2,14 +2,14 @@ import logging from flexmock import flexmock -from borgmatic.hooks import healthchecks as module +from borgmatic.hooks.monitoring import healthchecks as module def test_destroy_monitor_removes_healthchecks_handler(): logger = logging.getLogger() original_handlers = list(logger.handlers) - module.borgmatic.hooks.logs.add_handler( - module.borgmatic.hooks.logs.Forgetful_buffering_handler( + module.borgmatic.hooks.monitoring.logs.add_handler( + module.borgmatic.hooks.monitoring.logs.Forgetful_buffering_handler( identifier=module.HANDLER_IDENTIFIER, byte_capacity=100, log_level=1 ) ) diff --git a/tests/integration/hooks/test_loki.py b/tests/integration/hooks/monitoring/test_loki.py similarity index 98% rename from tests/integration/hooks/test_loki.py rename to tests/integration/hooks/monitoring/test_loki.py index 64c262f8..e0f1cbba 100644 --- a/tests/integration/hooks/test_loki.py +++ b/tests/integration/hooks/monitoring/test_loki.py @@ -3,7 +3,7 @@ import platform from flexmock import flexmock -from borgmatic.hooks import loki as module +from borgmatic.hooks.monitoring import loki as module def test_initialize_monitor_replaces_labels(): diff --git a/tests/unit/actions/test_restore.py b/tests/unit/actions/test_restore.py index e12c392a..9753acf0 100644 --- a/tests/unit/actions/test_restore.py +++ b/tests/unit/actions/test_restore.py @@ -90,7 +90,7 @@ def test_restore_single_data_source_extracts_and_restores_single_file_dump(): 'make_data_source_dump_patterns', object, object, object, object, object ).and_return({'postgresql': flexmock()}) flexmock(module.tempfile).should_receive('mkdtemp').never() - flexmock(module.borgmatic.hooks.dump).should_receive( + flexmock(module.borgmatic.hooks.data_source.dump).should_receive( 'convert_glob_patterns_to_borg_pattern' ).and_return(flexmock()) flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return( @@ -98,11 +98,11 @@ def test_restore_single_data_source_extracts_and_restores_single_file_dump(): ).once() flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never() flexmock(module.shutil).should_receive('rmtree').never() - flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( + flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args( function_name='restore_data_source_dump', config=object, log_prefix=object, - hook_names=object, + hook_name=object, data_source=object, dry_run=object, extract_process=object, @@ -132,7 +132,7 @@ def test_restore_single_data_source_extracts_and_restores_directory_dump(): flexmock(module.tempfile).should_receive('mkdtemp').once().and_return( '/run/user/0/borgmatic/tmp1234' ) - flexmock(module.borgmatic.hooks.dump).should_receive( + flexmock(module.borgmatic.hooks.data_source.dump).should_receive( 'convert_glob_patterns_to_borg_pattern' ).and_return(flexmock()) flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return( @@ -140,11 +140,11 @@ def test_restore_single_data_source_extracts_and_restores_directory_dump(): ).once() flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').once() flexmock(module.shutil).should_receive('rmtree').once() - flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( + flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args( function_name='restore_data_source_dump', config=object, log_prefix=object, - hook_names=object, + hook_name=object, data_source=object, dry_run=object, extract_process=object, @@ -174,7 +174,7 @@ def test_restore_single_data_source_with_directory_dump_error_cleans_up_temporar flexmock(module.tempfile).should_receive('mkdtemp').once().and_return( '/run/user/0/borgmatic/tmp1234' ) - flexmock(module.borgmatic.hooks.dump).should_receive( + flexmock(module.borgmatic.hooks.data_source.dump).should_receive( 'convert_glob_patterns_to_borg_pattern' ).and_return(flexmock()) flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_raise( @@ -182,11 +182,11 @@ def test_restore_single_data_source_with_directory_dump_error_cleans_up_temporar ).once() flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never() flexmock(module.shutil).should_receive('rmtree').once() - flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( + flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args( function_name='restore_data_source_dump', config=object, log_prefix=object, - hook_names=object, + hook_name=object, data_source=object, dry_run=object, extract_process=object, @@ -215,7 +215,7 @@ def test_restore_single_data_source_with_directory_dump_and_dry_run_skips_direct 'make_data_source_dump_patterns', object, object, object, object, object ).and_return({'postgresql': flexmock()}) flexmock(module.tempfile).should_receive('mkdtemp').once().and_return('/run/borgmatic/tmp1234') - flexmock(module.borgmatic.hooks.dump).should_receive( + flexmock(module.borgmatic.hooks.data_source.dump).should_receive( 'convert_glob_patterns_to_borg_pattern' ).and_return(flexmock()) flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return( @@ -223,11 +223,11 @@ def test_restore_single_data_source_with_directory_dump_and_dry_run_skips_direct ).once() flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never() flexmock(module.shutil).should_receive('rmtree').never() - flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args( + flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args( function_name='restore_data_source_dump', config=object, log_prefix=object, - hook_names=object, + hook_name=object, data_source=object, dry_run=object, extract_process=object, @@ -254,9 +254,9 @@ def test_collect_archive_data_source_names_parses_archive_paths(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.hooks.dump).should_receive('make_data_source_dump_path').and_return( - '' - ) + flexmock(module.borgmatic.hooks.data_source.dump).should_receive( + 'make_data_source_dump_path' + ).and_return('') flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return( [ 'borgmatic/postgresql_databases/localhost/foo', @@ -286,9 +286,9 @@ def test_collect_archive_data_source_names_parses_archive_paths_with_different_b flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.hooks.dump).should_receive('make_data_source_dump_path').and_return( - '' - ) + flexmock(module.borgmatic.hooks.data_source.dump).should_receive( + 'make_data_source_dump_path' + ).and_return('') flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return( [ 'borgmatic/postgresql_databases/localhost/foo', @@ -319,9 +319,9 @@ def test_collect_archive_data_source_names_parses_directory_format_archive_paths flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.hooks.dump).should_receive('make_data_source_dump_path').and_return( - '' - ) + flexmock(module.borgmatic.hooks.data_source.dump).should_receive( + 'make_data_source_dump_path' + ).and_return('') flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return( [ 'borgmatic/postgresql_databases/localhost/foo/table1', @@ -349,9 +349,9 @@ def test_collect_archive_data_source_names_skips_bad_archive_paths(): flexmock(module.borgmatic.config.paths).should_receive( 'get_borgmatic_source_directory' ).and_return('/root/.borgmatic') - flexmock(module.borgmatic.hooks.dump).should_receive('make_data_source_dump_path').and_return( - '' - ) + flexmock(module.borgmatic.hooks.data_source.dump).should_receive( + 'make_data_source_dump_path' + ).and_return('') flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return( [ 'borgmatic/postgresql_databases/localhost/foo', diff --git a/tests/unit/hooks/data_source/__init__.py b/tests/unit/hooks/data_source/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/hooks/test_bootstrap.py b/tests/unit/hooks/data_source/test_bootstrap.py similarity index 98% rename from tests/unit/hooks/test_bootstrap.py rename to tests/unit/hooks/data_source/test_bootstrap.py index 3db97381..62730883 100644 --- a/tests/unit/hooks/test_bootstrap.py +++ b/tests/unit/hooks/data_source/test_bootstrap.py @@ -2,7 +2,7 @@ import sys from flexmock import flexmock -from borgmatic.hooks import bootstrap as module +from borgmatic.hooks.data_source import bootstrap as module def test_dump_data_sources_creates_manifest_file(): diff --git a/tests/unit/hooks/test_dump.py b/tests/unit/hooks/data_source/test_dump.py similarity index 97% rename from tests/unit/hooks/test_dump.py rename to tests/unit/hooks/data_source/test_dump.py index 2db96e6d..01e00e17 100644 --- a/tests/unit/hooks/test_dump.py +++ b/tests/unit/hooks/data_source/test_dump.py @@ -1,7 +1,7 @@ import pytest from flexmock import flexmock -from borgmatic.hooks import dump as module +from borgmatic.hooks.data_source import dump as module def test_make_data_source_dump_path_joins_arguments(): diff --git a/tests/unit/hooks/test_mariadb.py b/tests/unit/hooks/data_source/test_mariadb.py similarity index 99% rename from tests/unit/hooks/test_mariadb.py rename to tests/unit/hooks/data_source/test_mariadb.py index 3d6879fa..dc63b216 100644 --- a/tests/unit/hooks/test_mariadb.py +++ b/tests/unit/hooks/data_source/test_mariadb.py @@ -3,7 +3,7 @@ import logging import pytest from flexmock import flexmock -from borgmatic.hooks import mariadb as module +from borgmatic.hooks.data_source import mariadb as module def test_database_names_to_dump_passes_through_name(): diff --git a/tests/unit/hooks/test_mongodb.py b/tests/unit/hooks/data_source/test_mongodb.py similarity index 99% rename from tests/unit/hooks/test_mongodb.py rename to tests/unit/hooks/data_source/test_mongodb.py index 0a1cc0e0..4ebe0ba5 100644 --- a/tests/unit/hooks/test_mongodb.py +++ b/tests/unit/hooks/data_source/test_mongodb.py @@ -2,7 +2,7 @@ import logging from flexmock import flexmock -from borgmatic.hooks import mongodb as module +from borgmatic.hooks.data_source import mongodb as module def test_use_streaming_true_for_any_non_directory_format_databases(): diff --git a/tests/unit/hooks/test_mysql.py b/tests/unit/hooks/data_source/test_mysql.py similarity index 99% rename from tests/unit/hooks/test_mysql.py rename to tests/unit/hooks/data_source/test_mysql.py index 6efdc6f3..ff5b53cc 100644 --- a/tests/unit/hooks/test_mysql.py +++ b/tests/unit/hooks/data_source/test_mysql.py @@ -3,7 +3,7 @@ import logging import pytest from flexmock import flexmock -from borgmatic.hooks import mysql as module +from borgmatic.hooks.data_source import mysql as module def test_database_names_to_dump_passes_through_name(): diff --git a/tests/unit/hooks/test_postgresql.py b/tests/unit/hooks/data_source/test_postgresql.py similarity index 99% rename from tests/unit/hooks/test_postgresql.py rename to tests/unit/hooks/data_source/test_postgresql.py index 2f32913f..6beb801a 100644 --- a/tests/unit/hooks/test_postgresql.py +++ b/tests/unit/hooks/data_source/test_postgresql.py @@ -3,7 +3,7 @@ import logging import pytest from flexmock import flexmock -from borgmatic.hooks import postgresql as module +from borgmatic.hooks.data_source import postgresql as module def test_make_extra_environment_maps_options_to_environment(): diff --git a/tests/unit/hooks/test_sqlite.py b/tests/unit/hooks/data_source/test_sqlite.py similarity index 99% rename from tests/unit/hooks/test_sqlite.py rename to tests/unit/hooks/data_source/test_sqlite.py index fa6f24e6..860c4fc7 100644 --- a/tests/unit/hooks/test_sqlite.py +++ b/tests/unit/hooks/data_source/test_sqlite.py @@ -2,7 +2,7 @@ import logging from flexmock import flexmock -from borgmatic.hooks import sqlite as module +from borgmatic.hooks.data_source import sqlite as module def test_use_streaming_true_for_any_databases(): diff --git a/tests/unit/hooks/test_zfs.py b/tests/unit/hooks/data_source/test_zfs.py similarity index 99% rename from tests/unit/hooks/test_zfs.py rename to tests/unit/hooks/data_source/test_zfs.py index e88d358c..7f634dad 100644 --- a/tests/unit/hooks/test_zfs.py +++ b/tests/unit/hooks/data_source/test_zfs.py @@ -2,7 +2,7 @@ import pytest from flexmock import flexmock import borgmatic.execute -from borgmatic.hooks import zfs as module +from borgmatic.hooks.data_source import zfs as module def test_get_datasets_to_backup_filters_datasets_by_source_directories(): diff --git a/tests/unit/hooks/monitoring/__init__.py b/tests/unit/hooks/monitoring/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/hooks/test_apprise.py b/tests/unit/hooks/monitoring/test_apprise.py similarity index 70% rename from tests/unit/hooks/test_apprise.py rename to tests/unit/hooks/monitoring/test_apprise.py index 28bd45d4..91118cb0 100644 --- a/tests/unit/hooks/test_apprise.py +++ b/tests/unit/hooks/monitoring/test_apprise.py @@ -2,8 +2,8 @@ import apprise from apprise import NotifyFormat, NotifyType from flexmock import flexmock -import borgmatic.hooks.monitor -from borgmatic.hooks import apprise as module +import borgmatic.hooks.monitoring.monitor +from borgmatic.hooks.monitoring import apprise as module TOPIC = 'borgmatic-unit-testing' @@ -18,7 +18,7 @@ def mock_apprise(): def test_initialize_monitor_with_send_logs_false_does_not_add_handler(): - flexmock(module.borgmatic.hooks.logs).should_receive('add_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('add_handler').never() module.initialize_monitor( hook_config={'send_logs': False}, @@ -31,12 +31,14 @@ def test_initialize_monitor_with_send_logs_false_does_not_add_handler(): def test_initialize_monitor_with_send_logs_true_adds_handler_with_default_log_size_limit(): truncation_indicator_length = 4 - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').with_args( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).with_args( module.HANDLER_IDENTIFIER, module.DEFAULT_LOGS_SIZE_LIMIT_BYTES - truncation_indicator_length, 1, ).once() - flexmock(module.borgmatic.hooks.logs).should_receive('add_handler').once() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('add_handler').once() module.initialize_monitor( hook_config={'send_logs': True}, @@ -49,12 +51,14 @@ def test_initialize_monitor_with_send_logs_true_adds_handler_with_default_log_si def test_initialize_monitor_without_send_logs_adds_handler_with_default_log_size_limit(): truncation_indicator_length = 4 - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').with_args( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).with_args( module.HANDLER_IDENTIFIER, module.DEFAULT_LOGS_SIZE_LIMIT_BYTES - truncation_indicator_length, 1, ).once() - flexmock(module.borgmatic.hooks.logs).should_receive('add_handler').once() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('add_handler').once() module.initialize_monitor( hook_config={}, @@ -66,8 +70,8 @@ def test_initialize_monitor_without_send_logs_adds_handler_with_default_log_size def test_ping_monitor_respects_dry_run(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('loggy log') mock_apprise().should_receive('notify').never() @@ -76,30 +80,32 @@ def test_ping_monitor_respects_dry_run(): {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}]}, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=True, ) def test_ping_monitor_with_no_states_does_not_notify(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler').never() - flexmock(module.borgmatic.hooks.logs).should_receive('format_buffered_logs_for_payload').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'format_buffered_logs_for_payload' + ).never() mock_apprise().should_receive('notify').never() module.ping_monitor( {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': []}, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=True, ) def test_ping_monitor_notifies_fail_by_default(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('') mock_apprise().should_receive('notify').with_args( @@ -109,7 +115,7 @@ def test_ping_monitor_notifies_fail_by_default(): notify_type=NotifyType.FAILURE, ).once() - for state in borgmatic.hooks.monitor.State: + for state in borgmatic.hooks.monitoring.monitor.State: module.ping_monitor( {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}]}, {}, @@ -121,8 +127,8 @@ def test_ping_monitor_notifies_fail_by_default(): def test_ping_monitor_with_logs_appends_logs_to_body(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('loggy log') mock_apprise().should_receive('notify').with_args( @@ -132,7 +138,7 @@ def test_ping_monitor_with_logs_appends_logs_to_body(): notify_type=NotifyType.FAILURE, ).once() - for state in borgmatic.hooks.monitor.State: + for state in borgmatic.hooks.monitoring.monitor.State: module.ping_monitor( {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}]}, {}, @@ -144,8 +150,8 @@ def test_ping_monitor_with_logs_appends_logs_to_body(): def test_ping_monitor_with_finish_default_config_notifies(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('') mock_apprise().should_receive('notify').with_args( @@ -159,15 +165,17 @@ def test_ping_monitor_with_finish_default_config_notifies(): {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': ['finish']}, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FINISH, + borgmatic.hooks.monitoring.monitor.State.FINISH, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_with_start_default_config_notifies(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler').never() - flexmock(module.borgmatic.hooks.logs).should_receive('format_buffered_logs_for_payload').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'format_buffered_logs_for_payload' + ).never() mock_apprise().should_receive('notify').with_args( title='A borgmatic START event happened', body='A borgmatic START event happened', @@ -179,15 +187,15 @@ def test_ping_monitor_with_start_default_config_notifies(): {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': ['start']}, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_with_fail_default_config_notifies(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('') mock_apprise().should_receive('notify').with_args( @@ -201,15 +209,15 @@ def test_ping_monitor_with_fail_default_config_notifies(): {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': ['fail']}, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_with_log_default_config_notifies(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('') mock_apprise().should_receive('notify').with_args( @@ -223,15 +231,15 @@ def test_ping_monitor_with_log_default_config_notifies(): {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': ['log']}, {}, 'config.yaml', - borgmatic.hooks.monitor.State.LOG, + borgmatic.hooks.monitoring.monitor.State.LOG, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_passes_through_custom_message_title(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('') mock_apprise().should_receive('notify').with_args( @@ -249,15 +257,15 @@ def test_ping_monitor_passes_through_custom_message_title(): }, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_passes_through_custom_message_body(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('') mock_apprise().should_receive('notify').with_args( @@ -275,15 +283,15 @@ def test_ping_monitor_passes_through_custom_message_body(): }, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_passes_through_custom_message_body_and_appends_logs(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('loggy log') mock_apprise().should_receive('notify').with_args( @@ -301,15 +309,15 @@ def test_ping_monitor_passes_through_custom_message_body_and_appends_logs(): }, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_pings_multiple_services(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('') mock_apprise().should_receive('add').with_args([f'ntfys://{TOPIC}', f'ntfy://{TOPIC}']).once() @@ -323,36 +331,38 @@ def test_ping_monitor_pings_multiple_services(): }, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_logs_info_for_no_services(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler').never() - flexmock(module.borgmatic.hooks.logs).should_receive('format_buffered_logs_for_payload').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'format_buffered_logs_for_payload' + ).never() flexmock(module.logger).should_receive('info').once() module.ping_monitor( {'services': []}, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) def test_ping_monitor_logs_warning_when_notify_fails(): - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return('') mock_apprise().should_receive('notify').and_return(False) flexmock(module.logger).should_receive('warning').once() - for state in borgmatic.hooks.monitor.State: + for state in borgmatic.hooks.monitoring.monitor.State: module.ping_monitor( {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}]}, {}, @@ -364,7 +374,7 @@ def test_ping_monitor_logs_warning_when_notify_fails(): def test_destroy_monitor_does_not_raise(): - flexmock(module.borgmatic.hooks.logs).should_receive('remove_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('remove_handler') module.destroy_monitor( hook_config={}, diff --git a/tests/unit/hooks/test_cronhub.py b/tests/unit/hooks/monitoring/test_cronhub.py similarity index 98% rename from tests/unit/hooks/test_cronhub.py rename to tests/unit/hooks/monitoring/test_cronhub.py index edb167ad..5a9dd882 100644 --- a/tests/unit/hooks/test_cronhub.py +++ b/tests/unit/hooks/monitoring/test_cronhub.py @@ -1,6 +1,6 @@ from flexmock import flexmock -from borgmatic.hooks import cronhub as module +from borgmatic.hooks.monitoring import cronhub as module def test_ping_monitor_rewrites_ping_url_for_start_state(): diff --git a/tests/unit/hooks/test_cronitor.py b/tests/unit/hooks/monitoring/test_cronitor.py similarity index 98% rename from tests/unit/hooks/test_cronitor.py rename to tests/unit/hooks/monitoring/test_cronitor.py index 9daa2c11..ebf3b569 100644 --- a/tests/unit/hooks/test_cronitor.py +++ b/tests/unit/hooks/monitoring/test_cronitor.py @@ -1,6 +1,6 @@ from flexmock import flexmock -from borgmatic.hooks import cronitor as module +from borgmatic.hooks.monitoring import cronitor as module def test_ping_monitor_hits_ping_url_for_start_state(): diff --git a/tests/unit/hooks/test_healthchecks.py b/tests/unit/hooks/monitoring/test_healthchecks.py similarity index 78% rename from tests/unit/hooks/test_healthchecks.py rename to tests/unit/hooks/monitoring/test_healthchecks.py index bef25e16..a8f703dc 100644 --- a/tests/unit/hooks/test_healthchecks.py +++ b/tests/unit/hooks/monitoring/test_healthchecks.py @@ -1,6 +1,6 @@ from flexmock import flexmock -from borgmatic.hooks import healthchecks as module +from borgmatic.hooks.monitoring import healthchecks as module def mock_logger(): @@ -15,9 +15,11 @@ def test_initialize_monitor_creates_log_handler_with_ping_body_limit(): monitoring_log_level = 1 mock_logger() - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').with_args( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).with_args( module.HANDLER_IDENTIFIER, - ping_body_limit - len(module.borgmatic.hooks.logs.PAYLOAD_TRUNCATION_INDICATOR), + ping_body_limit - len(module.borgmatic.hooks.monitoring.logs.PAYLOAD_TRUNCATION_INDICATOR), monitoring_log_level, ).once() @@ -30,10 +32,12 @@ def test_initialize_monitor_creates_log_handler_with_default_ping_body_limit(): monitoring_log_level = 1 mock_logger() - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').with_args( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).with_args( module.HANDLER_IDENTIFIER, module.DEFAULT_PING_BODY_LIMIT_BYTES - - len(module.borgmatic.hooks.logs.PAYLOAD_TRUNCATION_INDICATOR), + - len(module.borgmatic.hooks.monitoring.logs.PAYLOAD_TRUNCATION_INDICATOR), monitoring_log_level, ).once() @@ -45,9 +49,9 @@ def test_initialize_monitor_creates_log_handler_with_zero_ping_body_limit(): monitoring_log_level = 1 mock_logger() - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').with_args( - module.HANDLER_IDENTIFIER, ping_body_limit, monitoring_log_level - ).once() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).with_args(module.HANDLER_IDENTIFIER, ping_body_limit, monitoring_log_level).once() module.initialize_monitor( {'ping_body_limit': ping_body_limit}, {}, 'test.yaml', monitoring_log_level, dry_run=False @@ -56,7 +60,9 @@ def test_initialize_monitor_creates_log_handler_with_zero_ping_body_limit(): def test_initialize_monitor_creates_log_handler_when_send_logs_true(): mock_logger() - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').once() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).once() module.initialize_monitor( {'send_logs': True}, {}, 'test.yaml', monitoring_log_level=1, dry_run=False @@ -65,7 +71,9 @@ def test_initialize_monitor_creates_log_handler_when_send_logs_true(): def test_initialize_monitor_bails_when_send_logs_false(): mock_logger() - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() module.initialize_monitor( {'send_logs': False}, {}, 'test.yaml', monitoring_log_level=1, dry_run=False @@ -73,7 +81,9 @@ def test_initialize_monitor_bails_when_send_logs_false(): def test_ping_monitor_hits_ping_url_for_start_state(): - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() hook_config = {'ping_url': 'https://example.com'} flexmock(module.requests).should_receive('post').with_args( 'https://example.com/start', data=''.encode('utf-8'), verify=True @@ -92,8 +102,8 @@ def test_ping_monitor_hits_ping_url_for_start_state(): def test_ping_monitor_hits_ping_url_for_finish_state(): hook_config = {'ping_url': 'https://example.com'} payload = 'data' - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return(payload) flexmock(module.requests).should_receive('post').with_args( @@ -113,8 +123,8 @@ def test_ping_monitor_hits_ping_url_for_finish_state(): def test_ping_monitor_hits_ping_url_for_fail_state(): hook_config = {'ping_url': 'https://example.com'} payload = 'data' - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return(payload) flexmock(module.requests).should_receive('post').with_args( @@ -134,8 +144,8 @@ def test_ping_monitor_hits_ping_url_for_fail_state(): def test_ping_monitor_hits_ping_url_for_log_state(): hook_config = {'ping_url': 'https://example.com'} payload = 'data' - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return(payload) flexmock(module.requests).should_receive('post').with_args( @@ -155,8 +165,8 @@ def test_ping_monitor_hits_ping_url_for_log_state(): def test_ping_monitor_with_ping_uuid_hits_corresponding_url(): hook_config = {'ping_url': 'abcd-efgh-ijkl-mnop'} payload = 'data' - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return(payload) flexmock(module.requests).should_receive('post').with_args( @@ -178,8 +188,8 @@ def test_ping_monitor_with_ping_uuid_hits_corresponding_url(): def test_ping_monitor_skips_ssl_verification_when_verify_tls_false(): hook_config = {'ping_url': 'https://example.com', 'verify_tls': False} payload = 'data' - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return(payload) flexmock(module.requests).should_receive('post').with_args( @@ -199,8 +209,8 @@ def test_ping_monitor_skips_ssl_verification_when_verify_tls_false(): def test_ping_monitor_executes_ssl_verification_when_verify_tls_true(): hook_config = {'ping_url': 'https://example.com', 'verify_tls': True} payload = 'data' - flexmock(module.borgmatic.hooks.logs).should_receive('get_handler') - flexmock(module.borgmatic.hooks.logs).should_receive( + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload' ).and_return(payload) flexmock(module.requests).should_receive('post').with_args( @@ -218,7 +228,9 @@ def test_ping_monitor_executes_ssl_verification_when_verify_tls_true(): def test_ping_monitor_dry_run_does_not_hit_ping_url(): - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() hook_config = {'ping_url': 'https://example.com'} flexmock(module.requests).should_receive('post').never() @@ -233,7 +245,9 @@ def test_ping_monitor_dry_run_does_not_hit_ping_url(): def test_ping_monitor_does_not_hit_ping_url_when_states_not_matching(): - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() hook_config = {'ping_url': 'https://example.com', 'states': ['finish']} flexmock(module.requests).should_receive('post').never() @@ -248,7 +262,9 @@ def test_ping_monitor_does_not_hit_ping_url_when_states_not_matching(): def test_ping_monitor_hits_ping_url_when_states_matching(): - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() hook_config = {'ping_url': 'https://example.com', 'states': ['start', 'finish']} flexmock(module.requests).should_receive('post').with_args( 'https://example.com/start', data=''.encode('utf-8'), verify=True @@ -265,7 +281,9 @@ def test_ping_monitor_hits_ping_url_when_states_matching(): def test_ping_monitor_adds_create_query_parameter_when_create_slug_true(): - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() hook_config = {'ping_url': 'https://example.com', 'create_slug': True} flexmock(module.requests).should_receive('post').with_args( 'https://example.com/start?create=1', data=''.encode('utf-8'), verify=True @@ -282,7 +300,9 @@ def test_ping_monitor_adds_create_query_parameter_when_create_slug_true(): def test_ping_monitor_does_not_add_create_query_parameter_when_create_slug_false(): - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() hook_config = {'ping_url': 'https://example.com', 'create_slug': False} flexmock(module.requests).should_receive('post').with_args( 'https://example.com/start', data=''.encode('utf-8'), verify=True @@ -334,7 +354,9 @@ def test_ping_monitor_issues_warning_when_ping_url_is_uuid_and_create_slug_true( def test_ping_monitor_with_connection_error_logs_warning(): - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() hook_config = {'ping_url': 'https://example.com'} flexmock(module.requests).should_receive('post').with_args( 'https://example.com/start', data=''.encode('utf-8'), verify=True @@ -352,7 +374,9 @@ def test_ping_monitor_with_connection_error_logs_warning(): def test_ping_monitor_with_other_error_logs_warning(): - flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( + 'Forgetful_buffering_handler' + ).never() hook_config = {'ping_url': 'https://example.com'} response = flexmock(ok=False) response.should_receive('raise_for_status').and_raise( diff --git a/tests/unit/hooks/test_logs.py b/tests/unit/hooks/monitoring/test_logs.py similarity index 98% rename from tests/unit/hooks/test_logs.py rename to tests/unit/hooks/monitoring/test_logs.py index 3c57d3a8..11073802 100644 --- a/tests/unit/hooks/test_logs.py +++ b/tests/unit/hooks/monitoring/test_logs.py @@ -1,7 +1,7 @@ import pytest from flexmock import flexmock -from borgmatic.hooks import logs as module +from borgmatic.hooks.monitoring import logs as module def test_forgetful_buffering_handler_emit_collects_log_records(): diff --git a/tests/unit/hooks/test_loki.py b/tests/unit/hooks/monitoring/test_loki.py similarity index 98% rename from tests/unit/hooks/test_loki.py rename to tests/unit/hooks/monitoring/test_loki.py index ccf19553..b00ac3a2 100644 --- a/tests/unit/hooks/test_loki.py +++ b/tests/unit/hooks/monitoring/test_loki.py @@ -3,7 +3,7 @@ import json import requests from flexmock import flexmock -from borgmatic.hooks import loki as module +from borgmatic.hooks.monitoring import loki as module def test_loki_log_buffer_add_value_gets_raw(): diff --git a/tests/unit/hooks/test_ntfy.py b/tests/unit/hooks/monitoring/test_ntfy.py similarity index 87% rename from tests/unit/hooks/test_ntfy.py rename to tests/unit/hooks/monitoring/test_ntfy.py index 0f815b36..a5819ec9 100644 --- a/tests/unit/hooks/test_ntfy.py +++ b/tests/unit/hooks/monitoring/test_ntfy.py @@ -2,8 +2,8 @@ from enum import Enum from flexmock import flexmock -import borgmatic.hooks.monitor -from borgmatic.hooks import ntfy as module +import borgmatic.hooks.monitoring.monitor +from borgmatic.hooks.monitoring import ntfy as module default_base_url = 'https://ntfy.sh' custom_base_url = 'https://ntfy.example.com' @@ -38,7 +38,7 @@ def test_ping_monitor_minimal_config_hits_hosted_ntfy_on_fail(): hook_config = {'topic': topic} flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=None, ).and_return(flexmock(ok=True)).once() @@ -46,7 +46,7 @@ def test_ping_monitor_minimal_config_hits_hosted_ntfy_on_fail(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -59,7 +59,7 @@ def test_ping_monitor_with_access_token_hits_hosted_ntfy_on_fail(): } flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=module.requests.auth.HTTPBasicAuth('', 'abc123'), ).and_return(flexmock(ok=True)).once() @@ -67,7 +67,7 @@ def test_ping_monitor_with_access_token_hits_hosted_ntfy_on_fail(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -82,7 +82,7 @@ def test_ping_monitor_with_username_password_and_access_token_ignores_username_p } flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=module.requests.auth.HTTPBasicAuth('', 'abc123'), ).and_return(flexmock(ok=True)).once() flexmock(module.logger).should_receive('warning').once() @@ -91,7 +91,7 @@ def test_ping_monitor_with_username_password_and_access_token_ignores_username_p hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -105,7 +105,7 @@ def test_ping_monitor_with_username_password_hits_hosted_ntfy_on_fail(): } flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=module.requests.auth.HTTPBasicAuth('testuser', 'fakepassword'), ).and_return(flexmock(ok=True)).once() @@ -113,7 +113,7 @@ def test_ping_monitor_with_username_password_hits_hosted_ntfy_on_fail(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -123,7 +123,7 @@ def test_ping_monitor_with_password_but_no_username_warns(): hook_config = {'topic': topic, 'password': 'fakepassword'} flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=None, ).and_return(flexmock(ok=True)).once() flexmock(module.logger).should_receive('warning').once() @@ -132,7 +132,7 @@ def test_ping_monitor_with_password_but_no_username_warns(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -142,7 +142,7 @@ def test_ping_monitor_with_username_but_no_password_warns(): hook_config = {'topic': topic, 'username': 'testuser'} flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=None, ).and_return(flexmock(ok=True)).once() flexmock(module.logger).should_receive('warning').once() @@ -151,7 +151,7 @@ def test_ping_monitor_with_username_but_no_password_warns(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -165,7 +165,7 @@ def test_ping_monitor_minimal_config_does_not_hit_hosted_ntfy_on_start(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -179,7 +179,7 @@ def test_ping_monitor_minimal_config_does_not_hit_hosted_ntfy_on_finish(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FINISH, + borgmatic.hooks.monitoring.monitor.State.FINISH, monitoring_log_level=1, dry_run=False, ) @@ -189,7 +189,7 @@ def test_ping_monitor_minimal_config_hits_selfhosted_ntfy_on_fail(): hook_config = {'topic': topic, 'server': custom_base_url} flexmock(module.requests).should_receive('post').with_args( f'{custom_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=None, ).and_return(flexmock(ok=True)).once() @@ -197,7 +197,7 @@ def test_ping_monitor_minimal_config_hits_selfhosted_ntfy_on_fail(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -211,7 +211,7 @@ def test_ping_monitor_minimal_config_does_not_hit_hosted_ntfy_on_fail_dry_run(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=True, ) @@ -227,7 +227,7 @@ def test_ping_monitor_custom_message_hits_hosted_ntfy_on_fail(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -237,7 +237,7 @@ def test_ping_monitor_custom_state_hits_hosted_ntfy_on_start(): hook_config = {'topic': topic, 'states': ['start', 'fail']} flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.START), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.START), auth=None, ).and_return(flexmock(ok=True)).once() @@ -245,7 +245,7 @@ def test_ping_monitor_custom_state_hits_hosted_ntfy_on_start(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -255,7 +255,7 @@ def test_ping_monitor_with_connection_error_logs_warning(): hook_config = {'topic': topic} flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=None, ).and_raise(module.requests.exceptions.ConnectionError) flexmock(module.logger).should_receive('warning').once() @@ -264,7 +264,7 @@ def test_ping_monitor_with_connection_error_logs_warning(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -278,7 +278,7 @@ def test_ping_monitor_with_other_error_logs_warning(): ) flexmock(module.requests).should_receive('post').with_args( f'{default_base_url}/{topic}', - headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL), + headers=return_default_message_headers(borgmatic.hooks.monitoring.monitor.State.FAIL), auth=None, ).and_return(response) flexmock(module.logger).should_receive('warning').once() @@ -287,7 +287,7 @@ def test_ping_monitor_with_other_error_logs_warning(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) diff --git a/tests/unit/hooks/test_pagerduty.py b/tests/unit/hooks/monitoring/test_pagerduty.py similarity index 97% rename from tests/unit/hooks/test_pagerduty.py rename to tests/unit/hooks/monitoring/test_pagerduty.py index 5a5ed12a..bf7cc7ba 100644 --- a/tests/unit/hooks/test_pagerduty.py +++ b/tests/unit/hooks/monitoring/test_pagerduty.py @@ -1,6 +1,6 @@ from flexmock import flexmock -from borgmatic.hooks import pagerduty as module +from borgmatic.hooks.monitoring import pagerduty as module def test_ping_monitor_ignores_start_state(): diff --git a/tests/unit/hooks/test_pushover.py b/tests/unit/hooks/monitoring/test_pushover.py similarity index 95% rename from tests/unit/hooks/test_pushover.py rename to tests/unit/hooks/monitoring/test_pushover.py index 1cc451a4..281a88f0 100644 --- a/tests/unit/hooks/test_pushover.py +++ b/tests/unit/hooks/monitoring/test_pushover.py @@ -1,8 +1,8 @@ import pytest from flexmock import flexmock -import borgmatic.hooks.monitor -from borgmatic.hooks import pushover as module +import borgmatic.hooks.monitoring.monitor +from borgmatic.hooks.monitoring import pushover as module def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_send_to_pushover(): @@ -26,7 +26,7 @@ def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_ hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -45,7 +45,7 @@ def test_ping_monitor_config_with_minimum_config_start_state_backup_not_send_to_ hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -78,7 +78,7 @@ def test_ping_monitor_start_state_backup_default_message_successfully_send_to_pu hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -111,7 +111,7 @@ def test_ping_monitor_start_state_backup_custom_message_successfully_send_to_pus hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -146,7 +146,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -181,7 +181,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -216,7 +216,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -244,7 +244,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_high_decl hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -307,7 +307,7 @@ def test_ping_monitor_start_state_backup_based_on_documentation_advanced_example hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -375,7 +375,7 @@ def test_ping_monitor_fail_state_backup_based_on_documentation_advanced_example_ hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -440,7 +440,7 @@ def test_ping_monitor_finish_state_backup_based_on_documentation_advanced_exampl hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FINISH, + borgmatic.hooks.monitoring.monitor.State.FINISH, monitoring_log_level=1, dry_run=False, ) @@ -459,7 +459,7 @@ def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_ hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=True, ) @@ -480,7 +480,7 @@ def test_ping_monitor_config_incorrect_state_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=True, ) @@ -516,7 +516,7 @@ def test_ping_monitor_push_post_error_exits_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) diff --git a/tests/unit/hooks/test_uptimekuma.py b/tests/unit/hooks/monitoring/test_uptimekuma.py similarity index 86% rename from tests/unit/hooks/test_uptimekuma.py rename to tests/unit/hooks/monitoring/test_uptimekuma.py index 98bd3158..b29e1c44 100644 --- a/tests/unit/hooks/test_uptimekuma.py +++ b/tests/unit/hooks/monitoring/test_uptimekuma.py @@ -1,7 +1,7 @@ from flexmock import flexmock -import borgmatic.hooks.monitor -from borgmatic.hooks import uptimekuma as module +import borgmatic.hooks.monitoring.monitor +from borgmatic.hooks.monitoring import uptime_kuma as module DEFAULT_PUSH_URL = 'https://example.uptime.kuma/api/push/abcd1234' CUSTOM_PUSH_URL = 'https://uptime.example.com/api/push/efgh5678' @@ -17,7 +17,7 @@ def test_ping_monitor_hits_default_uptimekuma_on_fail(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -33,7 +33,7 @@ def test_ping_monitor_hits_custom_uptimekuma_on_fail(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -49,7 +49,7 @@ def test_ping_monitor_custom_uptimekuma_on_start(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -65,7 +65,7 @@ def test_ping_monitor_custom_uptimekuma_on_finish(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FINISH, + borgmatic.hooks.monitoring.monitor.State.FINISH, monitoring_log_level=1, dry_run=False, ) @@ -79,7 +79,7 @@ def test_ping_monitor_does_not_hit_custom_uptimekuma_on_fail_dry_run(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=True, ) @@ -93,7 +93,7 @@ def test_ping_monitor_does_not_hit_custom_uptimekuma_on_start_dry_run(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=True, ) @@ -107,7 +107,7 @@ def test_ping_monitor_does_not_hit_custom_uptimekuma_on_finish_dry_run(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FINISH, + borgmatic.hooks.monitoring.monitor.State.FINISH, monitoring_log_level=1, dry_run=True, ) @@ -124,7 +124,7 @@ def test_ping_monitor_with_connection_error_logs_warning(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -145,7 +145,7 @@ def test_ping_monitor_with_other_error_logs_warning(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -159,7 +159,7 @@ def test_ping_monitor_with_invalid_run_state(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.LOG, + borgmatic.hooks.monitoring.monitor.State.LOG, monitoring_log_level=1, dry_run=True, ) diff --git a/tests/unit/hooks/test_zabbix.py b/tests/unit/hooks/monitoring/test_zabbix.py similarity index 92% rename from tests/unit/hooks/test_zabbix.py rename to tests/unit/hooks/monitoring/test_zabbix.py index 6f1a641d..77193d67 100644 --- a/tests/unit/hooks/test_zabbix.py +++ b/tests/unit/hooks/monitoring/test_zabbix.py @@ -1,7 +1,7 @@ from flexmock import flexmock -import borgmatic.hooks.monitor -from borgmatic.hooks import zabbix as module +import borgmatic.hooks.monitoring.monitor +from borgmatic.hooks.monitoring import zabbix as module SERVER = 'https://zabbix.com/zabbix/api_jsonrpc.php' ITEMID = 55105 @@ -65,7 +65,7 @@ def test_ping_monitor_with_non_matching_state_exits_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.START, + borgmatic.hooks.monitoring.monitor.State.START, monitoring_log_level=1, dry_run=False, ) @@ -82,7 +82,7 @@ def test_ping_monitor_config_with_api_key_only_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -99,7 +99,7 @@ def test_ping_monitor_config_with_host_only_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -116,7 +116,7 @@ def test_ping_monitor_config_with_key_only_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -133,7 +133,7 @@ def test_ping_monitor_config_with_server_only_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -149,7 +149,7 @@ def test_ping_monitor_config_user_password_no_zabbix_data_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -165,7 +165,7 @@ def test_ping_monitor_config_api_key_no_zabbix_data_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -182,7 +182,7 @@ def test_ping_monitor_config_itemid_no_auth_data_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -199,7 +199,7 @@ def test_ping_monitor_config_host_and_key_no_auth_data_exit_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -220,7 +220,7 @@ def test_ping_monitor_config_host_and_key_with_api_key_auth_data_successful(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -235,7 +235,7 @@ def test_ping_monitor_config_host_and_missing_key_exits_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -250,7 +250,7 @@ def test_ping_monitor_config_key_and_missing_host_exits_early(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -290,7 +290,7 @@ def test_ping_monitor_config_host_and_key_with_username_password_auth_data_succe hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -329,7 +329,7 @@ def test_ping_monitor_config_host_and_key_with_username_password_auth_data_and_a hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -350,7 +350,7 @@ def test_ping_monitor_config_host_and_key_with_username_and_missing_password_exi hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -371,7 +371,7 @@ def test_ping_monitor_config_host_and_key_with_passing_and_missing_username_exit hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -392,7 +392,7 @@ def test_ping_monitor_config_itemid_with_api_key_auth_data_successful(): hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -426,7 +426,7 @@ def test_ping_monitor_config_itemid_with_username_password_auth_data_successful( hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) @@ -462,7 +462,7 @@ def test_ping_monitor_config_itemid_with_username_password_auth_data_and_push_po hook_config, {}, 'config.yaml', - borgmatic.hooks.monitor.State.FAIL, + borgmatic.hooks.monitoring.monitor.State.FAIL, monitoring_log_level=1, dry_run=False, ) diff --git a/tests/unit/hooks/test_dispatch.py b/tests/unit/hooks/test_dispatch.py index cdbcde1a..08bec40f 100644 --- a/tests/unit/hooks/test_dispatch.py +++ b/tests/unit/hooks/test_dispatch.py @@ -17,7 +17,15 @@ def test_call_hook_invokes_module_function_with_arguments_and_returns_value(): config = {'super_hook': flexmock(), 'other_hook': flexmock()} expected_return_value = flexmock() test_module = sys.modules[__name__] - flexmock(module).HOOK_NAME_TO_MODULE = {'super_hook': test_module} + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.data_source + ).and_return(['other_hook']) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'third_hook']) + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring.super_hook' + ).and_return(test_module) flexmock(test_module).should_receive('hook_function').with_args( config['super_hook'], config, 'prefix', 55, value=66 ).and_return(expected_return_value).once() @@ -27,11 +35,65 @@ def test_call_hook_invokes_module_function_with_arguments_and_returns_value(): assert return_value == expected_return_value +def test_call_hook_probes_config_with_databases_suffix(): + config = {'super_hook_databases': flexmock(), 'other_hook': flexmock()} + expected_return_value = flexmock() + test_module = sys.modules[__name__] + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.data_source + ).and_return(['other_hook']) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'third_hook']) + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring.super_hook' + ).and_return(test_module) + flexmock(test_module).should_receive('hook_function').with_args( + config['super_hook_databases'], config, 'prefix', 55, value=66 + ).and_return(expected_return_value).once() + + return_value = module.call_hook('hook_function', config, 'prefix', 'super_hook', 55, value=66) + + assert return_value == expected_return_value + + +def test_call_hook_strips_databases_suffix_from_hook_name(): + config = {'super_hook_databases': flexmock(), 'other_hook_databases': flexmock()} + expected_return_value = flexmock() + test_module = sys.modules[__name__] + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.data_source + ).and_return(['other_hook']) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'third_hook']) + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring.super_hook' + ).and_return(test_module) + flexmock(test_module).should_receive('hook_function').with_args( + config['super_hook_databases'], config, 'prefix', 55, value=66 + ).and_return(expected_return_value).once() + + return_value = module.call_hook( + 'hook_function', config, 'prefix', 'super_hook_databases', 55, value=66 + ) + + assert return_value == expected_return_value + + def test_call_hook_without_hook_config_invokes_module_function_with_arguments_and_returns_value(): config = {'other_hook': flexmock()} expected_return_value = flexmock() test_module = sys.modules[__name__] - flexmock(module).HOOK_NAME_TO_MODULE = {'super_hook': test_module} + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.data_source + ).and_return(['other_hook']) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'third_hook']) + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring.super_hook' + ).and_return(test_module) flexmock(test_module).should_receive('hook_function').with_args( {}, config, 'prefix', 55, value=66 ).and_return(expected_return_value).once() @@ -44,35 +106,69 @@ def test_call_hook_without_hook_config_invokes_module_function_with_arguments_an def test_call_hook_without_corresponding_module_raises(): config = {'super_hook': flexmock(), 'other_hook': flexmock()} test_module = sys.modules[__name__] - flexmock(module).HOOK_NAME_TO_MODULE = {'other_hook': test_module} + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.data_source + ).and_return(['other_hook']) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['some_hook']) + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring.super_hook' + ).and_return(test_module) flexmock(test_module).should_receive('hook_function').never() with pytest.raises(ValueError): module.call_hook('hook_function', config, 'prefix', 'super_hook', 55, value=66) +def test_call_hook_skips_non_hook_modules(): + config = {'not_a_hook': flexmock(), 'other_hook': flexmock()} + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.data_source + ).and_return(['other_hook']) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['not_a_hook', 'third_hook']) + not_a_hook_module = flexmock(IS_A_HOOK=False) + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring.not_a_hook' + ).and_return(not_a_hook_module) + + return_value = module.call_hook('hook_function', config, 'prefix', 'not_a_hook', 55, value=66) + + assert return_value is None + + def test_call_hooks_calls_each_hook_and_collects_return_values(): config = {'super_hook': flexmock(), 'other_hook': flexmock()} expected_return_values = {'super_hook': flexmock(), 'other_hook': flexmock()} + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring' + ).and_return(module.borgmatic.hooks.monitoring) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'other_hook']) flexmock(module).should_receive('call_hook').and_return( expected_return_values['super_hook'] ).and_return(expected_return_values['other_hook']) - return_values = module.call_hooks( - 'do_stuff', config, 'prefix', ('super_hook', 'other_hook'), 55 - ) + return_values = module.call_hooks('do_stuff', config, 'prefix', module.Hook_type.MONITORING, 55) assert return_values == expected_return_values -def test_call_hooks_calls_skips_return_values_for_missing_hooks(): +def test_call_hooks_calls_skips_return_values_for_unconfigured_hooks(): config = {'super_hook': flexmock()} expected_return_values = {'super_hook': flexmock()} + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring' + ).and_return(module.borgmatic.hooks.monitoring) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'other_hook']) flexmock(module).should_receive('call_hook').and_return(expected_return_values['super_hook']) - return_values = module.call_hooks( - 'do_stuff', config, 'prefix', ('super_hook', 'other_hook'), 55 - ) + return_values = module.call_hooks('do_stuff', config, 'prefix', module.Hook_type.MONITORING, 55) assert return_values == expected_return_values @@ -80,13 +176,35 @@ def test_call_hooks_calls_skips_return_values_for_missing_hooks(): def test_call_hooks_calls_treats_null_hook_as_optionless(): config = {'super_hook': flexmock(), 'other_hook': None} expected_return_values = {'super_hook': flexmock(), 'other_hook': flexmock()} + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring' + ).and_return(module.borgmatic.hooks.monitoring) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'other_hook']) flexmock(module).should_receive('call_hook').and_return( expected_return_values['super_hook'] ).and_return(expected_return_values['other_hook']) - return_values = module.call_hooks( - 'do_stuff', config, 'prefix', ('super_hook', 'other_hook'), 55 - ) + return_values = module.call_hooks('do_stuff', config, 'prefix', module.Hook_type.MONITORING, 55) + + assert return_values == expected_return_values + + +def test_call_hooks_calls_looks_up_databases_suffix_in_config(): + config = {'super_hook_databases': flexmock(), 'other_hook': flexmock()} + expected_return_values = {'super_hook': flexmock(), 'other_hook': flexmock()} + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring' + ).and_return(module.borgmatic.hooks.monitoring) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'other_hook']) + flexmock(module).should_receive('call_hook').and_return( + expected_return_values['super_hook'] + ).and_return(expected_return_values['other_hook']) + + return_values = module.call_hooks('do_stuff', config, 'prefix', module.Hook_type.MONITORING, 55) assert return_values == expected_return_values @@ -94,12 +212,18 @@ def test_call_hooks_calls_treats_null_hook_as_optionless(): def test_call_hooks_even_if_unconfigured_calls_each_hook_and_collects_return_values(): config = {'super_hook': flexmock(), 'other_hook': flexmock()} expected_return_values = {'super_hook': flexmock(), 'other_hook': flexmock()} + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring' + ).and_return(module.borgmatic.hooks.monitoring) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'other_hook']) flexmock(module).should_receive('call_hook').and_return( expected_return_values['super_hook'] ).and_return(expected_return_values['other_hook']) return_values = module.call_hooks_even_if_unconfigured( - 'do_stuff', config, 'prefix', ('super_hook', 'other_hook'), 55 + 'do_stuff', config, 'prefix', module.Hook_type.MONITORING, 55 ) assert return_values == expected_return_values @@ -108,12 +232,18 @@ def test_call_hooks_even_if_unconfigured_calls_each_hook_and_collects_return_val def test_call_hooks_even_if_unconfigured_calls_each_hook_configured_or_not_and_collects_return_values(): config = {'other_hook': flexmock()} expected_return_values = {'super_hook': flexmock(), 'other_hook': flexmock()} + flexmock(module.importlib).should_receive('import_module').with_args( + 'borgmatic.hooks.monitoring' + ).and_return(module.borgmatic.hooks.monitoring) + flexmock(module).should_receive('get_submodule_names').with_args( + module.borgmatic.hooks.monitoring + ).and_return(['super_hook', 'other_hook']) flexmock(module).should_receive('call_hook').and_return( expected_return_values['super_hook'] ).and_return(expected_return_values['other_hook']) return_values = module.call_hooks_even_if_unconfigured( - 'do_stuff', config, 'prefix', ('super_hook', 'other_hook'), 55 + 'do_stuff', config, 'prefix', module.Hook_type.MONITORING, 55 ) assert return_values == expected_return_values