When merging two configuration files, error gracefully if the two files do not adhere to the same format.
This commit is contained in:
parent
44f9ab95f9
commit
f60e97d5bf
3 changed files with 57 additions and 0 deletions
2
NEWS
2
NEWS
|
@ -4,6 +4,8 @@
|
||||||
variables available for explicit use in your commands. See the documentation for more
|
variables available for explicit use in your commands. See the documentation for more
|
||||||
information: https://torsion.org/borgmatic/docs/how-to/run-arbitrary-borg-commands/
|
information: https://torsion.org/borgmatic/docs/how-to/run-arbitrary-borg-commands/
|
||||||
* #719: Fix an error when running "borg key export" through borgmatic.
|
* #719: Fix an error when running "borg key export" through borgmatic.
|
||||||
|
* When merging two configuration files, error gracefully if the two files do not adhere to the same
|
||||||
|
format.
|
||||||
|
|
||||||
1.7.15
|
1.7.15
|
||||||
* #326: Add configuration options and command-line flags for backing up a database from one
|
* #326: Add configuration options and command-line flags for backing up a database from one
|
||||||
|
|
|
@ -225,6 +225,8 @@ def deep_merge_nodes(nodes):
|
||||||
The purpose of deep merging like this is to support, for instance, merging one borgmatic
|
The purpose of deep merging like this is to support, for instance, merging one borgmatic
|
||||||
configuration file into another for reuse, such that a configuration section ("retention",
|
configuration file into another for reuse, such that a configuration section ("retention",
|
||||||
etc.) does not completely replace the corresponding section in a merged file.
|
etc.) does not completely replace the corresponding section in a merged file.
|
||||||
|
|
||||||
|
Raise ValueError if a merge is implied using two incompatible types.
|
||||||
'''
|
'''
|
||||||
# Map from original node key/value to the replacement merged node. DELETED_NODE as a replacement
|
# Map from original node key/value to the replacement merged node. DELETED_NODE as a replacement
|
||||||
# node indications deletion.
|
# node indications deletion.
|
||||||
|
@ -239,6 +241,11 @@ def deep_merge_nodes(nodes):
|
||||||
|
|
||||||
# If the keys match and the values are different, we need to merge these two A and B nodes.
|
# If the keys match and the values are different, we need to merge these two A and B nodes.
|
||||||
if a_key.tag == b_key.tag and a_key.value == b_key.value and a_value != b_value:
|
if a_key.tag == b_key.tag and a_key.value == b_key.value and a_value != b_value:
|
||||||
|
if not type(a_value) is type(b_value):
|
||||||
|
raise ValueError(
|
||||||
|
f'Incompatible types found when trying to merge "{a_key.value}:" values across configuration files: {type(a_value).id} and {type(b_value).id}'
|
||||||
|
)
|
||||||
|
|
||||||
# Since we're merging into the B node, consider the A node a duplicate and remove it.
|
# Since we're merging into the B node, consider the A node a duplicate and remove it.
|
||||||
replaced_nodes[(a_key, a_value)] = DELETED_NODE
|
replaced_nodes[(a_key, a_value)] = DELETED_NODE
|
||||||
|
|
||||||
|
|
|
@ -702,6 +702,54 @@ def test_deep_merge_nodes_appends_colliding_sequence_values():
|
||||||
assert [item.value for item in options[0][1].value] == ['echo 1', 'echo 2', 'echo 3', 'echo 4']
|
assert [item.value for item in options[0][1].value] == ['echo 1', 'echo 2', 'echo 3', 'echo 4']
|
||||||
|
|
||||||
|
|
||||||
|
def test_deep_merge_nodes_errors_on_colliding_values_of_different_types():
|
||||||
|
node_values = [
|
||||||
|
(
|
||||||
|
module.ruamel.yaml.nodes.ScalarNode(tag='tag:yaml.org,2002:str', value='hooks'),
|
||||||
|
module.ruamel.yaml.nodes.MappingNode(
|
||||||
|
tag='tag:yaml.org,2002:map',
|
||||||
|
value=[
|
||||||
|
(
|
||||||
|
module.ruamel.yaml.nodes.ScalarNode(
|
||||||
|
tag='tag:yaml.org,2002:str', value='before_backup'
|
||||||
|
),
|
||||||
|
module.ruamel.yaml.nodes.ScalarNode(
|
||||||
|
tag='tag:yaml.org,2002:str', value='echo oopsie daisy'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
module.ruamel.yaml.nodes.ScalarNode(tag='tag:yaml.org,2002:str', value='hooks'),
|
||||||
|
module.ruamel.yaml.nodes.MappingNode(
|
||||||
|
tag='tag:yaml.org,2002:map',
|
||||||
|
value=[
|
||||||
|
(
|
||||||
|
module.ruamel.yaml.nodes.ScalarNode(
|
||||||
|
tag='tag:yaml.org,2002:str', value='before_backup'
|
||||||
|
),
|
||||||
|
module.ruamel.yaml.nodes.SequenceNode(
|
||||||
|
tag='tag:yaml.org,2002:seq',
|
||||||
|
value=[
|
||||||
|
module.ruamel.yaml.ScalarNode(
|
||||||
|
tag='tag:yaml.org,2002:str', value='echo 3'
|
||||||
|
),
|
||||||
|
module.ruamel.yaml.ScalarNode(
|
||||||
|
tag='tag:yaml.org,2002:str', value='echo 4'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
module.deep_merge_nodes(node_values)
|
||||||
|
|
||||||
|
|
||||||
def test_deep_merge_nodes_only_keeps_mapping_values_tagged_with_retain():
|
def test_deep_merge_nodes_only_keeps_mapping_values_tagged_with_retain():
|
||||||
node_values = [
|
node_values = [
|
||||||
(
|
(
|
||||||
|
|
Loading…
Reference in a new issue