When running multiple configuration files, attempt all of them even if one errors (#116).

This commit is contained in:
Dan Helfman 2018-12-25 15:23:54 -08:00
parent d6feca169c
commit 45a537b6b1
4 changed files with 83 additions and 25 deletions

4
NEWS
View file

@ -1,3 +1,7 @@
1.2.14.dev0
* #116: When running multiple configuration files, attempt all configuration files even if one of
them errors. Log a summary of results at the end.
1.2.13 1.2.13
* #100: Support for --stats command-line flag independent of --verbosity. * #100: Support for --stats command-line flag independent of --verbosity.
* #117: With borgmatic --init command-line flag, proceed without erroring if a repository already * #117: With borgmatic --init command-line flag, proceed without erroring if a repository already

View file

@ -29,7 +29,7 @@ LEGACY_CONFIG_PATH = '/etc/borgmatic/config'
def parse_arguments(*arguments): def parse_arguments(*arguments):
''' '''
Given command-line arguments with which this script was invoked, parse the arguments and return Given command-line arguments with which this script was invoked, parse the arguments and return
them as an ArgumentParser instance. them as an argparse.ArgumentParser instance.
''' '''
config_paths = collect.get_default_config_paths() config_paths = collect.get_default_config_paths()
@ -308,25 +308,53 @@ def _run_commands_on_repository(
sys.stdout.write(output) sys.stdout.write(output)
def main(): # pragma: no cover def collect_configuration_run_summary_logs(config_filenames, args):
try: '''
configure_signals() Given a sequence of configuration filenames and parsed command-line arguments as an
args = parse_arguments(*sys.argv[1:]) argparse.ArgumentParser instance, run each configuration file and yield a series of
logging.basicConfig(level=verbosity_to_log_level(args.verbosity), format='%(message)s') logging.LogRecord instances containing summary information about each run.
'''
config_filenames = tuple(collect.collect_config_filenames(args.config_paths)) for config_filename in config_filenames:
logger.debug('Ensuring legacy configuration is upgraded') try:
convert.guard_configuration_upgraded(LEGACY_CONFIG_PATH, config_filenames)
if len(config_filenames) == 0:
raise ValueError(
'Error: No configuration files found in: {}'.format(' '.join(args.config_paths))
)
for config_filename in config_filenames:
run_configuration(config_filename, args) run_configuration(config_filename, args)
except (ValueError, OSError, CalledProcessError) as error: yield logging.makeLogRecord(
print(error, file=sys.stderr) dict(
print(file=sys.stderr) levelno=logging.INFO,
print('Need some help? https://torsion.org/borgmatic/#issues', file=sys.stderr) msg='{}: Successfully ran configuration file'.format(config_filename),
)
)
except (ValueError, OSError, CalledProcessError) as error:
yield logging.makeLogRecord(
dict(
levelno=logging.CRITICAL,
msg='{}: Error running configuration file'.format(config_filename),
)
)
yield logging.makeLogRecord(dict(levelno=logging.CRITICAL, msg=error))
if not config_filenames:
yield logging.makeLogRecord(
dict(
levelno=logging.CRITICAL,
msg='{}: No configuration files found'.format(' '.join(args.config_paths)),
)
)
def main(): # pragma: no cover
configure_signals()
args = parse_arguments(*sys.argv[1:])
logging.basicConfig(level=verbosity_to_log_level(args.verbosity), format='%(message)s')
config_filenames = tuple(collect.collect_config_filenames(args.config_paths))
logger.debug('Ensuring legacy configuration is upgraded')
convert.guard_configuration_upgraded(LEGACY_CONFIG_PATH, config_filenames)
summary_logs = tuple(collect_configuration_run_summary_logs(config_filenames, args))
logger.info('\nsummary:')
[logger.handle(log) for log in summary_logs if log.levelno >= logger.getEffectiveLevel()]
if any(log.levelno == logging.CRITICAL for log in summary_logs):
logger.critical('\nNeed some help? https://torsion.org/borgmatic/#issues')
sys.exit(1) sys.exit(1)

View file

@ -1,7 +1,7 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
VERSION = '1.2.13' VERSION = '1.2.14.dev0'
setup( setup(

View file

@ -3,12 +3,12 @@ import sys
from flexmock import flexmock from flexmock import flexmock
from borgmatic.commands import borgmatic from borgmatic.commands import borgmatic as module
def test_run_commands_handles_multiple_json_outputs_in_array(): def test_run_commands_handles_multiple_json_outputs_in_array():
( (
flexmock(borgmatic) flexmock(module)
.should_receive('_run_commands_on_repository') .should_receive('_run_commands_on_repository')
.times(3) .times(3)
.replace_with( .replace_with(
@ -36,7 +36,7 @@ def test_run_commands_handles_multiple_json_outputs_in_array():
) )
) )
borgmatic._run_commands( module._run_commands(
args=flexmock(json=True), args=flexmock(json=True),
consistency=None, consistency=None,
local_path=None, local_path=None,
@ -45,3 +45,29 @@ def test_run_commands_handles_multiple_json_outputs_in_array():
retention=None, retention=None,
storage=None, storage=None,
) )
def test_collect_configuration_run_summary_logs_info_for_success():
flexmock(module).should_receive('run_configuration')
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=()))
assert any(log for log in logs if log.levelno == module.logging.INFO)
def test_collect_configuration_run_summary_logs_critical_for_error():
flexmock(module).should_receive('run_configuration').and_raise(ValueError)
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=()))
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)
def test_collect_configuration_run_summary_logs_critical_for_missing_configs():
logs = tuple(
module.collect_configuration_run_summary_logs(
config_filenames=(), args=flexmock(config_paths=())
)
)
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)