Reorganize data source and monitoring hooks to make developing new hooks easier.

This commit is contained in:
Dan Helfman 2024-11-27 08:50:34 -08:00
parent a5c6a2fe1c
commit 6b2f2b2ac4
58 changed files with 508 additions and 349 deletions

1
NEWS
View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

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

View File

@ -2,7 +2,7 @@ import logging
import requests
from borgmatic.hooks import monitor
from borgmatic.hooks.monitoring import monitor
logger = logging.getLogger(__name__)

View File

@ -2,7 +2,7 @@ import logging
import requests
from borgmatic.hooks import monitor
from borgmatic.hooks.monitoring import monitor
logger = logging.getLogger(__name__)

View File

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

View File

@ -1,5 +1,6 @@
import logging
IS_A_HOOK = False
PAYLOAD_TRUNCATION_INDICATOR = '...\n'

View File

@ -6,7 +6,7 @@ import time
import requests
from borgmatic.hooks import monitor
from borgmatic.hooks.monitoring import monitor
logger = logging.getLogger(__name__)

View File

@ -0,0 +1,10 @@
import enum
IS_A_HOOK = False
class State(enum.Enum):
START = 1
FINISH = 2
FAIL = 3
LOG = 4

View File

@ -5,7 +5,7 @@ import platform
import requests
from borgmatic.hooks import monitor
from borgmatic.hooks.monitoring import monitor
logger = logging.getLogger(__name__)

View File

@ -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.

View File

View File

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

View File

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

View File

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

View File

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

View File

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

@ -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={},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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