This commit is contained in:
Mateusz Gruszczyński committed 2026-10-04 11:15:58 +02:00
1 parent 87e571c170
commit fa7abc9e53
308 files changed
+49746 -50237

No files matched your search

+82
View File
@@ -0,0 +1,82 @@
impl App {
/// Re-read configured public certificate/key files and publish a new
/// immutable TLS/routing snapshot without changing desired configuration
/// or its SQLite revision. Existing connections continue on their current
/// TLS session; new H1/H2/H3 handshakes use the refreshed certificates.
pub async fn reload_certificates(self: &Arc<Self>) -> Result<u64, ApiError> {
self.reload_certificate_snapshot(None).await
}
/// Reload an externally managed/manual certificate by id. The immutable TLS
/// resolver is rebuilt as one validated snapshot, so the requested pair is
/// re-read atomically with the other configured pairs while desired
/// configuration and its revision remain unchanged.
pub async fn reload_certificate(self: &Arc<Self>, certificate_id: &str) -> Result<u64, ApiError> {
if !crate::config::valid_id(certificate_id) {
return Err(ApiError(StatusCode::NOT_FOUND, "certificate not found".into()));
}
self.reload_certificate_snapshot(Some(certificate_id)).await
}
async fn reload_certificate_snapshot(
self: &Arc<Self>,
certificate_id: Option<&str>,
) -> Result<u64, ApiError> {
let _lock = self.update_lock.lock().await;
let previous = self.config.load_full();
let desired = previous.envelope.config.clone();
if let Some(certificate_id) = certificate_id {
let certificate = desired
.tls
.certificates
.iter()
.find(|certificate| certificate.id == certificate_id)
.ok_or_else(|| ApiError(StatusCode::NOT_FOUND, "certificate not found".into()))?;
if certificate.issuer.is_some() {
return Err(ApiError(
StatusCode::CONFLICT,
"managed certificates are reloaded by the renewal pipeline".into(),
));
}
}
let desired_vhost_count = desired.vhosts.len();
let observed = self.docker.snapshot().await;
let policy = self.policy.clone();
let defaults = self.startup_listeners;
let active_listeners = self.active_listeners.clone();
let revision = previous.envelope.revision;
let prepared = tokio::task::spawn_blocking(move || {
let effective = crate::docker::effective_config(desired, &observed)?;
let mut prepared = config::compile_with_active(
effective,
revision,
&policy,
defaults,
Some(&previous),
&active_listeners,
)?;
prepared
.envelope
.config
.vhosts
.truncate(desired_vhost_count);
Ok::<_, anyhow::Error>(prepared)
})
.await
.map_err(|_| {
ApiError(
StatusCode::INTERNAL_SERVER_ERROR,
"certificate reload task failed".into(),
)
})?
.map_err(|error| ApiError(StatusCode::UNPROCESSABLE_ENTITY, format!("{error:#}")))?;
self.config.store(Arc::new(prepared));
if let Some(certificate_id) = certificate_id {
tracing::info!(revision, certificate_id, "configured certificate reloaded from disk");
} else {
tracing::info!(revision, "configured certificates reloaded from disk");
}
Ok(revision)
}
}