integration: wait for r2 to drop r1's route before pulling its cable

Headscale can report the new primary before r2 has the netmap; cutting r2
then strands r1's route in its table 52 and docker cannot set its gateway.
This commit is contained in:
Kristoffer Dalby
2026-09-26 06:44:51 +00:00
parent 46a287d1f1
commit 611dc388e6
+30
View File
@@ -28,6 +28,7 @@ import (
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/ipproto"
"tailscale.com/types/key"
"tailscale.com/types/views"
"tailscale.com/util/must"
"tailscale.com/util/slicesx"
@@ -4475,10 +4476,39 @@ func TestHASubnetRouterFailoverDockerDisconnect(t *testing.T) {
// lowest NodeID regardless of who was prev primary.
// ============================================================
t.Log("=== Phase 4a: cable-pull r1, expect failover to r2. ===")
var r1Key key.NodePublic
require.EventuallyWithT(t, func(c *assert.CollectT) {
st, err := subRouter1.Status()
if !assert.NoError(c, err) {
return
}
r1Key = st.Self.PublicKey
}, propagationTime, 200*time.Millisecond, "phase 4a: read r1's node key")
require.NoError(t, subRouter1.DisconnectFromNetwork(usernet1),
"phase 4a: docker disconnect r1")
requirePrimary(nodeID2, "phase 4a: r2 promoted after r1 down")
// Headscale can report the new primary before r2 has the netmap.
// Pulling r2's cable in that window strands r1's route for pref in
// r2's table 52, and docker then cannot set r2's gateway on reconnect.
assert.EventuallyWithT(t, func(c *assert.CollectT) {
st, err := subRouter2.Status()
if !assert.NoError(c, err) {
return
}
peer, ok := st.Peer[r1Key]
assert.True(c, ok, "r2 should list r1 as a peer")
if ok && peer.PrimaryRoutes != nil {
assert.NotContains(c, peer.PrimaryRoutes.AsSlice(), pref)
}
}, propagationTime, 200*time.Millisecond, "phase 4a: r2 no longer routes pref via r1")
t.Log("=== Phase 4b: cable-pull r2, primary must NOT flap to offline r1. ===")
require.NoError(t, subRouter2.DisconnectFromNetwork(usernet1),
"phase 4b: docker disconnect r2")