v0.25.1
This commit is contained in:
1 parent
87e571c170
commit
fa7abc9e53
308 files changed
+49746
-50237
No files matched your search
@@ -0,0 +1,130 @@
|
||||
impl App {
|
||||
pub fn start_update_check(self: &Arc<Self>, config: crate::update_check::Config) {
|
||||
self.software_update.configure(&config);
|
||||
self.tunnels.spawn(crate::update_check::poll_loop(
|
||||
self.software_update.clone(),
|
||||
config,
|
||||
self.shutdown.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
/// Drain detached writers after the listeners and their handlers have stopped.
|
||||
pub async fn drain_updates(&self) {
|
||||
let _all_slots = self.update_slots.clone().acquire_many_owned(8).await;
|
||||
}
|
||||
/// The detached task completes persistence + publication even if the HTTP caller disconnects.
|
||||
pub async fn update(self: &Arc<Self>, update: Update) -> Result<config::Envelope, ApiError> {
|
||||
self.update_as(update, "local".into()).await
|
||||
}
|
||||
pub async fn update_as(
|
||||
self: &Arc<Self>,
|
||||
update: Update,
|
||||
actor: String,
|
||||
) -> Result<config::Envelope, ApiError> {
|
||||
let slot = self.update_slots.clone().try_acquire_owned().map_err(|_| {
|
||||
ApiError(
|
||||
StatusCode::TOO_MANY_REQUESTS,
|
||||
"configuration update queue is full".into(),
|
||||
)
|
||||
})?;
|
||||
let expected_revision = update.expected_revision;
|
||||
let revision = expected_revision
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| ApiError(StatusCode::BAD_REQUEST, "revision overflow".into()))?;
|
||||
let policy = self.policy.clone();
|
||||
let defaults = self.startup_listeners;
|
||||
let previous = self.config.load_full();
|
||||
let previous_config = previous.envelope.config.clone();
|
||||
let active_listeners = self.active_listeners.clone();
|
||||
let observed = self.docker.snapshot().await;
|
||||
let desired_vhost_count = update.config.vhosts.len();
|
||||
let desired_config = update.config;
|
||||
self.startup_locks
|
||||
.validate(&desired_config, self.policy.admin_addr)
|
||||
.map_err(|error| ApiError(StatusCode::UNPROCESSABLE_ENTITY, format!("{error:#}")))?;
|
||||
crate::tls::validate_certificate_removals(&previous_config, &desired_config)
|
||||
.map_err(|error| ApiError(StatusCode::UNPROCESSABLE_ENTITY, format!("{error:#}")))?;
|
||||
let (prepared, invalidation, slot) = tokio::task::spawn_blocking(move || {
|
||||
let effective = crate::docker::effective_config(desired_config, &observed)?;
|
||||
let mut prepared = config::compile_with_active(
|
||||
effective,
|
||||
revision,
|
||||
&policy,
|
||||
defaults,
|
||||
Some(&previous),
|
||||
&active_listeners,
|
||||
)?;
|
||||
prepared
|
||||
.envelope
|
||||
.config
|
||||
.vhosts
|
||||
.truncate(desired_vhost_count);
|
||||
let invalidation = cache_invalidation(&previous_config, &prepared.envelope.config);
|
||||
Ok::<_, anyhow::Error>((prepared, invalidation, slot))
|
||||
})
|
||||
.await
|
||||
.map_err(|_| {
|
||||
ApiError(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"configuration validation task failed".into(),
|
||||
)
|
||||
})?
|
||||
.map_err(|e| ApiError(StatusCode::UNPROCESSABLE_ENTITY, format!("{e:#}")))?;
|
||||
let this = self.clone();
|
||||
tokio::spawn(async move {
|
||||
let _slot = slot;
|
||||
let _lock = this.update_lock.lock().await;
|
||||
if this.config.load().envelope.revision != expected_revision {
|
||||
return Err(ApiError(
|
||||
StatusCode::CONFLICT,
|
||||
"configuration changed; reload before saving".into(),
|
||||
));
|
||||
}
|
||||
match this
|
||||
.store
|
||||
.replace_as(expected_revision, &prepared.envelope.config, &actor)
|
||||
.await
|
||||
{
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
return Err(ApiError(
|
||||
StatusCode::CONFLICT,
|
||||
"stored revision differs; external SQLite writes are unsupported".into(),
|
||||
))
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!(%error, "configuration persistence failed");
|
||||
return Err(ApiError(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"configuration was not saved".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// No await between successful commit and publication. Cache identity is
|
||||
// intentionally decoupled from the configuration revision, so unrelated
|
||||
// control-plane saves do not invalidate hot/cold objects.
|
||||
let envelope = prepared.envelope.clone();
|
||||
match invalidation {
|
||||
CacheInvalidation::All(cache_revision) => {
|
||||
this.cache.purge(Some(cache_revision));
|
||||
}
|
||||
CacheInvalidation::None => {}
|
||||
}
|
||||
this.cache.configure(envelope.config.cache.clone());
|
||||
this.rate_limiter.reset();
|
||||
this.logs.configure(envelope.config.logging.clone());
|
||||
crate::logging::configure_error_log(envelope.config.error_log.clone());
|
||||
this.config.store(Arc::new(prepared));
|
||||
tracing::info!(revision, "configuration applied");
|
||||
Ok(envelope)
|
||||
})
|
||||
.await
|
||||
.map_err(|_| {
|
||||
ApiError(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"configuration task failed".into(),
|
||||
)
|
||||
})?
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user