Repair missing Store configuration before scanner setup
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user