In generate-borgmatic-config, comment out all optional config (#57).
This commit is contained in:
parent
3821636b77
commit
47efa88c9d
5 changed files with 141 additions and 21 deletions
4
NEWS
4
NEWS
|
@ -1,3 +1,7 @@
|
||||||
|
1.2.5
|
||||||
|
* #57: When generating sample configuration with generate-borgmatic-config, comment out all
|
||||||
|
optional configuration so as to streamline the initial configuration process.
|
||||||
|
|
||||||
1.2.4
|
1.2.4
|
||||||
* Fix for archive checking traceback due to parameter mismatch.
|
* Fix for archive checking traceback due to parameter mismatch.
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ not in your system `PATH`. Try looking in `/usr/local/bin/`.
|
||||||
This generates a sample configuration file at /etc/borgmatic/config.yaml (by
|
This generates a sample configuration file at /etc/borgmatic/config.yaml (by
|
||||||
default). You should edit the file to suit your needs, as the values are just
|
default). You should edit the file to suit your needs, as the values are just
|
||||||
representative. All fields are optional except where indicated, so feel free
|
representative. All fields are optional except where indicated, so feel free
|
||||||
to remove anything you don't need.
|
to ignore anything you don't need.
|
||||||
|
|
||||||
You can also have a look at the [full configuration
|
You can also have a look at the [full configuration
|
||||||
schema](https://projects.torsion.org/witten/borgmatic/src/master/borgmatic/config/schema.yaml)
|
schema](https://projects.torsion.org/witten/borgmatic/src/master/borgmatic/config/schema.yaml)
|
||||||
|
|
|
@ -9,7 +9,7 @@ INDENT = 4
|
||||||
|
|
||||||
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 before the given
|
||||||
field and its comments.
|
field and its comments.
|
||||||
'''
|
'''
|
||||||
config.ca.items[field_name][1].insert(
|
config.ca.items[field_name][1].insert(
|
||||||
|
@ -40,10 +40,58 @@ def _schema_to_sample_configuration(schema, level=0):
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def write_configuration(config_filename, config, mode=0o600):
|
def _comment_out_line(line):
|
||||||
|
# If it's already is commented out (or empty), there's nothing further to do!
|
||||||
|
stripped_line = line.lstrip()
|
||||||
|
if not stripped_line or stripped_line.startswith('#'):
|
||||||
|
return line
|
||||||
|
|
||||||
|
# Comment out the names of optional sections.
|
||||||
|
one_indent = ' ' * INDENT
|
||||||
|
if not line.startswith(one_indent):
|
||||||
|
return '#' + line
|
||||||
|
|
||||||
|
# Otherwise, comment out the line, but insert the "#" after the first indent for aesthetics.
|
||||||
|
return '#'.join((one_indent, line[INDENT:]))
|
||||||
|
|
||||||
|
|
||||||
|
def _comment_out_optional_configuration(rendered_config):
|
||||||
'''
|
'''
|
||||||
Given a target config filename and a config data structure of nested OrderedDicts, write out the
|
Post-process a rendered configuration string to comment out optional key/values. The idea is
|
||||||
config to file as YAML. Create any containing directories as needed.
|
that this prevents the user from having to comment out a bunch of configuration they don't care
|
||||||
|
about to get to a minimal viable configuration file.
|
||||||
|
|
||||||
|
Ideally ruamel.yaml would support this during configuration generation, but it's not terribly
|
||||||
|
easy to accomplish that way.
|
||||||
|
'''
|
||||||
|
lines = []
|
||||||
|
required = False
|
||||||
|
|
||||||
|
for line in rendered_config.split('\n'):
|
||||||
|
# Upon encountering a required configuration option, skip commenting out lines until the
|
||||||
|
# next blank line.
|
||||||
|
stripped_line = line.strip()
|
||||||
|
if stripped_line in {'source_directories:', 'repositories:'} or line == 'location:':
|
||||||
|
required = True
|
||||||
|
elif not stripped_line:
|
||||||
|
required = False
|
||||||
|
|
||||||
|
lines.append(_comment_out_line(line) if not required else line)
|
||||||
|
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _render_configuration(config):
|
||||||
|
'''
|
||||||
|
Given a config data structure of nested OrderedDicts, render the config as YAML and return it.
|
||||||
|
'''
|
||||||
|
return yaml.round_trip_dump(config, indent=INDENT, block_seq_indent=INDENT)
|
||||||
|
|
||||||
|
|
||||||
|
def write_configuration(config_filename, rendered_config, mode=0o600):
|
||||||
|
'''
|
||||||
|
Given a target config filename and rendered config YAML, write it out to file. Create any
|
||||||
|
containing directories as needed.
|
||||||
'''
|
'''
|
||||||
if os.path.exists(config_filename):
|
if os.path.exists(config_filename):
|
||||||
raise FileExistsError('{} already exists. Aborting.'.format(config_filename))
|
raise FileExistsError('{} already exists. Aborting.'.format(config_filename))
|
||||||
|
@ -54,7 +102,7 @@ def write_configuration(config_filename, config, mode=0o600):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with open(config_filename, 'w') as config_file:
|
with open(config_filename, 'w') as config_file:
|
||||||
config_file.write(yaml.round_trip_dump(config, indent=INDENT, block_seq_indent=INDENT))
|
config_file.write(rendered_config)
|
||||||
|
|
||||||
os.chmod(config_filename, mode)
|
os.chmod(config_filename, mode)
|
||||||
|
|
||||||
|
@ -90,4 +138,7 @@ def generate_sample_configuration(config_filename, schema_filename):
|
||||||
schema = yaml.round_trip_load(open(schema_filename))
|
schema = yaml.round_trip_load(open(schema_filename))
|
||||||
config = _schema_to_sample_configuration(schema)
|
config = _schema_to_sample_configuration(schema)
|
||||||
|
|
||||||
write_configuration(config_filename, config)
|
write_configuration(
|
||||||
|
config_filename,
|
||||||
|
_comment_out_optional_configuration(_render_configuration(config))
|
||||||
|
)
|
||||||
|
|
|
@ -18,6 +18,16 @@ map:
|
||||||
- /home
|
- /home
|
||||||
- /etc
|
- /etc
|
||||||
- /var/log/syslog*
|
- /var/log/syslog*
|
||||||
|
repositories:
|
||||||
|
required: true
|
||||||
|
seq:
|
||||||
|
- type: scalar
|
||||||
|
desc: |
|
||||||
|
Paths to local or remote repositories (required). Tildes are expanded. Multiple
|
||||||
|
repositories are backed up to in sequence. See ssh_command for SSH options like
|
||||||
|
identity file or port.
|
||||||
|
example:
|
||||||
|
- user@backupserver:sourcehostname.borg
|
||||||
one_file_system:
|
one_file_system:
|
||||||
type: bool
|
type: bool
|
||||||
desc: Stay in same file system (do not cross mount points).
|
desc: Stay in same file system (do not cross mount points).
|
||||||
|
@ -48,16 +58,6 @@ map:
|
||||||
type: scalar
|
type: scalar
|
||||||
desc: Alternate Borg remote executable. Defaults to "borg".
|
desc: Alternate Borg remote executable. Defaults to "borg".
|
||||||
example: borg1
|
example: borg1
|
||||||
repositories:
|
|
||||||
required: true
|
|
||||||
seq:
|
|
||||||
- type: scalar
|
|
||||||
desc: |
|
|
||||||
Paths to local or remote repositories (required). Tildes are expanded. Multiple
|
|
||||||
repositories are backed up to in sequence. See ssh_command for SSH options like
|
|
||||||
identity file or port.
|
|
||||||
example:
|
|
||||||
- user@backupserver:sourcehostname.borg
|
|
||||||
patterns:
|
patterns:
|
||||||
seq:
|
seq:
|
||||||
- type: scalar
|
- type: scalar
|
||||||
|
|
|
@ -16,6 +16,69 @@ def test_insert_newline_before_comment_does_not_raise():
|
||||||
module._insert_newline_before_comment(config, field_name)
|
module._insert_newline_before_comment(config, field_name)
|
||||||
|
|
||||||
|
|
||||||
|
def test_comment_out_line_skips_blank_line():
|
||||||
|
line = ' \n'
|
||||||
|
|
||||||
|
assert module._comment_out_line(line) == line
|
||||||
|
|
||||||
|
|
||||||
|
def test_comment_out_line_skips_already_commented_out_line():
|
||||||
|
line = ' # foo'
|
||||||
|
|
||||||
|
assert module._comment_out_line(line) == line
|
||||||
|
|
||||||
|
|
||||||
|
def test_comment_out_line_comments_section_name():
|
||||||
|
line = 'figgy-pudding:'
|
||||||
|
|
||||||
|
assert module._comment_out_line(line) == '#' + line
|
||||||
|
|
||||||
|
|
||||||
|
def test_comment_out_line_comments_indented_option():
|
||||||
|
line = ' enabled: true'
|
||||||
|
|
||||||
|
assert module._comment_out_line(line) == ' #enabled: true'
|
||||||
|
|
||||||
|
|
||||||
|
def test_comment_out_optional_configuration_comments_optional_config_only():
|
||||||
|
flexmock(module)._comment_out_line = lambda line: '#' + line
|
||||||
|
config = '''
|
||||||
|
foo:
|
||||||
|
bar:
|
||||||
|
- baz
|
||||||
|
- quux
|
||||||
|
|
||||||
|
location:
|
||||||
|
repositories:
|
||||||
|
- one
|
||||||
|
- two
|
||||||
|
|
||||||
|
other: thing
|
||||||
|
'''
|
||||||
|
|
||||||
|
expected_config = '''
|
||||||
|
#foo:
|
||||||
|
# bar:
|
||||||
|
# - baz
|
||||||
|
# - quux
|
||||||
|
#
|
||||||
|
location:
|
||||||
|
repositories:
|
||||||
|
- one
|
||||||
|
- two
|
||||||
|
#
|
||||||
|
# other: thing
|
||||||
|
'''
|
||||||
|
|
||||||
|
assert module._comment_out_optional_configuration(config.strip()) == expected_config.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_configuration_does_not_raise():
|
||||||
|
flexmock(module.yaml).should_receive('round_trip_dump')
|
||||||
|
|
||||||
|
module._render_configuration({})
|
||||||
|
|
||||||
|
|
||||||
def test_write_configuration_does_not_raise():
|
def test_write_configuration_does_not_raise():
|
||||||
flexmock(os.path).should_receive('exists').and_return(False)
|
flexmock(os.path).should_receive('exists').and_return(False)
|
||||||
flexmock(os).should_receive('makedirs')
|
flexmock(os).should_receive('makedirs')
|
||||||
|
@ -23,14 +86,14 @@ def test_write_configuration_does_not_raise():
|
||||||
builtins.should_receive('open').and_return(StringIO())
|
builtins.should_receive('open').and_return(StringIO())
|
||||||
flexmock(os).should_receive('chmod')
|
flexmock(os).should_receive('chmod')
|
||||||
|
|
||||||
module.write_configuration('config.yaml', {})
|
module.write_configuration('config.yaml', 'config: yaml')
|
||||||
|
|
||||||
|
|
||||||
def test_write_configuration_with_already_existing_file_raises():
|
def test_write_configuration_with_already_existing_file_raises():
|
||||||
flexmock(os.path).should_receive('exists').and_return(True)
|
flexmock(os.path).should_receive('exists').and_return(True)
|
||||||
|
|
||||||
with pytest.raises(FileExistsError):
|
with pytest.raises(FileExistsError):
|
||||||
module.write_configuration('config.yaml', {})
|
module.write_configuration('config.yaml', 'config: yaml')
|
||||||
|
|
||||||
|
|
||||||
def test_write_configuration_with_already_existing_directory_does_not_raise():
|
def test_write_configuration_with_already_existing_directory_does_not_raise():
|
||||||
|
@ -40,7 +103,7 @@ def test_write_configuration_with_already_existing_directory_does_not_raise():
|
||||||
builtins.should_receive('open').and_return(StringIO())
|
builtins.should_receive('open').and_return(StringIO())
|
||||||
flexmock(os).should_receive('chmod')
|
flexmock(os).should_receive('chmod')
|
||||||
|
|
||||||
module.write_configuration('config.yaml', {})
|
module.write_configuration('config.yaml', 'config: yaml')
|
||||||
|
|
||||||
|
|
||||||
def test_add_comments_to_configuration_does_not_raise():
|
def test_add_comments_to_configuration_does_not_raise():
|
||||||
|
@ -59,7 +122,9 @@ def test_add_comments_to_configuration_does_not_raise():
|
||||||
def test_generate_sample_configuration_does_not_raise():
|
def test_generate_sample_configuration_does_not_raise():
|
||||||
builtins = flexmock(sys.modules['builtins'])
|
builtins = flexmock(sys.modules['builtins'])
|
||||||
builtins.should_receive('open').with_args('schema.yaml').and_return('')
|
builtins.should_receive('open').with_args('schema.yaml').and_return('')
|
||||||
flexmock(module).should_receive('write_configuration')
|
|
||||||
flexmock(module).should_receive('_schema_to_sample_configuration')
|
flexmock(module).should_receive('_schema_to_sample_configuration')
|
||||||
|
flexmock(module).should_receive('_render_configuration')
|
||||||
|
flexmock(module).should_receive('_comment_out_optional_configuration')
|
||||||
|
flexmock(module).should_receive('write_configuration')
|
||||||
|
|
||||||
module.generate_sample_configuration('config.yaml', 'schema.yaml')
|
module.generate_sample_configuration('config.yaml', 'schema.yaml')
|
||||||
|
|
Loading…
Reference in a new issue