mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
I think this will resolve #12136 but lets get a proper test at the system level before closing. Before this change, we only register the node callback at start of day for the server tailnet. If the coordinator changes, like we know happens when we are licensed for the PGCoordinator, we close the connection to the old coord, and open a new one to the new coord. The callback is designed to direct the updates to the new coordinator, but there is nothing that specifically triggers it to fire after we connect to the new coordinator. If we have STUN, then period re-STUNs will generally get it to fire eventually, but without STUN it we could go indefinitely without a callback. This PR changes the servertailnet to re-register the callback each time we reconnect to the coordinator. Registering a callback (even if it's the same callback) triggers an immediate call with our node information, so the new coordinator will have it.
78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package coderd
|
|
|
|
import (
|
|
"context"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
"cdr.dev/slog"
|
|
"cdr.dev/slog/sloggers/slogtest"
|
|
"github.com/coder/coder/v2/tailnet"
|
|
"github.com/coder/coder/v2/tailnet/tailnettest"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
// TestServerTailnet_Reconnect tests that ServerTailnet calls SetAllPeersLost on the Coordinatee
|
|
// (tailnet.Conn in production) when it disconnects from the Coordinator (via MultiAgentConn) and
|
|
// reconnects.
|
|
func TestServerTailnet_Reconnect(t *testing.T) {
|
|
t.Parallel()
|
|
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
|
|
ctrl := gomock.NewController(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
|
|
mMultiAgent0 := tailnettest.NewMockMultiAgentConn(ctrl)
|
|
mMultiAgent1 := tailnettest.NewMockMultiAgentConn(ctrl)
|
|
mac := make(chan tailnet.MultiAgentConn, 2)
|
|
mac <- mMultiAgent0
|
|
mac <- mMultiAgent1
|
|
mCoord := tailnettest.NewMockCoordinatee(ctrl)
|
|
|
|
uut := &ServerTailnet{
|
|
ctx: ctx,
|
|
logger: logger,
|
|
coordinatee: mCoord,
|
|
getMultiAgent: func(ctx context.Context) (tailnet.MultiAgentConn, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
case m := <-mac:
|
|
return m, nil
|
|
}
|
|
},
|
|
agentConn: atomic.Pointer[tailnet.MultiAgentConn]{},
|
|
agentConnectionTimes: make(map[uuid.UUID]time.Time),
|
|
}
|
|
// reinit the Coordinator once, to load mMultiAgent0
|
|
mCoord.EXPECT().SetNodeCallback(gomock.Any()).Times(1)
|
|
uut.reinitCoordinator()
|
|
|
|
mMultiAgent0.EXPECT().NextUpdate(gomock.Any()).
|
|
Times(1).
|
|
Return(nil, false) // this indicates there are no more updates
|
|
closed0 := mMultiAgent0.EXPECT().IsClosed().
|
|
Times(1).
|
|
Return(true) // this triggers reconnect
|
|
setLost := mCoord.EXPECT().SetAllPeersLost().Times(1).After(closed0)
|
|
mCoord.EXPECT().SetNodeCallback(gomock.Any()).Times(1).After(closed0)
|
|
mMultiAgent1.EXPECT().NextUpdate(gomock.Any()).
|
|
Times(1).
|
|
After(setLost).
|
|
Return(nil, false)
|
|
mMultiAgent1.EXPECT().IsClosed().
|
|
Times(1).
|
|
Return(false) // this causes us to exit and not reconnect
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
uut.watchAgentUpdates()
|
|
close(done)
|
|
}()
|
|
|
|
testutil.RequireRecvCtx(ctx, t, done)
|
|
}
|