From 0aca0984edff0051ba5986f126de30d881d1b07e Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 8 Oct 2026 15:24:51 +0700 Subject: [PATCH] test(cli): keep the indefinite recovery wait test off the host's DNS TestNetworkShutdownCancelsIndefiniteRecoveryWait relied on an empty upstream map never recovering. Since 73db7adc an empty pool checks upstream.os instead, so the test probed the host's real OS resolver. On a host with working DNS that check succeeds, recovery completes, and it reads the upstream monitor the test never built. Recover against a loopback port nothing listens on, so the wait can never end on its own, and give the prog the upstream monitor that run always creates before network monitoring can start a recovery. The panic is test-only: production never reaches recovery without one. --- cmd/cli/network_shutdown_test.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cmd/cli/network_shutdown_test.go b/cmd/cli/network_shutdown_test.go index 714247a..ff28821 100644 --- a/cmd/cli/network_shutdown_test.go +++ b/cmd/cli/network_shutdown_test.go @@ -172,12 +172,21 @@ func TestNetworkShutdownCancelsIndefiniteRecoveryWait(t *testing.T) { oldReset, oldIntercept := recoveryResetDNSFn, dnsIntercept dnsIntercept = false defer func() { recoveryResetDNSFn, dnsIntercept = oldReset, oldIntercept }() - p := &prog{cfg: &ctrld.Config{}} + // The only upstream is a loopback port nothing listens on, so recovery + // can never succeed. An empty upstream map no longer does that: recovery + // then checks upstream.os, and on a host with working DNS that answers, + // completes the recovery and turns the test into a probe of the host. + dead := &ctrld.UpstreamConfig{Type: ctrld.ResolverTypeLegacy, Endpoint: deadUDPAddr(t), Timeout: 300} + dead.Init() + cfg := &ctrld.Config{Upstream: map[string]*ctrld.UpstreamConfig{"0": dead}} + // Recovery reads the upstream monitor, which run creates before network + // monitoring can start any recovery. + p := &prog{cfg: cfg, um: newUpstreamMonitor(cfg)} entered := make(chan struct{}) recoveryResetDNSFn = func(*prog, bool, bool) { close(entered) } recovered := make(chan struct{}) - // An empty upstream map can never recover. Shutdown must cancel that wait - // without waiting on a mutex held for the recovery's entire lifetime. + // Shutdown must cancel that wait without waiting on a mutex held for the + // recovery's entire lifetime. go func() { p.handleRecovery(RecoveryReasonRegularFailure); close(recovered) }() waitNetworkShutdownSignal(t, entered, "recovery reset seam") closed := make(chan struct{})