瀏覽代碼

More test coverage, and simplification of config generation.

Dan Helfman 8 年之前
父節點
當前提交
f581f4b8d9

+ 1 - 0
.hgignore

@@ -3,6 +3,7 @@ syntax: glob
 *.pyc
 *.swp
 .cache
+.coverage
 .tox
 build
 dist

+ 39 - 0
borgmatic/commands/generate_config.py

@@ -0,0 +1,39 @@
+from __future__ import print_function
+from argparse import ArgumentParser
+import os
+from subprocess import CalledProcessError
+import sys
+
+from ruamel import yaml
+
+from borgmatic import borg
+from borgmatic.config import convert, generate, validate
+
+
+DEFAULT_DESTINATION_CONFIG_FILENAME = '/etc/borgmatic/config.yaml'
+
+
+def parse_arguments(*arguments):
+    '''
+    Given command-line arguments with which this script was invoked, parse the arguments and return
+    them as an ArgumentParser instance.
+    '''
+    parser = ArgumentParser(description='Generate a sample borgmatic YAML configuration file.')
+    parser.add_argument(
+        '-d', '--destination',
+        dest='destination_filename',
+        default=DEFAULT_DESTINATION_CONFIG_FILENAME,
+        help='Destination YAML configuration filename. Default: {}'.format(DEFAULT_DESTINATION_CONFIG_FILENAME),
+    )
+
+    return parser.parse_args(arguments)
+
+
+def main():
+    try:
+        args = parse_arguments(*sys.argv[1:])
+
+        generate.generate_sample_configuration(args.destination_filename, validate.schema_filename())
+    except (ValueError, OSError) as error:
+        print(error, file=sys.stderr)
+        sys.exit(1)

+ 32 - 40
borgmatic/config/generate.py

@@ -6,15 +6,6 @@ from ruamel import yaml
 INDENT = 4
 
 
-def write_configuration(config_filename, config):
-    '''
-    Given a target config filename and a config data structure of nested OrderedDicts, write out the
-    config to file as YAML.
-    '''
-    with open(config_filename, 'w') as config_file:
-        config_file.write(yaml.round_trip_dump(config, indent=INDENT, block_seq_indent=INDENT))
-
-
 def _insert_newline_before_comment(config, field_name):
     '''
     Using some ruamel.yaml black magic, insert a blank line in the config right befor the given
@@ -26,6 +17,37 @@ def _insert_newline_before_comment(config, field_name):
     )
 
 
+def _schema_to_sample_configuration(schema, level=0):
+    '''
+    Given a loaded configuration schema, generate and return sample config for it. Include comments
+    for each section based on the schema "desc" description.
+    '''
+    example = schema.get('example')
+    if example:
+        return example
+
+    config = yaml.comments.CommentedMap([
+        (
+            section_name,
+            _schema_to_sample_configuration(section_schema, level + 1),
+        )
+        for section_name, section_schema in schema['map'].items()
+    ])
+
+    add_comments_to_configuration(config, schema, indent=(level * INDENT))
+
+    return config
+
+
+def write_configuration(config_filename, config):
+    '''
+    Given a target config filename and a config data structure of nested OrderedDicts, write out the
+    config to file as YAML.
+    '''
+    with open(config_filename, 'w') as config_file:
+        config_file.write(yaml.round_trip_dump(config, indent=INDENT, block_seq_indent=INDENT))
+
+
 def add_comments_to_configuration(config, schema, indent=0):
     '''
     Using descriptions from a schema as a source, add those descriptions as comments to the given
@@ -37,7 +59,7 @@ def add_comments_to_configuration(config, schema, indent=0):
         description = field_schema.get('desc')
 
         # No description to use? Skip it.
