Remove legacy configuration parsing code, no longer needed with upgrade-borgmatic-config gone (#529).
This commit is contained in:
parent
37a0a0c421
commit
b9a11e860d
4 changed files with 0 additions and 376 deletions
|
@ -46,8 +46,6 @@ from borgmatic.verbosity import verbosity_to_log_level
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
LEGACY_CONFIG_PATH = '/etc/borgmatic/config'
|
||||
|
||||
|
||||
def run_configuration(config_filename, config, arguments):
|
||||
'''
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
from collections import OrderedDict, namedtuple
|
||||
from configparser import RawConfigParser
|
||||
|
||||
Section_format = namedtuple('Section_format', ('name', 'options'))
|
||||
Config_option = namedtuple('Config_option', ('name', 'value_type', 'required'))
|
||||
|
||||
|
||||
def option(name, value_type=str, required=True):
|
||||
'''
|
||||
Given a config file option name, an expected type for its value, and whether it's required,
|
||||
return a Config_option capturing that information.
|
||||
'''
|
||||
return Config_option(name, value_type, required)
|
||||
|
||||
|
||||
CONFIG_FORMAT = (
|
||||
Section_format(
|
||||
'location',
|
||||
(
|
||||
option('source_directories'),
|
||||
option('one_file_system', value_type=bool, required=False),
|
||||
option('remote_path', required=False),
|
||||
option('repository'),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'storage',
|
||||
(
|
||||
option('encryption_passphrase', required=False),
|
||||
option('compression', required=False),
|
||||
option('umask', required=False),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'retention',
|
||||
(
|
||||
option('keep_within', required=False),
|
||||
option('keep_hourly', int, required=False),
|
||||
option('keep_daily', int, required=False),
|
||||
option('keep_weekly', int, required=False),
|
||||
option('keep_monthly', int, required=False),
|
||||
option('keep_yearly', int, required=False),
|
||||
option('prefix', required=False),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'consistency', (option('checks', required=False), option('check_last', required=False))
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def validate_configuration_format(parser, config_format):
|
||||
'''
|
||||
Given an open RawConfigParser and an expected config file format, validate that the parsed
|
||||
configuration file has the expected sections, that any required options are present in those
|
||||
sections, and that there aren't any unexpected options.
|
||||
|
||||
A section is required if any of its contained options are required.
|
||||
|
||||
Raise ValueError if anything is awry.
|
||||
'''
|
||||
section_names = set(parser.sections())
|
||||
required_section_names = tuple(
|
||||
section.name
|
||||
for section in config_format
|
||||
if any(option.required for option in section.options)
|
||||
)
|
||||
|
||||
unknown_section_names = section_names - set(
|
||||
section_format.name for section_format in config_format
|
||||
)
|
||||
if unknown_section_names:
|
||||
raise ValueError(f"Unknown config sections found: {', '.join(unknown_section_names)}")
|
||||
|
||||
missing_section_names = set(required_section_names) - section_names
|
||||
if missing_section_names:
|
||||
raise ValueError(f"Missing config sections: {', '.join(missing_section_names)}")
|
||||
|
||||
for section_format in config_format:
|
||||
if section_format.name not in section_names:
|
||||
continue
|
||||
|
||||
option_names = parser.options(section_format.name)
|
||||
expected_options = section_format.options
|
||||
|
||||
unexpected_option_names = set(option_names) - set(
|
||||
option.name for option in expected_options
|
||||
)
|
||||
|
||||
if unexpected_option_names:
|
||||
raise ValueError(
|
||||
f"Unexpected options found in config section {section_format.name}: {', '.join(sorted(unexpected_option_names))}",
|
||||
)
|
||||
|
||||
missing_option_names = tuple(
|
||||
option.name
|
||||
for option in expected_options
|
||||
if option.required
|
||||
if option.name not in option_names
|
||||
)
|
||||
|
||||
if missing_option_names:
|
||||
raise ValueError(
|
||||
f"Required options missing from config section {section_format.name}: {', '.join(missing_option_names)}",
|
||||
)
|
||||
|
||||
|
||||
def parse_section_options(parser, section_format):
|
||||
'''
|
||||
Given an open RawConfigParser and an expected section format, return the option values from that
|
||||
section as a dict mapping from option name to value. Omit those options that are not present in
|
||||
the parsed options.
|
||||
|
||||
Raise ValueError if any option values cannot be coerced to the expected Python data type.
|
||||
'''
|
||||
type_getter = {str: parser.get, int: parser.getint, bool: parser.getboolean}
|
||||
|
||||
return OrderedDict(
|
||||
(option.name, type_getter[option.value_type](section_format.name, option.name))
|
||||
for option in section_format.options
|
||||
if parser.has_option(section_format.name, option.name)
|
||||
)
|
||||
|
||||
|
||||
def parse_configuration(config_filename, config_format):
|
||||
'''
|
||||
Given a config filename and an expected config file format, return the parsed configuration
|
||||
as a namedtuple with one attribute for each parsed section.
|
||||
|
||||
Raise IOError if the file cannot be read, or ValueError if the format is not as expected.
|
||||
'''
|
||||
parser = RawConfigParser()
|
||||
if not parser.read(config_filename):
|
||||
raise ValueError(f'Configuration file cannot be opened: {config_filename}')
|
||||
|
||||
validate_configuration_format(parser, config_format)
|
||||
|
||||
# Describes a parsed configuration, where each attribute is the name of a configuration file
|
||||
# section and each value is a dict of that section's parsed options.
|
||||
Parsed_config = namedtuple(
|
||||
'Parsed_config', (section_format.name for section_format in config_format)
|
||||
)
|
||||
|
||||
return Parsed_config(
|
||||
*(parse_section_options(parser, section_format) for section_format in config_format)
|
||||
)
|
|
@ -1,18 +0,0 @@
|
|||
import string
|
||||
from collections import OrderedDict
|
||||
from io import StringIO
|
||||
|
||||
from borgmatic.config import legacy as module
|
||||
|
||||
|
||||
def test_parse_section_options_with_punctuation_should_return_section_options():
|
||||
parser = module.RawConfigParser()
|
||||
parser.read_file(StringIO(f'[section]\nfoo: {string.punctuation}\n'))
|
||||
|
||||
section_format = module.Section_format(
|
||||
'section', (module.Config_option('foo', str, required=True),)
|
||||
)
|
||||
|
||||
config = module.parse_section_options(parser, section_format)
|
||||
|
||||
assert config == OrderedDict((('foo', string.punctuation),))
|
|
@ -1,210 +0,0 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
import pytest
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.config import legacy as module
|
||||
|
||||
|
||||
def test_option_should_create_config_option():
|
||||
option = module.option('name', bool, required=False)
|
||||
|
||||
assert option == module.Config_option('name', bool, False)
|
||||
|
||||
|
||||
def test_option_should_create_config_option_with_defaults():
|
||||
option = module.option('name')
|
||||
|
||||
assert option == module.Config_option('name', str, True)
|
||||
|
||||
|
||||
def test_validate_configuration_format_with_valid_config_should_not_raise():
|
||||
parser = flexmock()
|
||||
parser.should_receive('sections').and_return(('section', 'other'))
|
||||
parser.should_receive('options').with_args('section').and_return(('stuff',))
|
||||
parser.should_receive('options').with_args('other').and_return(('such',))
|
||||
config_format = (
|
||||
module.Section_format(
|
||||
'section', options=(module.Config_option('stuff', str, required=True),)
|
||||
),
|
||||
module.Section_format('other', options=(module.Config_option('such', str, required=True),)),
|
||||
)
|
||||
|
||||
module.validate_configuration_format(parser, config_format)
|
||||
|
||||
|
||||
def test_validate_configuration_format_with_missing_required_section_should_raise():
|
||||
parser = flexmock()
|
||||
parser.should_receive('sections').and_return(('section',))
|
||||
config_format = (
|
||||
module.Section_format(
|
||||
'section', options=(module.Config_option('stuff', str, required=True),)
|
||||
),
|
||||
# At least one option in this section is required, so the section is required.
|
||||
module.Section_format(
|
||||
'missing',
|
||||
options=(
|
||||
module.Config_option('such', str, required=False),
|
||||
module.Config_option('things', str, required=True),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.validate_configuration_format(parser, config_format)
|
||||
|
||||
|
||||
def test_validate_configuration_format_with_missing_optional_section_should_not_raise():
|
||||
parser = flexmock()
|
||||
parser.should_receive('sections').and_return(('section',))
|
||||
parser.should_receive('options').with_args('section').and_return(('stuff',))
|
||||
config_format = (
|
||||
module.Section_format(
|
||||
'section', options=(module.Config_option('stuff', str, required=True),)
|
||||
),
|
||||
# No options in the section are required, so the section is optional.
|
||||
module.Section_format(
|
||||
'missing',
|
||||
options=(
|
||||
module.Config_option('such', str, required=False),
|
||||
module.Config_option('things', str, required=False),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
module.validate_configuration_format(parser, config_format)
|
||||
|
||||
|
||||
def test_validate_configuration_format_with_unknown_section_should_raise():
|
||||
parser = flexmock()
|
||||
parser.should_receive('sections').and_return(('section', 'extra'))
|
||||
config_format = (module.Section_format('section', options=()),)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.validate_configuration_format(parser, config_format)
|
||||
|
||||
|
||||
def test_validate_configuration_format_with_missing_required_option_should_raise():
|
||||
parser = flexmock()
|
||||
parser.should_receive('sections').and_return(('section',))
|
||||
parser.should_receive('options').with_args('section').and_return(('option',))
|
||||
config_format = (
|
||||
module.Section_format(
|
||||
'section',
|
||||
options=(
|
||||
module.Config_option('option', str, required=True),
|
||||
module.Config_option('missing', str, required=True),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.validate_configuration_format(parser, config_format)
|
||||
|
||||
|
||||
def test_validate_configuration_format_with_missing_optional_option_should_not_raise():
|
||||
parser = flexmock()
|
||||
parser.should_receive('sections').and_return(('section',))
|
||||
parser.should_receive('options').with_args('section').and_return(('option',))
|
||||
config_format = (
|
||||
module.Section_format(
|
||||
'section',
|
||||
options=(
|
||||
module.Config_option('option', str, required=True),
|
||||
module.Config_option('missing', str, required=False),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
module.validate_configuration_format(parser, config_format)
|
||||
|
||||
|
||||
def test_validate_configuration_format_with_extra_option_should_raise():
|
||||
parser = flexmock()
|
||||
parser.should_receive('sections').and_return(('section',))
|
||||
parser.should_receive('options').with_args('section').and_return(('option', 'extra'))
|
||||
config_format = (
|
||||
module.Section_format(
|
||||
'section', options=(module.Config_option('option', str, required=True),)
|
||||
),
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.validate_configuration_format(parser, config_format)
|
||||
|
||||
|
||||
def test_parse_section_options_should_return_section_options():
|
||||
parser = flexmock()
|
||||
parser.should_receive('get').with_args('section', 'foo').and_return('value')
|
||||
parser.should_receive('getint').with_args('section', 'bar').and_return(1)
|
||||
parser.should_receive('getboolean').never()
|
||||
parser.should_receive('has_option').with_args('section', 'foo').and_return(True)
|
||||
parser.should_receive('has_option').with_args('section', 'bar').and_return(True)
|
||||
|
||||
section_format = module.Section_format(
|
||||
'section',
|
||||
(
|
||||
module.Config_option('foo', str, required=True),
|
||||
module.Config_option('bar', int, required=True),
|
||||
),
|
||||
)
|
||||
|
||||
config = module.parse_section_options(parser, section_format)
|
||||
|
||||
assert config == OrderedDict((('foo', 'value'), ('bar', 1)))
|
||||
|
||||
|
||||
def test_parse_section_options_for_missing_section_should_return_empty_dict():
|
||||
parser = flexmock()
|
||||
parser.should_receive('get').never()
|
||||
parser.should_receive('getint').never()
|
||||
parser.should_receive('getboolean').never()
|
||||
parser.should_receive('has_option').with_args('section', 'foo').and_return(False)
|
||||
parser.should_receive('has_option').with_args('section', 'bar').and_return(False)
|
||||
|
||||
section_format = module.Section_format(
|
||||
'section',
|
||||
(
|
||||
module.Config_option('foo', str, required=False),
|
||||
module.Config_option('bar', int, required=False),
|
||||
),
|
||||
)
|
||||
|
||||
config = module.parse_section_options(parser, section_format)
|
||||
|
||||
assert config == OrderedDict()
|
||||
|
||||
|
||||
def insert_mock_parser():
|
||||
parser = flexmock()
|
||||
parser.should_receive('read').and_return([flexmock()])
|
||||
module.RawConfigParser = lambda: parser
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def test_parse_configuration_should_return_section_configs():
|
||||
parser = insert_mock_parser()
|
||||
config_format = (flexmock(name='items'), flexmock(name='things'))
|
||||
mock_module = flexmock(module)
|
||||
mock_module.should_receive('validate_configuration_format').with_args(
|
||||
parser, config_format
|
||||
).once()
|
||||
mock_section_configs = (flexmock(), flexmock())
|
||||
|
||||
for section_format, section_config in zip(config_format, mock_section_configs):
|
||||
mock_module.should_receive('parse_section_options').with_args(
|
||||
parser, section_format
|
||||
).and_return(section_config).once()
|
||||
|
||||
parsed_config = module.parse_configuration('filename', config_format)
|
||||
|
||||
assert parsed_config == type(parsed_config)(*mock_section_configs)
|
||||
|
||||
|
||||
def test_parse_configuration_with_file_open_error_should_raise():
|
||||
parser = insert_mock_parser()
|
||||
parser.should_receive('read').and_return([])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.parse_configuration('filename', config_format=flexmock())
|
Loading…
Reference in a new issue