2
0
Эх сурвалжийг харах

View the results of configuration file merging via "validate-borgmatic-config --show" flag (#673).

Dan Helfman 2 жил өмнө
parent
commit
4c0e2cab78

+ 3 - 0
NEWS

@@ -7,6 +7,9 @@
    "match_archives" configuration option for the "transfer", "list", "rlist", and "info" actions.
    "match_archives" configuration option for the "transfer", "list", "rlist", and "info" actions.
  * #668: Fix error when running the "prune" action with both "archive_name_format" and "prefix"
  * #668: Fix error when running the "prune" action with both "archive_name_format" and "prefix"
    options set.
    options set.
+ * #673: View the results of configuration file merging via "validate-borgmatic-config --show" flag.
+   See the documentation for more information:
+   https://torsion.org/borgmatic/docs/how-to/make-per-application-backups/#debugging-includes
  * Add optional support for running end-to-end tests and building documentation with rootless Podman
  * Add optional support for running end-to-end tests and building documentation with rootless Podman
    instead of Docker.
    instead of Docker.
 
 

+ 21 - 5
borgmatic/commands/validate_config.py

@@ -2,6 +2,7 @@ import logging
 import sys
 import sys
 from argparse import ArgumentParser
 from argparse import ArgumentParser
 
 
+import borgmatic.config.generate
 from borgmatic.config import collect, validate
 from borgmatic.config import collect, validate
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
@@ -23,16 +24,22 @@ def parse_arguments(*arguments):
         default=config_paths,
         default=config_paths,
         help=f'Configuration filenames or directories, defaults to: {config_paths}',
         help=f'Configuration filenames or directories, defaults to: {config_paths}',
     )
     )
+    parser.add_argument(
+        '-s',
+        '--show',
+        action='store_true',
+        help='Show the validated configuration after all include merging has occurred',
+    )
 
 
     return parser.parse_args(arguments)
     return parser.parse_args(arguments)
 
 
 
 
 def main():  # pragma: no cover
 def main():  # pragma: no cover
-    args = parse_arguments(*sys.argv[1:])
+    arguments = parse_arguments(*sys.argv[1:])
 
 
     logging.basicConfig(level=logging.INFO, format='%(message)s')
     logging.basicConfig(level=logging.INFO, format='%(message)s')
 
 
-    config_filenames = tuple(collect.collect_config_filenames(args.config_paths))
+    config_filenames = tuple(collect.collect_config_filenames(arguments.config_paths))
     if len(config_filenames) == 0:
     if len(config_filenames) == 0:
         logger.critical('No files to validate found')
         logger.critical('No files to validate found')
         sys.exit(1)
         sys.exit(1)
@@ -40,13 +47,22 @@ def main():  # pragma: no cover
     found_issues = False
     found_issues = False
     for config_filename in config_filenames:
     for config_filename in config_filenames:
         try:
         try:
-            validate.parse_configuration(config_filename, validate.schema_filename())
+            config, parse_logs = validate.parse_configuration(
+                config_filename, validate.schema_filename()
+            )
         except (ValueError, OSError, validate.Validation_error) as error:
         except (ValueError, OSError, validate.Validation_error) as error:
             logging.critical(f'{config_filename}: Error parsing configuration file')
             logging.critical(f'{config_filename}: Error parsing configuration file')
             logging.critical(error)
             logging.critical(error)
             found_issues = True
             found_issues = True
+        else:
+            for log in parse_logs:
+                logger.handle(log)
+
+            if arguments.show:
+                print('---')
+                print(borgmatic.config.generate.render_configuration(config))
 
 
     if found_issues:
     if found_issues:
         sys.exit(1)
         sys.exit(1)
-    else:
-        logger.info(f"All given configuration files are valid: {', '.join(config_filenames)}")
+
+    logger.info(f"All given configuration files are valid: {', '.join(config_filenames)}")

+ 17 - 0
docs/how-to/make-per-application-backups.md

@@ -276,6 +276,23 @@ include, the local file's option takes precedence.
 list values are appended together.
 list values are appended together.
 
 
 
 
+## Debugging includes
+
+<span class="minilink minilink-addedin">New in version 1.7.12</span> If you'd
+like to see what the loaded configuration looks like after includes get merged
+in, run `validate-borgmatic-config` on your configuration file:
+
+```bash
+sudo validate-borgmatic-config --show
+```
+
+You'll need to specify your configuration file with `--config` if it's not in
+a default location.
+
+This will output the merged configuration as borgmatic sees it, which can be
+helpful for understanding how your includes work in practice.
+
+
 ## Configuration overrides
 ## Configuration overrides
 
 
 In more complex multi-application setups, you may want to override particular
 In more complex multi-application setups, you may want to override particular

+ 3 - 0
docs/how-to/set-up-backups.md

@@ -180,6 +180,9 @@ following command is available for that:
 sudo validate-borgmatic-config
 sudo validate-borgmatic-config
 ```
 ```
 
 
+You'll need to specify your configuration file with `--config` if it's not in
+a default location.
+
 This command's exit status (`$?` in Bash) is zero when configuration is valid
 This command's exit status (`$?` in Bash) is zero when configuration is valid
 and non-zero otherwise.
 and non-zero otherwise.
 
 

+ 14 - 0
tests/end-to-end/test_validate_config.py

@@ -1,5 +1,6 @@
 import os
 import os
 import subprocess
 import subprocess
+import sys
 import tempfile
 import tempfile
 
 
 
 
@@ -26,3 +27,16 @@ def test_validate_config_command_with_invalid_configuration_fails():
         exit_code = subprocess.call(f'validate-borgmatic-config --config {config_path}'.split(' '))
         exit_code = subprocess.call(f'validate-borgmatic-config --config {config_path}'.split(' '))
 
 
         assert exit_code == 1
         assert exit_code == 1
+
+
+def test_validate_config_command_with_show_flag_displays_configuration():
+    with tempfile.TemporaryDirectory() as temporary_directory:
+        config_path = os.path.join(temporary_directory, 'test.yaml')
+
+        subprocess.check_call(f'generate-borgmatic-config --destination {config_path}'.split(' '))
+        output = subprocess.check_output(
+            f'validate-borgmatic-config --config {config_path} --show'.split(' ')
+        ).decode(sys.stdout.encoding)
+
+        assert 'location:' in output
+        assert 'repositories:' in output

+ 9 - 0
tests/integration/commands/test_validate_config.py

@@ -18,3 +18,12 @@ def test_parse_arguments_with_multiple_config_paths_parses_as_list():
     parser = module.parse_arguments('--config', 'myconfig', 'otherconfig')
     parser = module.parse_arguments('--config', 'myconfig', 'otherconfig')
 
 
     assert parser.config_paths == ['myconfig', 'otherconfig']
     assert parser.config_paths == ['myconfig', 'otherconfig']
+
+
+def test_parse_arguments_supports_show_flag():
+    config_paths = ['default']
+    flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
+
+    parser = module.parse_arguments('--config', 'myconfig', '--show')
+
+    assert parser.show