More test coverage, and simplification of config generation.

This commit is contained in:
Dan Helfman 2017-07-09 11:41:55 -07:00
parent a16d90ff46
commit 1bcb2a8be4
6 changed files with 165 additions and 40 deletions

View file

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

View file

@ -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)

View file

@ -6,15 +6,6 @@ from ruamel import yaml
INDENT = 4 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): 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 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): 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 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') description = field_schema.get('desc')
# No description to use? Skip it. # No description to use? Skip it.
if not schema or not description: if not field_schema or not description:
continue continue
config.yaml_set_comment_before_after_key( 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) _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): def generate_sample_configuration(config_filename, schema_filename):
''' '''
Given a target config filename and the path to a schema filename in pykwalify YAML schema Given a target config filename and the path to a schema filename in pykwalify YAML schema

View file

@ -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')

View file

@ -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'),
]),
)
])

View file

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