use std::sync::Arc;
use tracing::{error, info, warn};
use acme_proxy_core::config::Config;
use super::Assembly;
use super::generation::{announce_profile, prepare_reload, publish_reload};
use super::sockets::{Role, announce_admin_listener, announce_metrics_listener};
pub(super) struct Cells {
pub(super) acme_router:
tokio::sync::watch::Sender<axum::routing::RouterIntoService<axum::body::Body>>,
pub(super) admin_router:
tokio::sync::watch::Sender<axum::routing::RouterIntoService<axum::body::Body>>,
pub(super) job_registry: tokio::sync::watch::Sender<Arc<acme_proxy_jobs::jobs::JobRegistry>>,
pub(super) jobs: tokio::sync::watch::Sender<Arc<acme_proxy_core::config::JobsConfig>>,
pub(super) acme: acme_proxy_net::listener::ListenerHandle,
pub(super) admin: acme_proxy_net::listener::ListenerHandle,
pub(super) metrics: acme_proxy_net::listener::ListenerHandle,
}
pub(super) async fn supervise_reloads(
roles: crate::RoleSet,
mut reloads: crate::reload::Reloads,
mut config: Arc<Config>,
mut resolved: Vec<acme_proxy_core::config::ProfileConfig>,
assembly: Arc<Assembly>,
cells: Cells,
mut logins: Option<Arc<acme_proxy_admin::webadmin::LoginLimiter>>,
) {
let mut generation: u64 = 1;
while let Some(request) = reloads.recv().await {
let started = std::time::Instant::now();
info!(
event = "server_config_reload_requested",
outcome = "progress",
generation = generation,
);
let outcome = {
let config = config.clone();
let resolved = resolved.clone();
let assembly = assembly.clone();
let logins = logins.clone();
tokio::task::spawn_blocking(move || {
prepare_reload(roles, &config, &resolved, &assembly, logins.as_deref())
})
.await
.unwrap_or_else(|error| {
Err(crate::reload::ReloadError::Build(format!(
"the reload build task did not finish: {error}"
)))
})
};
if let Ok(prepared) = &outcome
&& roles.has(super::ProcessRole::Worker)
{
super::store_first_crls(prepared.signers()).await;
}
let outcome = outcome.map(|prepared| {
publish_reload(
prepared,
&config,
&assembly,
&cells,
generation + 1,
started,
)
});
match outcome {
Ok(reloaded) => {
let report = reloaded.report;
config = reloaded.config;
resolved = reloaded.resolved;
logins = reloaded.logins;
generation = report.generation;
info!(
event = "server_config_reloaded",
outcome = "success",
generation = report.generation,
profiles = ?report.profiles,
job_kinds = ?report.job_kinds,
tls_reloaded = report.tls_reloaded,
admin_tls_reloaded = report.admin_tls_reloaded,
listeners_rebound = ?report.listeners_rebound,
logging_reloaded = report.logging_reloaded,
duration_ms = acme_proxy_core::logfields::millis(report.duration),
);
for (role, address) in reloaded.opened {
match role {
Role::Acme => info!(
event = "server_listening",
outcome = "success",
bind_address = %address,
protocol = if config.server.tls.enabled { "https" } else { "http" }
),
Role::Admin => {
announce_admin_listener(&config, &assembly.database, &address).await;
}
Role::Metrics => announce_metrics_listener(&address),
}
}
for profile in reloaded.mounted {
announce_profile(&profile).await;
}
if let Some(respond) = request.respond {
let _ = respond.send(Ok(report));
}
}
Err(error) => {
match &error {
crate::reload::ReloadError::Frozen { .. } => warn!(
event = "server_config_reload_refused",
outcome = "failure",
generation = generation,
reason = error.kind(),
error = %error,
),
_ => error!(
event = "server_config_reload_failed",
outcome = "failure",
generation = generation,
reason = error.kind(),
error = %error,
),
}
if let Some(respond) = request.respond {
let _ = respond.send(Err(error));
}
}
}
}
}