hub 0.166.0 source: the clock-ahead tunnel alarm wg_tunnel_stale/ok (R-1204, operator-only, edge-triggered); D21 + D4 proof evidence
This commit is contained in:
1 parent
0639cf0772
commit
e267c5e2cd
12 files changed
+279
-1
No files matched your search
@@ -0,0 +1,78 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
|
||||
)
|
||||
|
||||
// R-1204: the box's tunnel to ep0 — a future-stamped handshake (the clock ran ahead) or no handshake for 6 h raises
|
||||
// wg_tunnel_stale ONCE (edge-triggered from the host's newest tunnel event), and wg_tunnel_ok once when healthy; a
|
||||
// healthy tunnel, an inactive one and an older agent are not judged.
|
||||
// RED-PROOF: with checkWGTunnel not called from the host-report handler the first count fails (0 events); with
|
||||
// wgTunnelStale ignoring handshake_future_s the clock case fails — seen failing 2026-10-22.
|
||||
func tunnelReportBody(hostID, wg string) string {
|
||||
b := validReportBody(hostID)
|
||||
i := strings.LastIndex(b, "}")
|
||||
return b[:i] + `,"wireguard":` + wg + b[i:]
|
||||
}
|
||||
|
||||
func TestR1204_TheClockAheadTunnelAlarmsOnceAndClears(t *testing.T) {
|
||||
h, st, _ := newTestHandler(t)
|
||||
st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ckey", RetrievalPassword: "p"})
|
||||
st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "HKEY"})
|
||||
post := func(wg string) {
|
||||
t.Helper()
|
||||
if rr := do(h, http.MethodPost, "/host-report", "HKEY", tunnelReportBody("h1", wg)); rr.Code != http.StatusOK {
|
||||
t.Fatalf("host-report = %d %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
count := func(typ string) int {
|
||||
evs, _ := st.GetEventsByType("c1", typ, time.Time{})
|
||||
return len(evs)
|
||||
}
|
||||
post(`{"registered":true,"active":true,"last_handshake_age_s":60}`) // healthy
|
||||
post(`{"registered":true,"active":false}`) // service down: not judged here
|
||||
if count(eventWGTunnelStale) != 0 {
|
||||
t.Fatal("a healthy or inactive tunnel must not alarm")
|
||||
}
|
||||
post(`{"registered":true,"active":true,"last_handshake_age_s":0,"handshake_future_s":63072000}`) // ran 2 years ahead
|
||||
post(`{"registered":true,"active":true,"last_handshake_age_s":0,"handshake_future_s":63071100}`)
|
||||
if n := count(eventWGTunnelStale); n != 1 {
|
||||
t.Fatalf("wg_tunnel_stale = %d, want exactly 1 (edge-triggered)", n)
|
||||
}
|
||||
evs, _ := st.GetEventsByType("c1", eventWGTunnelStale, time.Time{})
|
||||
if !strings.Contains(evs[0].Message, "Host h1:") || !strings.Contains(evs[0].Message, "FUTURE") {
|
||||
t.Fatalf("the alarm must name the host and the clock cause: %q", evs[0].Message)
|
||||
}
|
||||
post(`{"registered":true,"active":true,"last_handshake_age_s":45}`)
|
||||
post(`{"registered":true,"active":true,"last_handshake_age_s":50}`)
|
||||
if count(eventWGTunnelOK) != 1 {
|
||||
t.Fatalf("wg_tunnel_ok = %d, want 1", count(eventWGTunnelOK))
|
||||
}
|
||||
post(`{"registered":true,"active":true,"last_handshake_age_s":30000}`) // 8 h 20 min with no handshake
|
||||
if count(eventWGTunnelStale) != 2 {
|
||||
t.Fatal("six hours without a handshake while reporting must alarm again")
|
||||
}
|
||||
}
|
||||
|
||||
func TestR1204_WhatIsJudged(t *testing.T) {
|
||||
i := func(v int64) *int64 { return &v }
|
||||
for _, c := range []struct {
|
||||
w *hostWireguard
|
||||
want bool
|
||||
}{
|
||||
{nil, false},
|
||||
{&hostWireguard{Registered: true, Active: true, LastHandshakeAgeS: i(120)}, false},
|
||||
{&hostWireguard{Registered: true, Active: true, LastHandshakeAgeS: i(6*3600 + 1)}, true},
|
||||
{&hostWireguard{Registered: true, Active: true, LastHandshakeAgeS: i(0), HandshakeFutureS: i(3600)}, true},
|
||||
{&hostWireguard{Registered: false, Active: true, LastHandshakeAgeS: i(99999)}, false},
|
||||
} {
|
||||
if got, _ := wgTunnelStale(c.w); got != c.want {
|
||||
t.Errorf("%+v: stale=%t, want %t", c.w, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user