mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-12 17:59:06 +02:00
Read the relay instance URL and IP atomically to prevent reconnects from mixing values from different connections. Extend existing connection and offer/answer logs with relay URLs and IPs to help trace mismatched advertisements.
104 lines
2.4 KiB
Go
104 lines
2.4 KiB
Go
package client
|
|
|
|
import (
|
|
"net/netip"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestManager_RelayInstanceAddressAcrossReconnect(t *testing.T) {
|
|
relays := []struct {
|
|
url *RelayAddr
|
|
conn stubConn
|
|
ip netip.Addr
|
|
}{
|
|
{
|
|
url: &RelayAddr{addr: "rels://relay-a.example:443"},
|
|
conn: stubConn{remote: staticAddr{s: "192.0.2.1:443"}},
|
|
ip: netip.MustParseAddr("192.0.2.1"),
|
|
},
|
|
{
|
|
url: &RelayAddr{addr: "rels://relay-b.example:443"},
|
|
conn: stubConn{remote: staticAddr{s: "192.0.2.2:443"}},
|
|
ip: netip.MustParseAddr("192.0.2.2"),
|
|
},
|
|
}
|
|
c := &Client{
|
|
instanceURL: relays[0].url,
|
|
relayConn: relays[0].conn,
|
|
serviceIsRunning: true,
|
|
}
|
|
m := &Manager{relayClient: c}
|
|
started := make(chan struct{})
|
|
stop := make(chan struct{})
|
|
done := make(chan struct{})
|
|
t.Cleanup(func() {
|
|
close(stop)
|
|
<-done
|
|
})
|
|
go func() {
|
|
defer close(done)
|
|
for i := 0; ; i++ {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
}
|
|
// Publish successive connection states using the lifecycle locks.
|
|
// Yield before publication so a getter using only muInstanceURL
|
|
// can read the old URL while waiting for the new connection's IP.
|
|
c.mu.Lock()
|
|
runtime.Gosched()
|
|
relay := relays[i%len(relays)]
|
|
c.muInstanceURL.Lock()
|
|
c.instanceURL = relay.url
|
|
c.muInstanceURL.Unlock()
|
|
c.relayConn = relay.conn
|
|
c.mu.Unlock()
|
|
if i == 0 {
|
|
close(started)
|
|
}
|
|
}
|
|
}()
|
|
<-started
|
|
|
|
for range 1000 {
|
|
url, ip, err := m.RelayInstanceAddress()
|
|
require.NoError(t, err)
|
|
wantIP := relays[0].ip
|
|
if url == relays[1].url.String() {
|
|
wantIP = relays[1].ip
|
|
}
|
|
if !assert.Equal(t, wantIP, ip, "advertised IP must belong to relay %s", url) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestManager_RelayInstanceAddressDisconnected(t *testing.T) {
|
|
for _, tt := range []struct {
|
|
name string
|
|
client *Client
|
|
}{
|
|
{name: "no client"},
|
|
{name: "not connected", client: &Client{}},
|
|
{
|
|
name: "closed connection",
|
|
client: &Client{
|
|
relayConn: stubConn{remote: staticAddr{s: "192.0.2.1:443"}},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &Manager{relayClient: tt.client}
|
|
url, ip, err := m.RelayInstanceAddress()
|
|
assert.Error(t, err)
|
|
assert.Empty(t, url, "disconnected relay must not advertise a URL")
|
|
assert.False(t, ip.IsValid(), "disconnected relay must not advertise a stale IP")
|
|
})
|
|
}
|
|
}
|