Repair missing Store configuration before scanner setup

This commit is contained in:
Mario Fetka
2026-07-19 06:41:30 +02:00
parent 0b6df331be
commit fa08055778
2 changed files with 67 additions and 1 deletions
+36
View File
@@ -40,6 +40,17 @@ from bongo.configuration.storage import ConfigurationError # noqa: E402
class FakeStore:
def __init__(self):
self.saved = []
self.load_errors = []
def load_live(self):
if self.load_errors:
error = self.load_errors.pop(0)
if error:
raise error
return configurations()
def validate(self, _configurations):
return []
def save(self, configurations):
self.saved.append(copy.deepcopy(configurations))
@@ -83,6 +94,31 @@ class ConfigurationCliTest(unittest.TestCase):
self.assertEqual(len(store.saved), 2)
self.assertEqual(store.saved[1], original)
def test_missing_new_store_document_is_synchronized_before_save(self):
store = FakeStore()
store.load_errors = [
ConfigurationError("cannot read configuration acme"), None,
]
original = configurations()
with mock.patch.object(cli, "_reload_manager") as reload_manager, \
mock.patch.object(cli.time, "sleep") as sleep:
cli._activate_verified_scanners(
store, original, {"SpamAssassin"})
self.assertEqual(len(store.saved), 1)
self.assertEqual(reload_manager.call_count, 2)
sleep.assert_not_called()
def test_incomplete_store_sync_stops_before_scanner_activation(self):
store = FakeStore()
store.load_errors = [ConfigurationError("missing acme")] * 21
with mock.patch.object(cli, "_reload_manager"), \
mock.patch.object(cli.time, "sleep"):
with self.assertRaisesRegex(
ConfigurationError, "did not synchronize every"):
cli._activate_verified_scanners(
store, configurations(), {"SpamAssassin"})
self.assertEqual(store.saved, [])
if __name__ == "__main__":
unittest.main()
+31 -1
View File
@@ -28,6 +28,7 @@ import curses
import os
import subprocess
import sys
import time
from pathlib import Path
from .acme_dns import provision_acme_dns
@@ -35,7 +36,7 @@ from .model import enable_scanner_agents, errors
from .scanner_config import ScannerTransaction
from .spamassassin_rules import key_change_warnings
from .security_material import install_dns_tasks, provision_security_material
from .storage import ConfigurationError, ConfigurationStore
from .storage import ConfigurationError, ConfigurationStore, ValidationError
from .tui import AdminEditor, Cancelled, scanner_setup_wizard, setup_wizard
@@ -69,12 +70,41 @@ def _reload_manager() -> None:
"bongo-manager")
def _ensure_live_configuration(
store: ConfigurationStore, local_configuration: dict) -> None:
"""Ask the manager to backfill Store documents added after setup."""
invalid = errors(store.validate(local_configuration))
if invalid:
raise ValidationError(invalid)
initial_error = None
try:
store.load_live()
return
except ConfigurationError as error:
initial_error = error
_reload_manager()
last_error = initial_error
for attempt in range(20):
try:
store.load_live()
return
except ConfigurationError as error:
last_error = error
if attempt != 19:
time.sleep(0.1)
raise ConfigurationError(
"bongo-manager did not synchronize every local configuration "
f"document into the Store: {last_error}") from initial_error
def _activate_verified_scanners(
store: ConfigurationStore,
originals: dict,
scanners: set[str]) -> None:
if not scanners:
return
_ensure_live_configuration(store, originals)
updated = copy.deepcopy(originals)
enable_scanner_agents(updated, scanners)
store.save(updated)