Skip to content

Commit d9f955e

Browse files
committed
fix: don't print 'Tunnel ready' until proxy URL responds
verify_endpoints was written but never called, causing tunnel_ready to fire as soon as Kubernetes conditions flipped — before Envoy Gateway had finished propagating the xDS config and the tunnel was actually serving traffic. Fix: - Call verify_endpoints after hostname resolution, before emitting tunnel_ready / printing 'Tunnel ready:' - Probe the origin first (best-effort, bounded budget) to confirm the local service is up - Then poll the tunnel HTTPS URL on a fixed 10s interval until it returns a non-5xx response, printing a status line each attempt - Only then emit tunnel_ready Also simplifies verify_endpoints: removes the exponential-backoff inner loop in favour of the fixed 10s interval the user sees.
1 parent b9e4d79 commit d9f955e

2 files changed

Lines changed: 37 additions & 33 deletions

File tree

connect-lib/bin/src/main.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,20 @@ async fn run() -> n0_error::Result<()> {
546546
n0_error::anyerr!("Tunnel {tunnel_id} has no hostname after Ready")
547547
})?;
548548

549+
// Verify origin is up and poll the tunnel URL every 10 seconds
550+
// until it returns a successful (non-5xx) response. Only after
551+
// this do we declare the tunnel ready to the user / Go supervisor.
552+
let verify_mode = mode;
553+
progress::verify_endpoints(
554+
&endpoint,
555+
&hostname,
556+
std::time::Duration::from_secs(10),
557+
move |label, url, elapsed, status| {
558+
progress::render_verify(verify_mode, label, url, elapsed, status);
559+
},
560+
)
561+
.await?;
562+
549563
// Re-fetch the up-to-date TunnelSummary for the tunnel_ready
550564
// payload (existing contract — id, label, endpoint, hostnames,
551565
// endpoint_id, status, elapsed_secs).
@@ -571,7 +585,7 @@ async fn run() -> n0_error::Result<()> {
571585
);
572586
} else {
573587
for hostname in &tunnel.hostnames {
574-
println!("Tunnel ready after {} sec: https://{}", elapsed, hostname);
588+
println!("Tunnel ready: https://{}", hostname);
575589
}
576590
}
577591

connect-lib/bin/src/progress.rs

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,11 @@ where
245245

246246
// --- verify_endpoints ---
247247

248-
/// Probe the origin endpoint (HTTP, best-effort) and proxy URL (HTTPS,
249-
/// indefinite). Origin is bounded by `budget` and is non-fatal on failure.
250-
/// Proxy retries indefinitely with exponential backoff and prints a status
251-
/// message every 10s (e.g. `" waiting for proxy [url] (30s) ... HTTP 503"`)
252-
/// so the user sees progress even during long settling times.
248+
/// Probe the origin endpoint (HTTP, best-effort) and then the tunnel proxy URL
249+
/// (HTTPS, indefinite). Origin is bounded by `budget` and is non-fatal on
250+
/// failure. The proxy URL is checked on a fixed 10-second interval until it
251+
/// returns a non-5xx response, printing a status line on each attempt so the
252+
/// user sees progress during settling time.
253253
pub async fn verify_endpoints<F>(
254254
origin_endpoint: &str,
255255
hostname: &str,
@@ -283,10 +283,8 @@ where
283283
}
284284
}
285285

286-
// Proxy probe — indefinite retry with periodic status every 10s.
286+
// Proxy probe — fixed 10s interval, indefinite, until non-5xx.
287287
let start = Instant::now();
288-
let mut backoff = Duration::from_millis(250);
289-
let mut last_status = Instant::now();
290288
loop {
291289
match client.get(&proxy_url).send().await {
292290
Ok(resp) => {
@@ -295,34 +293,26 @@ where
295293
verify_cb("proxy responding", &proxy_url, start.elapsed(), Some(status));
296294
return Ok(());
297295
}
298-
if last_status.elapsed() >= Duration::from_secs(10) {
299-
let _ = writeln!(
300-
std::io::stderr(),
301-
" \u{25CB} waiting for proxy [{}] ({:.0}s) ... HTTP {}",
302-
proxy_url,
303-
start.elapsed().as_secs_f64(),
304-
status,
305-
);
306-
let _ = std::io::stderr().flush();
307-
last_status = Instant::now();
308-
}
296+
let _ = writeln!(
297+
std::io::stderr(),
298+
" \u{25CB} waiting for tunnel [{}] ({:.0}s) ... HTTP {}",
299+
proxy_url,
300+
start.elapsed().as_secs_f64(),
301+
status,
302+
);
303+
let _ = std::io::stderr().flush();
309304
}
310305
Err(_e) => {
311-
if last_status.elapsed() >= Duration::from_secs(10) {
312-
let _ = writeln!(
313-
std::io::stderr(),
314-
" \u{25CB} waiting for proxy [{}] ({:.0}s) ... no response",
315-
proxy_url,
316-
start.elapsed().as_secs_f64(),
317-
);
318-
let _ = std::io::stderr().flush();
319-
last_status = Instant::now();
320-
}
306+
let _ = writeln!(
307+
std::io::stderr(),
308+
" \u{25CB} waiting for tunnel [{}] ({:.0}s) ... no response",
309+
proxy_url,
310+
start.elapsed().as_secs_f64(),
311+
);
312+
let _ = std::io::stderr().flush();
321313
}
322314
}
323-
let sleep_dur = std::cmp::min(backoff, Duration::from_secs(2));
324-
sleep(sleep_dur).await;
325-
backoff = std::cmp::min(backoff * 2, Duration::from_secs(2));
315+
sleep(Duration::from_secs(10)).await;
326316
}
327317
}
328318

0 commit comments

Comments
 (0)