Skip to content

Commit 1c4eec7

Browse files
authored
Merge pull request #1408 from Dstack-TEE/fix/gateway-wg-apply-shutdown
fix(gateway): let an in-flight WireGuard apply finish before exiting
2 parents 1cf4287 + a5e99dd commit 1c4eec7

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

‎dstack/gateway/src/main.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ async fn main() -> Result<()> {
193193
tls_config,
194194
})
195195
.await?;
196+
let _stop_wg_apply = StopWgApplyOnExit(state.clone());
196197
info!("Starting background tasks");
197198
state.start_bg_tasks().await?;
198199

@@ -281,6 +282,16 @@ async fn main() -> Result<()> {
281282
Ok(())
282283
}
283284

285+
/// Lets an in-flight WireGuard apply finish before the process exits, on every
286+
/// return path out of `main` (a failed listener bind as much as a shutdown).
287+
struct StopWgApplyOnExit(Proxy);
288+
289+
impl Drop for StopWgApplyOnExit {
290+
fn drop(&mut self) {
291+
self.0.stop_wg_apply();
292+
}
293+
}
294+
284295
#[cfg(test)]
285296
mod startup_tests {
286297
use super::write_cert;

‎dstack/gateway/src/main_service.rs‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ pub struct ProxyInner {
7575
/// Capacity-one queue: each apply renders the latest state, so pending
7676
/// requests can be coalesced without losing a state change.
7777
wg_apply_tx: std::sync::mpsc::SyncSender<()>,
78+
/// Held for the whole of an apply; `true` once the process is exiting.
79+
/// The apply writes the interface's private key through a temporary file,
80+
/// and exiting mid-write would leave that file behind.
81+
wg_apply_stopped: Mutex<bool>,
7882
/// Only one registration mutates the routing table at a time anyway.
7983
/// Wait asynchronously rather than parking all RPC/blocking workers on it.
8084
registration_slot: Arc<Semaphore>,
@@ -309,12 +313,28 @@ impl ProxyInner {
309313
/// Render the WireGuard config under the routing lock, then write and apply
310314
/// it after releasing that lock. Only the apply worker calls this.
311315
fn reconfigure_wg(&self) -> Result<()> {
316+
let stopped = self
317+
.wg_apply_stopped
318+
.lock()
319+
.or_panic("Failed to lock wg apply");
320+
if *stopped {
321+
return Ok(());
322+
}
312323
let rendered = self.lock().generate_wg_config();
313324
let result = rendered.and_then(|rendered| apply_wg_config(&self.config, &rendered));
314325
crate::metrics::record_wg_reconfigure(result.is_ok());
315326
result
316327
}
317328

329+
/// Wait for an apply in progress to finish and refuse any later one.
330+
/// Called on the way out of `main`, which does not join the worker thread.
331+
pub(crate) fn stop_wg_apply(&self) {
332+
*self
333+
.wg_apply_stopped
334+
.lock()
335+
.or_panic("Failed to lock wg apply") = true;
336+
}
337+
318338
/// Queue a full apply on the worker. Every caller goes through here, so
319339
/// registrations, removals and reloads never wait on disk I/O or `wg`.
320340
pub(crate) fn request_wg_apply(&self) {
@@ -672,6 +692,7 @@ impl ProxyInner {
672692
state,
673693
notify_state_updated: Notify::new(),
674694
wg_apply_tx,
695+
wg_apply_stopped: Mutex::new(false),
675696
registration_slot: Arc::new(Semaphore::new(1)),
676697
my_app_id,
677698
auth_client,

0 commit comments

Comments
 (0)