mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: add derpserver to wsproxy, add proxies to derpmap (#7311)
This commit is contained in:
@@ -195,3 +195,124 @@ func TestConn_PreferredDERP(t *testing.T) {
|
||||
t.Fatal("timed out waiting for node")
|
||||
}
|
||||
}
|
||||
|
||||
// TestConn_UpdateDERP tests that when update the DERP map we pick a new
|
||||
// preferred DERP server and new connections can be made from clients.
|
||||
func TestConn_UpdateDERP(t *testing.T) {
|
||||
t.Parallel()
|
||||
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
|
||||
derpMap1, _ := tailnettest.RunDERPAndSTUN(t)
|
||||
ip := tailnet.IP()
|
||||
conn, err := tailnet.NewConn(&tailnet.Options{
|
||||
Addresses: []netip.Prefix{netip.PrefixFrom(ip, 128)},
|
||||
Logger: logger.Named("w1"),
|
||||
DERPMap: derpMap1,
|
||||
BlockEndpoints: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := conn.Close()
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
// Buffer channel so callback doesn't block
|
||||
nodes := make(chan *tailnet.Node, 50)
|
||||
conn.SetNodeCallback(func(node *tailnet.Node) {
|
||||
nodes <- node
|
||||
})
|
||||
|
||||
ctx1, cancel1 := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer cancel1()
|
||||
select {
|
||||
case node := <-nodes:
|
||||
require.Equal(t, 1, node.PreferredDERP)
|
||||
case <-ctx1.Done():
|
||||
t.Fatal("timed out waiting for node")
|
||||
}
|
||||
|
||||
// Connect from a different client.
|
||||
client1, err := tailnet.NewConn(&tailnet.Options{
|
||||
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
|
||||
Logger: logger.Named("client1"),
|
||||
DERPMap: derpMap1,
|
||||
BlockEndpoints: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := client1.Close()
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
client1.SetNodeCallback(func(node *tailnet.Node) {
|
||||
err := conn.UpdateNodes([]*tailnet.Node{node}, false)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
client1.UpdateNodes([]*tailnet.Node{conn.Node()}, false)
|
||||
|
||||
awaitReachableCtx1, awaitReachableCancel1 := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer awaitReachableCancel1()
|
||||
require.True(t, client1.AwaitReachable(awaitReachableCtx1, ip))
|
||||
|
||||
// Update the DERP map and wait for the preferred DERP server to change.
|
||||
derpMap2, _ := tailnettest.RunDERPAndSTUN(t)
|
||||
// Change the region ID.
|
||||
derpMap2.Regions[2] = derpMap2.Regions[1]
|
||||
delete(derpMap2.Regions, 1)
|
||||
derpMap2.Regions[2].RegionID = 2
|
||||
for _, node := range derpMap2.Regions[2].Nodes {
|
||||
node.RegionID = 2
|
||||
}
|
||||
conn.SetDERPMap(derpMap2)
|
||||
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer cancel2()
|
||||
parentLoop:
|
||||
for {
|
||||
select {
|
||||
case node := <-nodes:
|
||||
if node.PreferredDERP != 2 {
|
||||
t.Logf("waiting for preferred DERP server to change, got %v", node.PreferredDERP)
|
||||
continue
|
||||
}
|
||||
t.Log("preferred DERP server changed!")
|
||||
break parentLoop
|
||||
case <-ctx2.Done():
|
||||
t.Fatal("timed out waiting for preferred DERP server to change")
|
||||
}
|
||||
}
|
||||
|
||||
// Client1 should be dropped...
|
||||
awaitReachableCtx2, awaitReachableCancel2 := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer awaitReachableCancel2()
|
||||
require.False(t, client1.AwaitReachable(awaitReachableCtx2, ip))
|
||||
|
||||
// ... unless the client updates it's derp map and nodes.
|
||||
client1.SetDERPMap(derpMap2)
|
||||
client1.UpdateNodes([]*tailnet.Node{conn.Node()}, false)
|
||||
awaitReachableCtx3, awaitReachableCancel3 := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer awaitReachableCancel3()
|
||||
require.True(t, client1.AwaitReachable(awaitReachableCtx3, ip))
|
||||
|
||||
// Connect from a different different client with up-to-date derp map and
|
||||
// nodes.
|
||||
client2, err := tailnet.NewConn(&tailnet.Options{
|
||||
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
|
||||
Logger: logger.Named("client2"),
|
||||
DERPMap: derpMap2,
|
||||
BlockEndpoints: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := client2.Close()
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
client2.SetNodeCallback(func(node *tailnet.Node) {
|
||||
err := conn.UpdateNodes([]*tailnet.Node{node}, false)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
client2.UpdateNodes([]*tailnet.Node{conn.Node()}, false)
|
||||
|
||||
awaitReachableCtx4, awaitReachableCancel4 := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer awaitReachableCancel4()
|
||||
require.True(t, client2.AwaitReachable(awaitReachableCtx4, ip))
|
||||
}
|
||||
|
||||
@@ -97,3 +97,115 @@ func NewDERPMap(ctx context.Context, region *tailcfg.DERPRegion, stunAddrs []str
|
||||
|
||||
return derpMap, nil
|
||||
}
|
||||
|
||||
// CompareDERPMaps returns true if the given DERPMaps are equivalent. Ordering
|
||||
// of slices is ignored.
|
||||
//
|
||||
// If the first map is nil, the second map must also be nil for them to be
|
||||
// considered equivalent. If the second map is nil, the first map can be any
|
||||
// value and the function will return true.
|
||||
func CompareDERPMaps(a *tailcfg.DERPMap, b *tailcfg.DERPMap) bool {
|
||||
if a == nil {
|
||||
return b == nil
|
||||
}
|
||||
if b == nil {
|
||||
return true
|
||||
}
|
||||
if len(a.Regions) != len(b.Regions) {
|
||||
return false
|
||||
}
|
||||
if a.OmitDefaultRegions != b.OmitDefaultRegions {
|
||||
return false
|
||||
}
|
||||
|
||||
for id, region := range a.Regions {
|
||||
other, ok := b.Regions[id]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !compareDERPRegions(region, other) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func compareDERPRegions(a *tailcfg.DERPRegion, b *tailcfg.DERPRegion) bool {
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
if a.EmbeddedRelay != b.EmbeddedRelay {
|
||||
return false
|
||||
}
|
||||
if a.RegionID != b.RegionID {
|
||||
return false
|
||||
}
|
||||
if a.RegionCode != b.RegionCode {
|
||||
return false
|
||||
}
|
||||
if a.RegionName != b.RegionName {
|
||||
return false
|
||||
}
|
||||
if a.Avoid != b.Avoid {
|
||||
return false
|
||||
}
|
||||
if len(a.Nodes) != len(b.Nodes) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Convert both slices to maps so ordering can be ignored easier.
|
||||
aNodes := map[string]*tailcfg.DERPNode{}
|
||||
for _, node := range a.Nodes {
|
||||
aNodes[node.Name] = node
|
||||
}
|
||||
bNodes := map[string]*tailcfg.DERPNode{}
|
||||
for _, node := range b.Nodes {
|
||||
bNodes[node.Name] = node
|
||||
}
|
||||
|
||||
for name, aNode := range aNodes {
|
||||
bNode, ok := bNodes[name]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if aNode.Name != bNode.Name {
|
||||
return false
|
||||
}
|
||||
if aNode.RegionID != bNode.RegionID {
|
||||
return false
|
||||
}
|
||||
if aNode.HostName != bNode.HostName {
|
||||
return false
|
||||
}
|
||||
if aNode.CertName != bNode.CertName {
|
||||
return false
|
||||
}
|
||||
if aNode.IPv4 != bNode.IPv4 {
|
||||
return false
|
||||
}
|
||||
if aNode.IPv6 != bNode.IPv6 {
|
||||
return false
|
||||
}
|
||||
if aNode.STUNPort != bNode.STUNPort {
|
||||
return false
|
||||
}
|
||||
if aNode.STUNOnly != bNode.STUNOnly {
|
||||
return false
|
||||
}
|
||||
if aNode.DERPPort != bNode.DERPPort {
|
||||
return false
|
||||
}
|
||||
if aNode.InsecureForTests != bNode.InsecureForTests {
|
||||
return false
|
||||
}
|
||||
if aNode.ForceHTTP != bNode.ForceHTTP {
|
||||
return false
|
||||
}
|
||||
if aNode.STUNTestIP != bNode.STUNTestIP {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user