Browse Source

Require "prefix" in retention section when "archive_name_format" is set.

tags/1.1.9
Dan 3 years ago
parent
commit
43d0e597a2
6 changed files with 79 additions and 25 deletions
  1. +2
    -0
      .gitignore
  2. +4
    -2
      NEWS
  3. +3
    -1
      borgmatic/config/schema.yaml
  4. +27
    -15
      borgmatic/config/validate.py
  5. +0
    -7
      borgmatic/tests/integration/config/test_validate.py
  6. +43
    -0
      borgmatic/tests/unit/config/test_validate.py

+ 2
- 0
.gitignore View File

@@ -1,6 +1,8 @@
*.egg-info
*.pyc
*.swp
.cache
.coverage
.tox
build
dist

+ 4
- 2
NEWS View File

@@ -2,8 +2,10 @@
* #16, #38: Support for user-defined hooks before/after backup, or on error.
* #33: Improve clarity of logging spew at high verbosity levels.
* #29: Support for using tilde in source directory path to reference home directory.
* Converted main source repository from Mercurial to Git.
* Updated dead links to Borg documentation.
* Require "prefix" in retention section when "archive_name_format" is set. This is to avoid
accidental pruning of archives with a different archive name format.
* Convert main source repository from Mercurial to Git.
* Update dead links to Borg documentation.

1.1.8
* #39: Fix to make /etc/borgmatic/config.yaml optional rather than required when using the default


+ 3
- 1
borgmatic/config/schema.yaml View File

@@ -94,7 +94,9 @@ map:
desc: |
Name of the archive. Borg placeholders can be used. See the output of
"borg help placeholders" for details. Default is
"{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}"
"{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}". If you specify this option, you must
also specify a prefix in the retention section to avoid accidental pruning of
archives with a different archive name format.
example: "{hostname}-documents-{now}"
retention:
desc: |


+ 27
- 15
borgmatic/config/validate.py View File

@@ -25,6 +25,31 @@ class Validation_error(ValueError):
self.config_filename = config_filename
self.error_messages = error_messages

def __str__(self):
'''
Render a validation error as a user-facing string.
'''
return 'An error occurred while parsing a configuration file at {}:\n'.format(
self.config_filename
) + '\n'.join(self.error_messages)


def apply_logical_validation(config_filename, parsed_configuration):
'''
Given a parsed and schematically valid configuration as a data structure of nested dicts (see
below), run through any additional logical validation checks. If there are any such validation
problems, raise a Validation_error.
'''
archive_name_format = parsed_configuration.get('storage', {}).get('archive_name_format')
prefix = parsed_configuration.get('retention', {}).get('prefix')

if archive_name_format and not prefix:
raise Validation_error(
config_filename, (
'If you provide an archive_name_format, you must also specify a retention prefix.',
)
)


def parse_configuration(config_filename, schema_filename):
'''
@@ -58,19 +83,6 @@ def parse_configuration(config_filename, schema_filename):
if validator.validation_errors:
raise Validation_error(config_filename, validator.validation_errors)

return parsed_result

apply_logical_validation(config_filename, parsed_result)

def display_validation_error(validation_error):
'''
Given a Validation_error, display its error messages to stderr.
'''
print(
'An error occurred while parsing a configuration file at {}:'.format(
validation_error.config_filename
),
file=sys.stderr,
)

for error in validation_error.error_messages:
print(error, file=sys.stderr)
return parsed_result

+ 0
- 7
borgmatic/tests/integration/config/test_validate.py View File

@@ -148,10 +148,3 @@ def test_parse_configuration_raises_for_validation_error():

with pytest.raises(module.Validation_error):
module.parse_configuration('config.yaml', 'schema.yaml')


def test_display_validation_error_does_not_raise():
flexmock(sys.modules['builtins']).should_receive('print')
error = module.Validation_error('config.yaml', ('oops', 'uh oh'))

module.display_validation_error(error)

+ 43
- 0
borgmatic/tests/unit/config/test_validate.py View File

@@ -0,0 +1,43 @@
import pytest

from borgmatic.config import validate as module


def test_validation_error_str_contains_error_messages_and_config_filename():
error = module.Validation_error('config.yaml', ('oops', 'uh oh'))

result = str(error)

assert 'config.yaml' in result
assert 'oops' in result
assert 'uh oh' in result


def test_apply_logical_validation_raises_if_archive_name_format_present_without_prefix():
with pytest.raises(module.Validation_error):
module.apply_logical_validation(
'config.yaml',
{
'storage': {'archive_name_format': '{hostname}-{now}'},
'retention': {'keep_daily': 7},
},
)


def test_apply_logical_validation_does_not_raise_if_archive_name_format_and_prefix_present():
module.apply_logical_validation(
'config.yaml',
{
'storage': {'archive_name_format': '{hostname}-{now}'},
'retention': {'prefix': '{hostname}-'},
},
)


def test_apply_logical_validation_does_not_raise_otherwise():
module.apply_logical_validation(
'config.yaml',
{
'retention': {'keep_secondly': 1000},
},
)

Loading…
Cancel
Save