Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions e2e/healthcheck_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,9 @@ health.service.test. 0 IN A %s
},
}

// Configure Backup with Static check (Metric 1000)
backupMetric := uint32(1000)
// Keep the backup more expensive even with the initial one-second link
// metric, CI latency variation, and the 10% route-switch deadband.
backupMetric := state.DurationToMetric(10 * time.Second)
central.Routers[2].Prefixes = []state.PrefixHealthWrapper{
{
&state.StaticPrefixHealth{
Expand Down Expand Up @@ -229,7 +230,7 @@ health.service.test. 0 IN A %s

// A. Initial state: HTTP server is DOWN on primary.
// Primary health check should fail (Metric INF).
// Client should route to Backup (Metric 1000).
// Client should route to Backup.

t.Log("Step A: Waiting for routing to fallback (Primary DOWN)")
h.WaitForStatus(t, "client", func(status *protocol.StatusResponse) bool {
Expand All @@ -246,7 +247,16 @@ health.service.test. 0 IN A %s
// C. Wait for Primary to become healthy
// Primary should advertise Metric 10.
// Client should switch to Primary.
t.Log("Step C: Waiting for routing to switch to Primary (Primary UP)")
t.Log("Step C: Waiting for Primary to advertise healthy Metric 10")
h.WaitForStatus(t, "primary", func(status *protocol.StatusResponse) bool {
for _, prefix := range status.GetNode().GetAdvertised() {
if prefix.GetPrefix() == servicePrefixStr && prefix.GetMetric() == 10 {
return true
}
}
return false
})
t.Log("Waiting for routing to switch to Primary (Primary UP)")
h.WaitForStatus(t, "client", func(status *protocol.StatusResponse) bool {
return HasSelectedRoute(status, "10.0.3.1/32", "primary", "primary")
})
Expand Down
29 changes: 22 additions & 7 deletions e2e/recovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,18 @@ func TestRecoveryExample(t *testing.T) {
}

// 6. Wait for recovery
defer func() {
if t.Failed() {
for _, name := range nodeNames {
stdout, stderr, err := h.Exec(name, []string{"nylon", "status", "-i", "nylon0", "--json"})
t.Logf("Recovery status for %s (error: %v):\n%s\n%s", name, err, stdout, stderr)
}
}
}()
t.Log("Waiting for recovery (rerouting)...")
// Start a background pinger to trigger routing
stopPinger := make(chan struct{})
defer close(stopPinger)
go func() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
Expand All @@ -125,13 +134,19 @@ func TestRecoveryExample(t *testing.T) {
}
}()
h.WaitForTrace(alice, fmt.Sprintf("Fwd packet: %s -> %s, via %s", nylonIPs[alice], nylonIPs[bob], vps))
close(stopPinger)

t.Log("Recovery successful! Traffic rerouted via VPS.")

// Final connectivity check
stdout, stderr, err = h.Exec(alice, []string{"ping", "-c", "3", nylonIPs[bob]})
if err != nil {
t.Fatalf("Post-recovery ping failed: %v\nStdout: %s\nStderr: %s", err, stdout, stderr)
// A forwarding trace only establishes Alice's next hop. Wait for the
// remaining hops and the return path to recover before asserting delivery.
deadline := time.Now().Add(WaitTimeout)
for {
stdout, stderr, err = h.Exec(alice, []string{"ping", "-c", "3", "-W", "1", "-w", "4", nylonIPs[bob]})
if err == nil {
break
}
if time.Now().After(deadline) {
t.Fatalf("Timed out waiting for post-recovery connectivity: %v\nStdout: %s\nStderr: %s", err, stdout, stderr)
}
time.Sleep(time.Second)
}
t.Log("Recovery successful! Traffic rerouted via VPS and connectivity restored.")
}
6 changes: 5 additions & 1 deletion state/prefix_health.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,14 @@ type httpPrefixHealthMonitor struct {
}

func (h *httpPrefixHealthMonitor) GetMetric() uint32 {
metric := h.lastMetric.Load()
if metric == INF {
return INF
}
if h.hasMetricOverride {
return h.metricOverride
}
return h.lastMetric.Load()
return metric
}

func (h *httpPrefixHealthMonitor) Stop() {
Expand Down
14 changes: 14 additions & 0 deletions state/prefix_health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ delay: 5s
}
}

func TestHTTPPrefixHealthMetricOverridePreservesFailures(t *testing.T) {
monitor := &httpPrefixHealthMonitor{hasMetricOverride: true, metricOverride: 10}
for _, metric := range []uint32{INF, 250, INF} {
monitor.lastMetric.Store(metric)
want := uint32(10)
if metric == INF {
want = INF
}
if got := monitor.GetMetric(); got != want {
t.Fatalf("health metric %d: got %d, want %d", metric, got, want)
}
}
}

type roundTripFunc func(*http.Request) (*http.Response, error)

func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
Expand Down
Loading