浏览代码

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

Dan Helfman 6 年之前
父节点
当前提交
45a537b6b1
共有 4 个文件被更改,包括 80 次插入22 次删除
  1. 4 0
      NEWS
  2. 46 18
      borgmatic/commands/borgmatic.py
  3. 1 1
      setup.py
  4. 29 3
      tests/unit/commands/test_borgmatic.py

+ 4 - 0
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
  * #100: Support for --stats command-line flag independent of --verbosity.
  * #117: With borgmatic --init command-line flag, proceed without erroring if a repository already

+ 46 - 18
borgmatic/commands/borgmatic.py

@@ -29,7 +29,7 @@ LEGACY_CONFIG_PATH = '/etc/borgmatic/config'
 def parse_arguments(*arguments):
     '''
     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()
 
@@ -308,25 +308,53 @@ def _run_commands_on_repository(
             sys.stdout.write(output)
 
 
+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:
+            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
-    try:
-        configure_signals()
-        args = parse_arguments(*sys.argv[1:])
-        logging.basicConfig(level=verbosity_to_log_level(args.verbosity), format='%(message)s')
+    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)
+    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)
 
-        if len(config_filenames) == 0:
-            raise ValueError(
-                'Error: No configuration files found in: {}'.format(' '.join(args.config_paths))
-            )
+    summary_logs = tuple(collect_configuration_run_summary_logs(config_filenames, args))
 
-        for config_filename in config_filenames:
-            run_configuration(config_filename, args)
-    except (ValueError, OSError, CalledProcessError) as error:
-        print(error, file=sys.stderr)
-        print(file=sys.stderr)
-        print('Need some help? https://torsion.org/borgmatic/#issues', file=sys.stderr)
+    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)

+ 1 - 1
setup.py

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

+ 29 - 3
tests/unit/commands/test_borgmatic.py

@@ -3,12 +3,12 @@ import sys
 
 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():
     (
-        flexmock(borgmatic)
+        flexmock(module)
         .should_receive('_run_commands_on_repository')
         .times(3)
         .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),
         consistency=None,
         local_path=None,
@@ -45,3 +45,29 @@ def test_run_commands_handles_multiple_json_outputs_in_array():
         retention=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)