More test coverage, and simplification of config generation.
This commit is contained in:
parent
a16d90ff46
commit
1bcb2a8be4
6 changed files with 165 additions and 40 deletions
|
@ -3,6 +3,7 @@ syntax: glob
|
||||||
*.pyc
|
*.pyc
|
||||||
*.swp
|
*.swp
|
||||||
.cache
|
.cache
|
||||||
|
.coverage
|
||||||
.tox
|
.tox
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
|
|
39
borgmatic/commands/generate_config.py
Normal file
39
borgmatic/commands/generate_config.py
Normal 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)
|
|
@ -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
|
||||||
|
|
43
borgmatic/tests/integration/config/test_generate.py
Normal file
43
borgmatic/tests/integration/config/test_generate.py
Normal 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')
|
49
borgmatic/tests/unit/config/test_generate.py
Normal file
49
borgmatic/tests/unit/config/test_generate.py
Normal 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'),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
])
|
1
setup.py
1
setup.py
|
@ -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=[
|
||||||
|
|
Loading…
Reference in a new issue