mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-04 20:12:16 +02:00
feature/relayserver: use eventbus.Monitor to simplify lifecycle management (#17234)
Instead of using separate channels to manage the lifecycle of the eventbus client, use the recently-added eventbus.Monitor, which handles signaling the processing loop to stop and waiting for it to complete. This allows us to simplify some of the setup and cleanup code in the relay server. Updates #15160 Change-Id: Ia1a47ce2e5a31bc8f546dca4c56c3141a40d67af Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
This commit is contained in:
parent
1d93bdce20
commit
3c32f87624
@ -82,11 +82,11 @@ type extension struct {
|
|||||||
logf logger.Logf
|
logf logger.Logf
|
||||||
bus *eventbus.Bus
|
bus *eventbus.Bus
|
||||||
|
|
||||||
mu sync.Mutex // guards the following fields
|
mu sync.Mutex // guards the following fields
|
||||||
shutdown bool
|
shutdown bool
|
||||||
|
|
||||||
port *int // ipn.Prefs.RelayServerPort, nil if disabled
|
port *int // ipn.Prefs.RelayServerPort, nil if disabled
|
||||||
disconnectFromBusCh chan struct{} // non-nil if consumeEventbusTopics is running, closed to signal it to return
|
eventSubs *eventbus.Monitor // nil if not connected to eventbus
|
||||||
busDoneCh chan struct{} // non-nil if consumeEventbusTopics is running, closed when it returns
|
|
||||||
debugSessionsCh chan chan []status.ServerSession // non-nil if consumeEventbusTopics is running
|
debugSessionsCh chan chan []status.ServerSession // non-nil if consumeEventbusTopics is running
|
||||||
hasNodeAttrDisableRelayServer bool // tailcfg.NodeAttrDisableRelayServer
|
hasNodeAttrDisableRelayServer bool // tailcfg.NodeAttrDisableRelayServer
|
||||||
}
|
}
|
||||||
@ -119,15 +119,13 @@ func (e *extension) handleBusLifetimeLocked() {
|
|||||||
if !busShouldBeRunning {
|
if !busShouldBeRunning {
|
||||||
e.disconnectFromBusLocked()
|
e.disconnectFromBusLocked()
|
||||||
return
|
return
|
||||||
}
|
} else if e.eventSubs != nil {
|
||||||
if e.busDoneCh != nil {
|
|
||||||
return // already running
|
return // already running
|
||||||
}
|
}
|
||||||
port := *e.port
|
|
||||||
e.disconnectFromBusCh = make(chan struct{})
|
ec := e.bus.Client("relayserver.extension")
|
||||||
e.busDoneCh = make(chan struct{})
|
|
||||||
e.debugSessionsCh = make(chan chan []status.ServerSession)
|
e.debugSessionsCh = make(chan chan []status.ServerSession)
|
||||||
go e.consumeEventbusTopics(port)
|
e.eventSubs = ptr.To(ec.Monitor(e.consumeEventbusTopics(ec, *e.port)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *extension) selfNodeViewChanged(nodeView tailcfg.NodeView) {
|
func (e *extension) selfNodeViewChanged(nodeView tailcfg.NodeView) {
|
||||||
@ -175,77 +173,72 @@ var overrideAddrs = sync.OnceValue(func() (ret []netip.Addr) {
|
|||||||
|
|
||||||
// consumeEventbusTopics serves endpoint allocation requests over the eventbus.
|
// consumeEventbusTopics serves endpoint allocation requests over the eventbus.
|
||||||
// It also serves [relayServer] debug information on a channel.
|
// It also serves [relayServer] debug information on a channel.
|
||||||
// consumeEventbusTopics must never acquire [extension.mu], which can be held by
|
// consumeEventbusTopics must never acquire [extension.mu], which can be held
|
||||||
// other goroutines while waiting to receive on [extension.busDoneCh] or the
|
// by other goroutines while waiting to receive on [extension.eventSubs] or the
|
||||||
// inner [extension.debugSessionsCh] channel.
|
// inner [extension.debugSessionsCh] channel.
|
||||||
func (e *extension) consumeEventbusTopics(port int) {
|
func (e *extension) consumeEventbusTopics(ec *eventbus.Client, port int) func(*eventbus.Client) {
|
||||||
defer close(e.busDoneCh)
|
reqSub := eventbus.Subscribe[magicsock.UDPRelayAllocReq](ec)
|
||||||
|
respPub := eventbus.Publish[magicsock.UDPRelayAllocResp](ec)
|
||||||
|
debugSessionsCh := e.debugSessionsCh
|
||||||
|
|
||||||
eventClient := e.bus.Client("relayserver.extension")
|
return func(ec *eventbus.Client) {
|
||||||
reqSub := eventbus.Subscribe[magicsock.UDPRelayAllocReq](eventClient)
|
var rs relayServer // lazily initialized
|
||||||
respPub := eventbus.Publish[magicsock.UDPRelayAllocResp](eventClient)
|
defer func() {
|
||||||
defer eventClient.Close()
|
if rs != nil {
|
||||||
|
rs.Close()
|
||||||
var rs relayServer // lazily initialized
|
|
||||||
defer func() {
|
|
||||||
if rs != nil {
|
|
||||||
rs.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-e.disconnectFromBusCh:
|
|
||||||
return
|
|
||||||
case <-eventClient.Done():
|
|
||||||
return
|
|
||||||
case respCh := <-e.debugSessionsCh:
|
|
||||||
if rs == nil {
|
|
||||||
// Don't initialize the server simply for a debug request.
|
|
||||||
respCh <- nil
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
sessions := rs.GetSessions()
|
}()
|
||||||
respCh <- sessions
|
for {
|
||||||
case req := <-reqSub.Events():
|
select {
|
||||||
if rs == nil {
|
case <-ec.Done():
|
||||||
var err error
|
return
|
||||||
rs, err = udprelay.NewServer(e.logf, port, overrideAddrs())
|
case respCh := <-debugSessionsCh:
|
||||||
if err != nil {
|
if rs == nil {
|
||||||
e.logf("error initializing server: %v", err)
|
// Don't initialize the server simply for a debug request.
|
||||||
|
respCh <- nil
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
sessions := rs.GetSessions()
|
||||||
se, err := rs.AllocateEndpoint(req.Message.ClientDisco[0], req.Message.ClientDisco[1])
|
respCh <- sessions
|
||||||
if err != nil {
|
case req := <-reqSub.Events():
|
||||||
e.logf("error allocating endpoint: %v", err)
|
if rs == nil {
|
||||||
continue
|
var err error
|
||||||
}
|
rs, err = udprelay.NewServer(e.logf, port, overrideAddrs())
|
||||||
respPub.Publish(magicsock.UDPRelayAllocResp{
|
if err != nil {
|
||||||
ReqRxFromNodeKey: req.RxFromNodeKey,
|
e.logf("error initializing server: %v", err)
|
||||||
ReqRxFromDiscoKey: req.RxFromDiscoKey,
|
continue
|
||||||
Message: &disco.AllocateUDPRelayEndpointResponse{
|
}
|
||||||
Generation: req.Message.Generation,
|
}
|
||||||
UDPRelayEndpoint: disco.UDPRelayEndpoint{
|
se, err := rs.AllocateEndpoint(req.Message.ClientDisco[0], req.Message.ClientDisco[1])
|
||||||
ServerDisco: se.ServerDisco,
|
if err != nil {
|
||||||
ClientDisco: se.ClientDisco,
|
e.logf("error allocating endpoint: %v", err)
|
||||||
LamportID: se.LamportID,
|
continue
|
||||||
VNI: se.VNI,
|
}
|
||||||
BindLifetime: se.BindLifetime.Duration,
|
respPub.Publish(magicsock.UDPRelayAllocResp{
|
||||||
SteadyStateLifetime: se.SteadyStateLifetime.Duration,
|
ReqRxFromNodeKey: req.RxFromNodeKey,
|
||||||
AddrPorts: se.AddrPorts,
|
ReqRxFromDiscoKey: req.RxFromDiscoKey,
|
||||||
|
Message: &disco.AllocateUDPRelayEndpointResponse{
|
||||||
|
Generation: req.Message.Generation,
|
||||||
|
UDPRelayEndpoint: disco.UDPRelayEndpoint{
|
||||||
|
ServerDisco: se.ServerDisco,
|
||||||
|
ClientDisco: se.ClientDisco,
|
||||||
|
LamportID: se.LamportID,
|
||||||
|
VNI: se.VNI,
|
||||||
|
BindLifetime: se.BindLifetime.Duration,
|
||||||
|
SteadyStateLifetime: se.SteadyStateLifetime.Duration,
|
||||||
|
AddrPorts: se.AddrPorts,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *extension) disconnectFromBusLocked() {
|
func (e *extension) disconnectFromBusLocked() {
|
||||||
if e.busDoneCh != nil {
|
if e.eventSubs != nil {
|
||||||
close(e.disconnectFromBusCh)
|
e.eventSubs.Close()
|
||||||
<-e.busDoneCh
|
e.eventSubs = nil
|
||||||
e.busDoneCh = nil
|
|
||||||
e.disconnectFromBusCh = nil
|
|
||||||
e.debugSessionsCh = nil
|
e.debugSessionsCh = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,7 +263,7 @@ func (e *extension) serverStatus() status.ServerStatus {
|
|||||||
UDPPort: nil,
|
UDPPort: nil,
|
||||||
Sessions: nil,
|
Sessions: nil,
|
||||||
}
|
}
|
||||||
if e.port == nil || e.busDoneCh == nil {
|
if e.port == nil || e.eventSubs == nil {
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
st.UDPPort = ptr.To(*e.port)
|
st.UDPPort = ptr.To(*e.port)
|
||||||
@ -281,7 +274,7 @@ func (e *extension) serverStatus() status.ServerStatus {
|
|||||||
resp := <-ch
|
resp := <-ch
|
||||||
st.Sessions = resp
|
st.Sessions = resp
|
||||||
return st
|
return st
|
||||||
case <-e.busDoneCh:
|
case <-e.eventSubs.Done():
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,8 +101,8 @@ func Test_extension_profileStateChanged(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer e.disconnectFromBusLocked()
|
defer e.disconnectFromBusLocked()
|
||||||
e.profileStateChanged(ipn.LoginProfileView{}, tt.args.prefs, tt.args.sameNode)
|
e.profileStateChanged(ipn.LoginProfileView{}, tt.args.prefs, tt.args.sameNode)
|
||||||
if tt.wantBusRunning != (e.busDoneCh != nil) {
|
if tt.wantBusRunning != (e.eventSubs != nil) {
|
||||||
t.Errorf("wantBusRunning: %v != (e.busDoneCh != nil): %v", tt.wantBusRunning, e.busDoneCh != nil)
|
t.Errorf("wantBusRunning: %v != (e.eventSubs != nil): %v", tt.wantBusRunning, e.eventSubs != nil)
|
||||||
}
|
}
|
||||||
if (tt.wantPort == nil) != (e.port == nil) {
|
if (tt.wantPort == nil) != (e.port == nil) {
|
||||||
t.Errorf("(tt.wantPort == nil): %v != (e.port == nil): %v", tt.wantPort == nil, e.port == nil)
|
t.Errorf("(tt.wantPort == nil): %v != (e.port == nil): %v", tt.wantPort == nil, e.port == nil)
|
||||||
@ -118,7 +118,7 @@ func Test_extension_handleBusLifetimeLocked(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
shutdown bool
|
shutdown bool
|
||||||
port *int
|
port *int
|
||||||
busDoneCh chan struct{}
|
eventSubs *eventbus.Monitor
|
||||||
hasNodeAttrDisableRelayServer bool
|
hasNodeAttrDisableRelayServer bool
|
||||||
wantBusRunning bool
|
wantBusRunning bool
|
||||||
}{
|
}{
|
||||||
@ -157,13 +157,13 @@ func Test_extension_handleBusLifetimeLocked(t *testing.T) {
|
|||||||
bus: eventbus.New(),
|
bus: eventbus.New(),
|
||||||
shutdown: tt.shutdown,
|
shutdown: tt.shutdown,
|
||||||
port: tt.port,
|
port: tt.port,
|
||||||
busDoneCh: tt.busDoneCh,
|
eventSubs: tt.eventSubs,
|
||||||
hasNodeAttrDisableRelayServer: tt.hasNodeAttrDisableRelayServer,
|
hasNodeAttrDisableRelayServer: tt.hasNodeAttrDisableRelayServer,
|
||||||
}
|
}
|
||||||
e.handleBusLifetimeLocked()
|
e.handleBusLifetimeLocked()
|
||||||
defer e.disconnectFromBusLocked()
|
defer e.disconnectFromBusLocked()
|
||||||
if tt.wantBusRunning != (e.busDoneCh != nil) {
|
if tt.wantBusRunning != (e.eventSubs != nil) {
|
||||||
t.Errorf("wantBusRunning: %v != (e.busDoneCh != nil): %v", tt.wantBusRunning, e.busDoneCh != nil)
|
t.Errorf("wantBusRunning: %v != (e.eventSubs != nil): %v", tt.wantBusRunning, e.eventSubs != nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user