Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0e6d39a

Browse files
committed
tests for derp map changing
1 parent 9b503fa commit 0e6d39a

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed

agent/agent_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,109 @@ func TestAgent_Dial(t *testing.T) {
15641564
}
15651565
}
15661566

1567+
// TestAgent_UpdatedDERP checks that agents can handle their DERP map being
1568+
// updated, and that clients can also handle it.
1569+
func TestAgent_UpdatedDERP(t *testing.T) {
1570+
t.Parallel()
1571+
1572+
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
1573+
1574+
derpMap := *tailnettest.RunDERPAndSTUN(t)
1575+
metadata := agentsdk.Manifest{
1576+
DERPMap: &derpMap,
1577+
}
1578+
coordinator := tailnet.NewCoordinator(logger, func() *tailcfg.DERPMap {
1579+
return &derpMap
1580+
})
1581+
defer func() {
1582+
_ = coordinator.Close()
1583+
}()
1584+
agentID := uuid.New()
1585+
statsCh := make(chan *agentsdk.Stats, 50)
1586+
fs := afero.NewMemMapFs()
1587+
c := &client{
1588+
t: t,
1589+
agentID: agentID,
1590+
manifest: metadata,
1591+
statsChan: statsCh,
1592+
coordinator: coordinator,
1593+
}
1594+
closer := agent.New(agent.Options{
1595+
Client: c,
1596+
Filesystem: fs,
1597+
Logger: logger.Named("agent"),
1598+
ReconnectingPTYTimeout: time.Minute,
1599+
})
1600+
defer func() {
1601+
_ = closer.Close()
1602+
}()
1603+
1604+
// Setup a client connection.
1605+
newClientConn := func() *codersdk.WorkspaceAgentConn {
1606+
conn, err := tailnet.NewConn(&tailnet.Options{
1607+
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
1608+
DERPMap: metadata.DERPMap,
1609+
Logger: logger.Named("client"),
1610+
})
1611+
require.NoError(t, err)
1612+
clientConn, serverConn := net.Pipe()
1613+
serveClientDone := make(chan struct{})
1614+
t.Cleanup(func() {
1615+
_ = clientConn.Close()
1616+
_ = serverConn.Close()
1617+
_ = conn.Close()
1618+
<-serveClientDone
1619+
})
1620+
go func() {
1621+
defer close(serveClientDone)
1622+
err := coordinator.ServeClient(serverConn, uuid.New(), agentID)
1623+
assert.NoError(t, err)
1624+
}()
1625+
sendNode, _ := tailnet.ServeCoordinator(clientConn, func(update tailnet.CoordinatorNodeUpdate) error {
1626+
if tailnet.CompareDERPMaps(conn.DERPMap(), update.DERPMap) {
1627+
conn.SetDERPMap(update.DERPMap)
1628+
}
1629+
return conn.UpdateNodes(update.Nodes, false)
1630+
})
1631+
conn.SetNodeCallback(sendNode)
1632+
1633+
sdkConn := &codersdk.WorkspaceAgentConn{
1634+
Conn: conn,
1635+
}
1636+
t.Cleanup(func() {
1637+
_ = sdkConn.Close()
1638+
})
1639+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
1640+
defer cancel()
1641+
if !sdkConn.AwaitReachable(ctx) {
1642+
t.Fatal("agent not reachable")
1643+
}
1644+
1645+
return sdkConn
1646+
}
1647+
conn1 := newClientConn()
1648+
1649+
// Change the DERP map.
1650+
derpMap = *tailnettest.RunDERPAndSTUN(t)
1651+
// Change the region ID.
1652+
derpMap.Regions[2] = derpMap.Regions[1]
1653+
delete(derpMap.Regions, 1)
1654+
derpMap.Regions[2].RegionID = 2
1655+
for _, node := range derpMap.Regions[2].Nodes {
1656+
node.RegionID = 2
1657+
}
1658+
1659+
// Connect from a second client and make sure it uses the new DERP map.
1660+
conn2 := newClientConn()
1661+
require.Equal(t, []int{2}, conn2.DERPMap().RegionIDs())
1662+
1663+
// Make sure the first client is still reachable (it received a a DERP map
1664+
// update when the agent's nodes changed).
1665+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
1666+
defer cancel()
1667+
require.True(t, conn1.AwaitReachable(ctx))
1668+
}
1669+
15671670
func TestAgent_Speedtest(t *testing.T) {
15681671
t.Parallel()
15691672
t.Skip("This test is relatively flakey because of Tailscale's speedtest code...")

tailnet/conn_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,124 @@ func TestConn_PreferredDERP(t *testing.T) {
195195
t.Fatal("timed out waiting for node")
196196
}
197197
}
198+
199+
// TestConn_UpdateDERP tests that when update the DERP map we pick a new
200+
// preferred DERP server and new connections can be made from clients.
201+
func TestConn_UpdateDERP(t *testing.T) {
202+
t.Parallel()
203+
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
204+
205+
derpMap1 := tailnettest.RunDERPAndSTUN(t)
206+
ip := tailnet.IP()
207+
conn, err := tailnet.NewConn(&tailnet.Options{
208+
Addresses: []netip.Prefix{netip.PrefixFrom(ip, 128)},
209+
Logger: logger.Named("w1"),
210+
DERPMap: derpMap1,
211+
BlockEndpoints: true,
212+
})
213+
require.NoError(t, err)
214+
defer func() {
215+
err := conn.Close()
216+
assert.NoError(t, err)
217+
}()
218+
219+
// Buffer channel so callback doesn't block
220+
nodes := make(chan *tailnet.Node, 50)
221+
conn.SetNodeCallback(func(node *tailnet.Node) {
222+
nodes <- node
223+
})
224+
225+
ctx1, cancel1 := context.WithTimeout(context.Background(), testutil.WaitShort)
226+
defer cancel1()
227+
select {
228+
case node := <-nodes:
229+
require.Equal(t, 1, node.PreferredDERP)
230+
case <-ctx1.Done():
231+
t.Fatal("timed out waiting for node")
232+
}
233+
234+
// Connect from a different client.
235+
client1, err := tailnet.NewConn(&tailnet.Options{
236+
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
237+
Logger: logger.Named("client1"),
238+
DERPMap: derpMap1,
239+
BlockEndpoints: true,
240+
})
241+
require.NoError(t, err)
242+
defer func() {
243+
err := client1.Close()
244+
assert.NoError(t, err)
245+
}()
246+
client1.SetNodeCallback(func(node *tailnet.Node) {
247+
err := conn.UpdateNodes([]*tailnet.Node{node}, false)
248+
assert.NoError(t, err)
249+
})
250+
client1.UpdateNodes([]*tailnet.Node{conn.Node()}, false)
251+
252+
awaitReachableCtx1, awaitReachableCancel1 := context.WithTimeout(context.Background(), testutil.WaitShort)
253+
defer awaitReachableCancel1()
254+
require.True(t, client1.AwaitReachable(awaitReachableCtx1, ip))
255+
256+
// Update the DERP map and wait for the preferred DERP server to change.
257+
derpMap2 := tailnettest.RunDERPAndSTUN(t)
258+
// Change the region ID.
259+
derpMap2.Regions[2] = derpMap2.Regions[1]
260+
delete(derpMap2.Regions, 1)
261+
derpMap2.Regions[2].RegionID = 2
262+
for _, node := range derpMap2.Regions[2].Nodes {
263+
node.RegionID = 2
264+
}
265+
conn.SetDERPMap(derpMap2)
266+
267+
ctx2, cancel2 := context.WithTimeout(context.Background(), testutil.WaitShort)
268+
defer cancel2()
269+
parentLoop:
270+
for {
271+
select {
272+
case node := <-nodes:
273+
if node.PreferredDERP != 2 {
274+
t.Logf("waiting for preferred DERP server to change, got %v", node.PreferredDERP)
275+
continue
276+
}
277+
t.Log("preferred DERP server changed!")
278+
break parentLoop
279+
case <-ctx2.Done():
280+
t.Fatal("timed out waiting for preferred DERP server to change")
281+
}
282+
}
283+
284+
// Client1 should be dropped...
285+
awaitReachableCtx2, awaitReachableCancel2 := context.WithTimeout(context.Background(), testutil.WaitShort)
286+
defer awaitReachableCancel2()
287+
require.False(t, client1.AwaitReachable(awaitReachableCtx2, ip))
288+
289+
// ... unless the client updates it's derp map and nodes.
290+
client1.SetDERPMap(derpMap2)
291+
client1.UpdateNodes([]*tailnet.Node{conn.Node()}, false)
292+
awaitReachableCtx3, awaitReachableCancel3 := context.WithTimeout(context.Background(), testutil.WaitShort)
293+
defer awaitReachableCancel3()
294+
require.True(t, client1.AwaitReachable(awaitReachableCtx3, ip))
295+
296+
// Connect from a different different client with up-to-date derp map and
297+
// nodes.
298+
client2, err := tailnet.NewConn(&tailnet.Options{
299+
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
300+
Logger: logger.Named("client2"),
301+
DERPMap: derpMap2,
302+
BlockEndpoints: true,
303+
})
304+
require.NoError(t, err)
305+
defer func() {
306+
err := client2.Close()
307+
assert.NoError(t, err)
308+
}()
309+
client2.SetNodeCallback(func(node *tailnet.Node) {
310+
err := conn.UpdateNodes([]*tailnet.Node{node}, false)
311+
assert.NoError(t, err)
312+
})
313+
client2.UpdateNodes([]*tailnet.Node{conn.Node()}, false)
314+
315+
awaitReachableCtx4, awaitReachableCancel4 := context.WithTimeout(context.Background(), testutil.WaitShort)
316+
defer awaitReachableCancel4()
317+
require.True(t, client2.AwaitReachable(awaitReachableCtx4, ip))
318+
}

0 commit comments

Comments
 (0)