When running multiple configuration files, attempt all of them even if one errors (#116).
This commit is contained in:
parent
d6feca169c
commit
45a537b6b1
4 changed files with 83 additions and 25 deletions
4
NEWS
4
NEWS
|
@ -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
|
||||||
|
|
|
@ -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,8 +308,40 @@ 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):
|
||||||
|
'''
|
||||||
|
Given a sequence of configuration filenames and parsed command-line arguments as an
|
||||||
|
argparse.ArgumentParser instance, run each configuration file and yield a series of
|
||||||
|
logging.LogRecord instances containing summary information about each run.
|
||||||
|
'''
|
||||||
|
for config_filename in config_filenames:
|
||||||
try:
|
try:
|
||||||
|
run_configuration(config_filename, args)
|
||||||
|
yield logging.makeLogRecord(
|
||||||
|
dict(
|
||||||
|
levelno=logging.INFO,
|
||||||
|
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()
|
configure_signals()
|
||||||
args = parse_arguments(*sys.argv[1:])
|
args = parse_arguments(*sys.argv[1:])
|
||||||
logging.basicConfig(level=verbosity_to_log_level(args.verbosity), format='%(message)s')
|
logging.basicConfig(level=verbosity_to_log_level(args.verbosity), format='%(message)s')
|
||||||
|
@ -318,15 +350,11 @@ def main(): # pragma: no cover
|
||||||
logger.debug('Ensuring legacy configuration is upgraded')
|
logger.debug('Ensuring legacy configuration is upgraded')
|
||||||
convert.guard_configuration_upgraded(LEGACY_CONFIG_PATH, config_filenames)
|
convert.guard_configuration_upgraded(LEGACY_CONFIG_PATH, config_filenames)
|
||||||
|
|
||||||
if len(config_filenames) == 0:
|
summary_logs = tuple(collect_configuration_run_summary_logs(config_filenames, args))
|
||||||
raise ValueError(
|
|
||||||
'Error: No configuration files found in: {}'.format(' '.join(args.config_paths))
|
|
||||||
)
|
|
||||||
|
|
||||||
for config_filename in config_filenames:
|
logger.info('\nsummary:')
|
||||||
run_configuration(config_filename, args)
|
[logger.handle(log) for log in summary_logs if log.levelno >= logger.getEffectiveLevel()]
|
||||||
except (ValueError, OSError, CalledProcessError) as error:
|
|
||||||
print(error, file=sys.stderr)
|
if any(log.levelno == logging.CRITICAL for log in summary_logs):
|
||||||
print(file=sys.stderr)
|
logger.critical('\nNeed some help? https://torsion.org/borgmatic/#issues')
|
||||||
print('Need some help? https://torsion.org/borgmatic/#issues', file=sys.stderr)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue