Skip to content

Commit fb52b92

Browse files
committed
chore: add setCallback to nodeUpdater
1 parent 5ed9e70 commit fb52b92

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

tailnet/node.go

+20-3
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,28 @@ func (u *nodeUpdater) updateLoop() {
5555
u.logger.Debug(context.Background(), "closing nodeUpdater updateLoop")
5656
return
5757
}
58-
node := u.nodeLocked()
5958
u.dirty = false
6059
u.phase = configuring
6160
u.Broadcast()
6261

62+
callback := u.callback
63+
if callback == nil {
64+
u.logger.Debug(context.Background(), "skipped sending node; no node callback")
65+
continue
66+
}
67+
6368
// We cannot reach nodes without DERP for discovery. Therefore, there is no point in sending
6469
// the node without this, and we can save ourselves from churn in the tailscale/wireguard
6570
// layer.
71+
node := u.nodeLocked()
6672
if node.PreferredDERP == 0 {
6773
u.logger.Debug(context.Background(), "skipped sending node; no PreferredDERP", slog.F("node", node))
6874
continue
6975
}
7076

7177
u.L.Unlock()
7278
u.logger.Debug(context.Background(), "calling nodeUpdater callback", slog.F("node", node))
73-
u.callback(node)
79+
callback(node)
7480
u.L.Lock()
7581
}
7682
}
@@ -155,7 +161,7 @@ func (u *nodeUpdater) setDERPForcedWebsocket(region int, reason string) {
155161
}
156162

157163
// setStatus handles the status callback from the wireguard engine to learn about new endpoints
158-
// (e.g. discovered by STUN)
164+
// (e.g. discovered by STUN). u.L MUST NOT be held
159165
func (u *nodeUpdater) setStatus(s *wgengine.Status, err error) {
160166
u.logger.Debug(context.Background(), "wireguard status", slog.F("status", s), slog.Error(err))
161167
if err != nil {
@@ -181,6 +187,7 @@ func (u *nodeUpdater) setStatus(s *wgengine.Status, err error) {
181187
u.Broadcast()
182188
}
183189

190+
// setAddresses sets the local addresses for the node. u.L MUST NOT be held.
184191
func (u *nodeUpdater) setAddresses(ips []netip.Prefix) {
185192
u.L.Lock()
186193
defer u.L.Unlock()
@@ -192,3 +199,13 @@ func (u *nodeUpdater) setAddresses(ips []netip.Prefix) {
192199
u.dirty = true
193200
u.Broadcast()
194201
}
202+
203+
// setCallback sets the callback for node changes. It also triggers a call
204+
// for the current node immediately. u.L MUST NOT be held.
205+
func (u *nodeUpdater) setCallback(callback func(node *Node)) {
206+
u.L.Lock()
207+
defer u.L.Unlock()
208+
u.callback = callback
209+
u.dirty = true
210+
u.Broadcast()
211+
}

tailnet/node_internal_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,44 @@ func TestNodeUpdater_setAddresses_same(t *testing.T) {
441441
}()
442442
_ = testutil.RequireRecvCtx(ctx, t, done)
443443
}
444+
445+
func TestNodeUpdater_setCallback(t *testing.T) {
446+
t.Parallel()
447+
ctx := testutil.Context(t, testutil.WaitShort)
448+
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
449+
id := tailcfg.NodeID(1)
450+
nodeKey := key.NewNode().Public()
451+
discoKey := key.NewDisco().Public()
452+
uut := newNodeUpdater(
453+
logger,
454+
nil,
455+
id, nodeKey, discoKey,
456+
)
457+
defer uut.close()
458+
459+
// Given: preferred DERP is 1
460+
addrs := []netip.Prefix{netip.MustParsePrefix("192.168.0.200/32")}
461+
uut.L.Lock()
462+
uut.preferredDERP = 1
463+
uut.addresses = slices.Clone(addrs)
464+
uut.L.Unlock()
465+
466+
// When: we set callback
467+
nodeCh := make(chan *Node)
468+
uut.setCallback(func(n *Node) {
469+
nodeCh <- n
470+
})
471+
472+
// Then: we get a node update
473+
node := testutil.RequireRecvCtx(ctx, t, nodeCh)
474+
require.Equal(t, nodeKey, node.Key)
475+
require.Equal(t, discoKey, node.DiscoKey)
476+
require.Equal(t, 1, node.PreferredDERP)
477+
478+
done := make(chan struct{})
479+
go func() {
480+
defer close(done)
481+
uut.close()
482+
}()
483+
_ = testutil.RequireRecvCtx(ctx, t, done)
484+
}

0 commit comments

Comments
 (0)