-        if not schema or not description:
+        if not field_schema or not description:
             continue
 
         config.yaml_set_comment_before_after_key(
@@ -49,36 +71,6 @@ def add_comments_to_configuration(config, schema, indent=0):
             _insert_newline_before_comment(config, field_name)
 
 
-def _section_schema_to_sample_configuration(section_schema):
-    '''
-    Given the schema for a particular config section, generate and return sample config for that
-    section. Include comments for each field based on the schema "desc" description.
-    '''
-    section_config = yaml.comments.CommentedMap([
-        (field_name, field_schema['example'])
-        for field_name, field_schema in section_schema['map'].items()
-    ])
-
-    add_comments_to_configuration(section_config, section_schema, indent=INDENT)
-
-    return section_config
-
-
-def _schema_to_sample_configuration(schema):
-    '''
-    Given a loaded configuration schema, generate and return sample config for it. Include comments
-    for each section based on the schema "desc" description.
-    '''
-    config = yaml.comments.CommentedMap([
-        (section_name, _section_schema_to_sample_configuration(section_schema))
-        for section_name, section_schema in schema['map'].items()
-    ])
-
-    add_comments_to_configuration(config, schema)
-
-    return config
-
-
 def generate_sample_configuration(config_filename, schema_filename):
     '''
     Given a target config filename and the path to a schema filename in pykwalify YAML schema

+ 43 - 0
borgmatic/tests/integration/config/test_generate.py

@@ -0,0 +1,43 @@
+from io import StringIO
+import sys
+
+from flexmock import flexmock
+
+from borgmatic.config import generate as module
+
+
+def test_insert_newline_before_comment_does_not_raise():
+    field_name = 'foo'
+    config = module.yaml.comments.CommentedMap([(field_name, 33)])
+    config.yaml_set_comment_before_after_key(key=field_name, before='Comment',)
+
+    module._insert_newline_before_comment(config, field_name)
+
+
+def test_write_configuration_does_not_raise():
+    builtins = flexmock(sys.modules['builtins'])
+    builtins.should_receive('open').and_return(StringIO())
+
+    module.write_configuration('config.yaml', {})
+
+
+def test_add_comments_to_configuration_does_not_raise():
+    # Ensure that it can deal with fields both in the schema and missing from the schema.
+    config = module.yaml.comments.CommentedMap([('foo', 33), ('bar', 44), ('baz', 55)])
+    schema = {
+        'map': {
+            'foo': {'desc': 'Foo'},
+            'bar': {'desc': 'Bar'},
+        }
+    }
+
+    module.add_comments_to_configuration(config, schema)
+
+
+def test_generate_sample_configuration_does_not_raise():
+    builtins = flexmock(sys.modules['builtins'])
+    builtins.should_receive('open').with_args('schema.yaml').and_return('')
+    flexmock(module).should_receive('write_configuration')
+    flexmock(module).should_receive('_schema_to_sample_configuration')
+
+    module.generate_sample_configuration('config.yaml', 'schema.yaml')

+ 49 - 0
borgmatic/tests/unit/config/test_generate.py

@@ -0,0 +1,49 @@
+from collections import OrderedDict
+
+from flexmock import flexmock
+
+from borgmatic.config import generate as module
+
+
+def test_schema_to_sample_configuration_generates_config_with_examples():
+    flexmock(module.yaml.comments).should_receive('CommentedMap').replace_with(OrderedDict)
+    flexmock(module).should_receive('add_comments_to_configuration')
+    schema = {
+        'map': OrderedDict([
+            (
+                'section1', {
+                    'map': {
+                        'field1': OrderedDict([
+                            ('example', 'Example 1')
+                        ]),
+                    },
+                },
+            ),
+            (
+                'section2', {
+                    'map': OrderedDict([
+                        ('field2', {'example': 'Example 2'}),
+                        ('field3', {'example': 'Example 3'}),
+                    ]),
+                }
+            ),
+        ])
+    }
+
+    config = module._schema_to_sample_configuration(schema)
+
+    assert config == OrderedDict([
+        (
+            'section1',
+            OrderedDict([
+                ('field1', 'Example 1'),
+            ]),
+        ),
+        (
+            'section2',
+            OrderedDict([
+                ('field2', 'Example 2'),
+                ('field3', 'Example 3'),
+            ]),
+        )
+    ])

+ 1 - 0
setup.py

@@ -26,6 +26,7 @@ setup(
         'console_scripts': [
             'borgmatic = borgmatic.commands.borgmatic:main',
             'convert-borgmatic-config = borgmatic.commands.convert_config:main',
+            'generate-borgmatic-config = borgmatic.commands.generate_config:main',
         ]
     },
     obsoletes=[