Per-domain directories: changes apply on the next request, and one broken directory doesn't block reloads (DIR-17, DIR-21)

A write to x:Directory or Authentication reloads the directories at once,
here and across the cluster. A directory that fails to open is logged as
a warning against its id and becomes unavailable; before, it was a build
error, and any build error stopped every later reload from applying.

per_domain_directory_tests covers acceptance tests 1, 3, 4, 6, 7, 9, 11,
15 and 19 over SQL directories on SQLite files: each domain against its
own directory and the default, no fallback to internal passwords, a
directory answering for another directory's domain, aliases and groups
dropped, recipients through the directory and a 4xx while it's down, app
passwords while it's down, password changes refused and then allowed
after a move to the internal directory, linked directories, tenant
foreign keys, and changes taking effect without a reload.
This commit is contained in:
2026-09-19 10:42:33 -07:00
parent f72e3bb85c
commit 3158277b04
4 changed files with 504 additions and 3 deletions
+4 -2
View File
@@ -33,7 +33,9 @@ impl Directories {
let directory = match result {
Ok(directory) => directory,
Err(err) => {
bp.build_error(id, err.clone());
// inbuxa: DIR-21: logged against the directory, which becomes
// unavailable; the rest of the reload carries on
bp.build_warning(id, err.clone());
Directory::Unavailable(UnavailableDirectory::new(directory_type, err))
}
};
@@ -45,7 +47,7 @@ impl Directories {
match directories.get(&(directory_id.id() as u32)) {
Some(default_directory) => default_directory.clone().into(),
None => {
bp.build_error(
bp.build_warning(
ObjectType::Authentication.singleton(),
format!("Default directory with ID {} not found", directory_id),
);
+28 -1
View File
@@ -167,7 +167,7 @@ impl RegistrySet for Server {
update,
destroy,
};
match object_type {
let result = match object_type {
ObjectType::AddressBook
| ObjectType::Asn
| ObjectType::Authentication
@@ -915,7 +915,34 @@ impl RegistrySet for Server {
set.fail_all_destroy("Enterprise objects cannot be deleted");
Ok(set.into_response())
}
};
// inbuxa: DIR-17: a directory or the server default applies on the
// next request, here and on every node
if matches!(
object_type,
ObjectType::Directory | ObjectType::Authentication
) && let Ok(response) = &result
&& (!response.created.is_empty()
|| !response.updated.is_empty()
|| !response.destroyed.is_empty())
{
let change = common::ipc::RegistryChange::Reload(ObjectType::Directory);
match Box::pin(self.reload_registry(change)).await {
Ok(reload) if !reload.has_errors() => {
self.cluster_broadcast(common::ipc::BroadcastEvent::RegistryChange(change))
.await;
}
Ok(_) => trc::event!(
Registry(trc::RegistryEvent::BuildWarning),
Details = "Settings didn't reload after a directory change",
),
Err(err) => {
trc::error!(err.details("Failed to reload directories"));
}
}
}
result
}
}