mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-08-02 22:53:01 +02:00
Fix for the "bootstrap" action ignoring the "--verbosity" flag when no configuration is present (#1336).
This commit is contained in:
@@ -18,6 +18,8 @@
|
||||
segment compaction.
|
||||
* #1334: Add the "CAP_FOWNER" capability to "CapabilityBoundingSet" in the sample systemd service,
|
||||
so that Borg can open source files without changing file access times.
|
||||
* #1336: Fix for the "bootstrap" action ignoring the "--verbosity" flag when no configuration is
|
||||
present.
|
||||
* Fix a bug in which the "compact" action does not pass a compact threshold of zero to Borg.
|
||||
|
||||
2.1.6
|
||||
|
||||
@@ -665,6 +665,13 @@ def load_configurations(config_filenames, arguments, overrides=None, resolve_env
|
||||
config_paths = set()
|
||||
logs = []
|
||||
|
||||
# As a special case for the "bootstrap" action, parse configuration for a non-existent
|
||||
# configuration file with None for a filename. This sets any command-line arguments into an
|
||||
# empty configuration dict, so for instance a "--verbosity" flag gets used even if there is no
|
||||
# configuration file yet.
|
||||
if 'bootstrap' in arguments and not config_filenames:
|
||||
config_filenames = (None,)
|
||||
|
||||
# Parse and load each configuration file.
|
||||
for config_filename in config_filenames:
|
||||
logs.extend(
|
||||
|
||||
@@ -108,7 +108,8 @@ def parse_configuration(
|
||||
rendition of JSON Schema format, arguments as dict from action name to argparse.Namespace, a
|
||||
sequence of configuration file override strings in the form of "option.suboption=value", and
|
||||
whether to resolve environment variables, return the parsed configuration as a data structure of
|
||||
nested dicts and lists corresponding to the schema. Example return value.
|
||||
nested dicts and lists corresponding to the schema. Apply the given arguments to the config,
|
||||
modifying it based on the values of those arguments.
|
||||
|
||||
Example return value:
|
||||
|
||||
@@ -122,13 +123,21 @@ def parse_configuration(
|
||||
Also return a set of loaded configuration paths and a sequence of logging.LogRecord instances
|
||||
containing any warnings about the configuration.
|
||||
|
||||
If the given config filename is None, then create a configuration dict from whole cloth,
|
||||
applying the given arguments to it. This is useful for the "bootstrap" action, for which
|
||||
configuration may not yet exist.
|
||||
|
||||
Raise FileNotFoundError if the file does not exist, PermissionError if the user does not
|
||||
have permissions to read the file, or Validation_error if the config does not match the schema.
|
||||
'''
|
||||
config_paths = set()
|
||||
|
||||
try:
|
||||
config = load.load_configuration(config_filename, config_paths)
|
||||
config = (
|
||||
load.load_configuration(config_filename, config_paths)
|
||||
if config_filename
|
||||
else {'repositories': []}
|
||||
)
|
||||
schema = load.load_configuration(schema_filename)
|
||||
except (ruamel.yaml.error.YAMLError, RecursionError) as error:
|
||||
raise Validation_error(config_filename, (str(error),))
|
||||
|
||||
@@ -15,14 +15,17 @@ def test_schema_filename_returns_plausible_path():
|
||||
assert schema_path.endswith('/schema.yaml')
|
||||
|
||||
|
||||
def mock_config_and_schema(config_yaml, schema_yaml=None):
|
||||
def mock_config_and_schema(config_yaml=None, schema_yaml=None):
|
||||
'''
|
||||
Set up mocks for the given config config YAML string and the schema YAML string, or the default
|
||||
schema if no schema is provided. The idea is that that the code under test consumes these mocks
|
||||
when parsing the configuration.
|
||||
Set up mocks for the given config config YAML string (if provided) and the schema YAML string or
|
||||
the default schema if no schema is provided. The idea is that that the code under test consumes
|
||||
these mocks when parsing the configuration.
|
||||
'''
|
||||
config_stream = io.StringIO(config_yaml)
|
||||
config_stream.name = 'config.yaml'
|
||||
if config_yaml is None:
|
||||
config_stream = None
|
||||
else:
|
||||
config_stream = io.StringIO(config_yaml)
|
||||
config_stream.name = 'config.yaml'
|
||||
|
||||
if schema_yaml is None:
|
||||
schema_stream = open(module.schema_filename())
|
||||
@@ -81,6 +84,24 @@ def test_parse_configuration_transforms_file_into_mapping():
|
||||
assert logs == []
|
||||
|
||||
|
||||
def test_parse_configuration_with_none_config_filename_creates_configuration_from_whole_cloth():
|
||||
mock_config_and_schema()
|
||||
|
||||
config, config_paths, logs = module.parse_configuration(
|
||||
None,
|
||||
'/tmp/schema.yaml',
|
||||
arguments={'global': flexmock(verbosity=2)},
|
||||
)
|
||||
|
||||
assert config == {
|
||||
'bootstrap': {},
|
||||
'repositories': [],
|
||||
'verbosity': 2,
|
||||
}
|
||||
assert config_paths == set()
|
||||
assert logs == []
|
||||
|
||||
|
||||
def test_parse_configuration_passes_through_quoted_punctuation():
|
||||
escaped_punctuation = string.punctuation.replace('\\', r'\\').replace('"', r'\"')
|
||||
|
||||
|
||||
@@ -1801,6 +1801,50 @@ def test_load_configurations_logs_critical_for_parse_error():
|
||||
assert max(log.levelno for log in logs) == logging.CRITICAL
|
||||
|
||||
|
||||
def test_load_configurations_with_bootstrap_action_and_no_configuration_file_creates_configuration_from_whole_cloth():
|
||||
configuration = flexmock()
|
||||
test_expected_logs = [flexmock(), flexmock()]
|
||||
flexmock(module.validate).should_receive('parse_configuration').and_return(
|
||||
configuration,
|
||||
[None],
|
||||
test_expected_logs,
|
||||
)
|
||||
|
||||
configs, config_paths, logs = tuple(
|
||||
module.load_configurations(
|
||||
(),
|
||||
arguments={'bootstrap': flexmock()},
|
||||
resolve_env=False,
|
||||
),
|
||||
)
|
||||
|
||||
assert configs == {None: configuration}
|
||||
assert config_paths == [None]
|
||||
assert logs
|
||||
|
||||
|
||||
def test_load_configurations_with_bootstrap_action_and_existing_configuration_file_uses_it():
|
||||
configuration = flexmock()
|
||||
test_expected_logs = [flexmock(), flexmock()]
|
||||
flexmock(module.validate).should_receive('parse_configuration').and_return(
|
||||
configuration,
|
||||
['/tmp/test.yaml'],
|
||||
test_expected_logs,
|
||||
)
|
||||
|
||||
configs, config_paths, logs = tuple(
|
||||
module.load_configurations(
|
||||
('test.yaml',),
|
||||
arguments={'bootstrap': flexmock()},
|
||||
resolve_env=False,
|
||||
),
|
||||
)
|
||||
|
||||
assert configs == {'test.yaml': configuration}
|
||||
assert config_paths == ['/tmp/test.yaml']
|
||||
assert logs
|
||||
|
||||
|
||||
def test_log_record_does_not_raise():
|
||||
module.log_record(levelno=1, foo='bar', baz='quux')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user