Align behavior with vault concerning config imports
Marco Ricci

Marco Ricci commited on 2024-10-13 14:39:43
Zeige 1 geänderte Dateien mit 21 Einfügungen und 1 Löschungen.


When importing a configuration, the original vault(1) does not replace
its configuration as a whole with the supplied one.  Instead, each
"section" of the new configuration – the global settings, and each named
service's settings – replaces the corresponding section in the old
configuration, if any.  Any sections not mentioned in the new
configuration are left untouched; in particular, existing service
settings are *kept* if they are not mentioned in the new configuration.

`derivepassphrase` uses a simpler, database-like dump/restore model,
overwriting the whole old configuration with the new one.  As
a consequence, old services settings are *dropped* if they are not
mentioned in the new configuration.  This behavior is a visible
deviation from vault(1), and shall thus be removed.

This commit contains the necessary changes to the import machinery to
correctly calculate the new, merged configuration.  Surprisingly, this
already passes all tests, which is more a sign that our tests are
incomplete rather than that the code is robust against this failure
type.  So, in upcoming commits we will introduce functional tests for
the config import/merging machinery second, and bugfixes for other
things we uncovered while writing these functional tests first.
... ...
@@ -1451,7 +1451,27 @@ def derivepassphrase_vault(  # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915
1451 1451
                 cast(dict[str, Any], value),
1452 1452
                 form=form,
1453 1453
             )
1454
-        put_config(maybe_config)
1454
+        configuration = get_config()
1455
+        merged_config: collections.ChainMap[str, Any] = collections.ChainMap(
1456
+            {
1457
+                'services': collections.ChainMap(
1458
+                    maybe_config['services'],
1459
+                    configuration['services'],
1460
+                ),
1461
+            },
1462
+            {'global': maybe_config['global']}
1463
+            if 'global' in maybe_config
1464
+            else {},
1465
+            {'global': configuration['global']}
1466
+            if 'global' in configuration
1467
+            else {},
1468
+        )
1469
+        new_config = {
1470
+            k: dict(v) if isinstance(v, collections.ChainMap) else v
1471
+            for k, v in sorted(merged_config.items())
1472
+        }
1473
+        assert _types.is_vault_config(new_config)
1474
+        put_config(new_config)
1455 1475
     elif export_settings:
1456 1476
         configuration = get_config()
1457 1477
         try:
1458 1478