Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

chore: improve tailnet integration test#18124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
spikecurtis merged 6 commits intomainfromspike/15523-integration-test
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 154 additions & 65 deletionstailnet/test/integration/integration.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ import (
"golang.org/x/xerrors"
"tailscale.com/derp"
"tailscale.com/derp/derphttp"
"tailscale.com/net/packet"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/wgengine/capture"

"cdr.dev/slog"
"github.com/coder/coder/v2/coderd/httpapi"
Expand All@@ -54,35 +56,36 @@ type Client struct {
ID uuid.UUID
ListenPortuint16
ShouldRunTestsbool
TunnelSrcbool
}

varClient1=Client{
Number:ClientNumber1,
ID:uuid.MustParse("00000000-0000-0000-0000-000000000001"),
ListenPort:client1Port,
ShouldRunTests:true,
TunnelSrc:true,
}

varClient2=Client{
Number:ClientNumber2,
ID:uuid.MustParse("00000000-0000-0000-0000-000000000002"),
ListenPort:client2Port,
ShouldRunTests:false,
TunnelSrc:false,
}

typeTestTopologystruct {
Namestring
// SetupNetworking creates interfaces and network namespaces for the test.
// The most simple implementation is NetworkSetupDefault, which only creates
// a network namespace shared for all tests.
SetupNetworkingfunc(t*testing.T,logger slog.Logger)TestNetworking

NetworkingProviderNetworkingProvider

// Server is the server starter for the test. It is executed in the server
// subprocess.
ServerServerStarter
// StartClient gets called in each client subprocess. It's expected to
//ClientStarter.StartClient gets called in each client subprocess. It's expected to
// create the tailnet.Conn and ensure connectivity to it's peer.
StartClientfunc(t*testing.T,logger slog.Logger,serverURL*url.URL,derpMap*tailcfg.DERPMap,meClient,peerClient)*tailnet.Conn
ClientStarterClientStarter

// RunTests is the main test function. It's called in each of the client
// subprocesses. If tests can only run once, they should check the client ID
Expand All@@ -97,6 +100,17 @@ type ServerStarter interface {
StartServer(t*testing.T,logger slog.Logger,listenAddrstring)
}

typeNetworkingProviderinterface {
// SetupNetworking creates interfaces and network namespaces for the test.
// The most simple implementation is NetworkSetupDefault, which only creates
// a network namespace shared for all tests.
SetupNetworking(t*testing.T,logger slog.Logger)TestNetworking
}

typeClientStarterinterface {
StartClient(t*testing.T,logger slog.Logger,serverURL*url.URL,derpMap*tailcfg.DERPMap,meClient,peerClient)*tailnet.Conn
}

typeSimpleServerOptionsstruct {
// FailUpgradeDERP will make the DERP server fail to handle the initial DERP
// upgrade in a way that causes the client to fallback to
Expand DownExpand Up@@ -369,77 +383,107 @@ http {
_,_=ExecBackground(t,"server.nginx",nil,"nginx", []string{"-c",cfgPath})
}

// StartClientDERP creates a client connection to the server for coordination
// and creates a tailnet.Conn which will only use DERP to connect to the peer.
funcStartClientDERP(t*testing.T,logger slog.Logger,serverURL*url.URL,derpMap*tailcfg.DERPMap,me,peerClient)*tailnet.Conn {
returnstartClientOptions(t,logger,serverURL,me,peer,&tailnet.Options{
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.PrefixFromUUID(me.ID)},
DERPMap:derpMap,
BlockEndpoints:true,
Logger:logger,
DERPForceWebSockets:false,
ListenPort:me.ListenPort,
// These tests don't have internet connection, so we need to force
// magicsock to do anything.
ForceNetworkUp:true,
})
typeBasicClientStarterstruct {
BlockEndpointsbool
DERPForceWebsocketsbool
// WaitForConnection means wait for (any) peer connection before returning from StartClient
WaitForConnectionbool
// WaitForConnection means wait for a direct peer connection before returning from StartClient
WaitForDirectbool
// Service is a network service (e.g. an echo server) to start on the client. If Wait* is set, the service is
// started prior to waiting.
ServiceNetworkService
LogPacketsbool
}

// StartClientDERPWebSockets does the same thing as StartClientDERP but will
// only use DERP WebSocket fallback.
funcStartClientDERPWebSockets(t*testing.T,logger slog.Logger,serverURL*url.URL,derpMap*tailcfg.DERPMap,me,peerClient)*tailnet.Conn {
returnstartClientOptions(t,logger,serverURL,me,peer,&tailnet.Options{
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.PrefixFromUUID(me.ID)},
DERPMap:derpMap,
BlockEndpoints:true,
Logger:logger,
DERPForceWebSockets:true,
ListenPort:me.ListenPort,
// These tests don't have internet connection, so we need to force
// magicsock to do anything.
ForceNetworkUp:true,
})
typeNetworkServiceinterface {
StartService(t*testing.T,logger slog.Logger,conn*tailnet.Conn)
}

// StartClientDirect does the same thing as StartClientDERP but disables
// BlockEndpoints (which enables Direct connections), and waits for a direct
// connection to be established between the two peers.
funcStartClientDirect(t*testing.T,logger slog.Logger,serverURL*url.URL,derpMap*tailcfg.DERPMap,me,peerClient)*tailnet.Conn {
func (bBasicClientStarter)StartClient(t*testing.T,logger slog.Logger,serverURL*url.URL,derpMap*tailcfg.DERPMap,me,peerClient)*tailnet.Conn {
varhook capture.Callback
ifb.LogPackets {
pktLogger:=packetLogger{logger}
hook=pktLogger.LogPacket
}
conn:=startClientOptions(t,logger,serverURL,me,peer,&tailnet.Options{
Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.PrefixFromUUID(me.ID)},
DERPMap:derpMap,
BlockEndpoints:false,
BlockEndpoints:b.BlockEndpoints,
Logger:logger,
DERPForceWebSockets:true,
DERPForceWebSockets:b.DERPForceWebsockets,
ListenPort:me.ListenPort,
// These tests don't have internet connection, so we need to force
// magicsock to do anything.
ForceNetworkUp:true,
CaptureHook:hook,
})

// Wait for direct connection to be established.
peerIP:=tailnet.TailscaleServicePrefix.AddrFromUUID(peer.ID)
require.Eventually(t,func()bool {
t.Log("attempting ping to peer to judge direct connection")
ctx:=testutil.Context(t,testutil.WaitShort)
_,p2p,pong,err:=conn.Ping(ctx,peerIP)
iferr!=nil {
t.Logf("ping failed: %v",err)
returnfalse
}
if!p2p {
t.Log("ping succeeded, but not direct yet")
returnfalse
}
t.Logf("ping succeeded, direct connection established via %s",pong.Endpoint)
returntrue
},testutil.WaitLong,testutil.IntervalMedium)
ifb.Service!=nil {
b.Service.StartService(t,logger,conn)
}

ifb.WaitForConnection||b.WaitForDirect {
// Wait for connection to be established.
peerIP:=tailnet.TailscaleServicePrefix.AddrFromUUID(peer.ID)
require.Eventually(t,func()bool {
t.Log("attempting ping to peer to judge direct connection")
ctx:=testutil.Context(t,testutil.WaitShort)
_,p2p,pong,err:=conn.Ping(ctx,peerIP)
iferr!=nil {
t.Logf("ping failed: %v",err)
returnfalse
}
if!p2p&&b.WaitForDirect {
t.Log("ping succeeded, but not direct yet")
returnfalse
}
t.Logf("ping succeeded, p2p=%t, endpoint=%s",p2p,pong.Endpoint)
returntrue
},testutil.WaitLong,testutil.IntervalMedium)
}

returnconn
}

typeClientStarterstruct {
Options*tailnet.Options
constEchoPort=2381

typeUDPEchoServicestruct{}

func (UDPEchoService)StartService(t*testing.T,logger slog.Logger,_*tailnet.Conn) {
// tailnet doesn't handle UDP connections "in-process" the way we do for TCP, so we need to listen in the OS,
// and tailnet will forward packets.
l,err:=net.ListenUDP("udp",&net.UDPAddr{
IP:net.IPv6zero,// all interfaces
Port:EchoPort,
})
require.NoError(t,err)
logger.Info(context.Background(),"started UDPEcho server")
t.Cleanup(func() {
lCloseErr:=l.Close()
iflCloseErr!=nil {
t.Logf("error closing UDPEcho listener: %v",lCloseErr)
}
})
gofunc() {
buf:=make([]byte,1500)
for {
n,remote,readErr:=l.ReadFromUDP(buf)
ifreadErr!=nil {
logger.Info(context.Background(),"error reading UDPEcho listener",slog.Error(readErr))
return
}
logger.Info(context.Background(),"received UDPEcho packet",
slog.F("len",n),slog.F("remote",remote))
n,writeErr:=l.WriteToUDP(buf[:n],remote)
ifwriteErr!=nil {
logger.Info(context.Background(),"error writing UDPEcho listener",slog.Error(writeErr))
return
}
logger.Info(context.Background(),"wrote UDPEcho packet",
slog.F("len",n),slog.F("remote",remote))
}
}()
}

funcstartClientOptions(t*testing.T,logger slog.Logger,serverURL*url.URL,me,peerClient,options*tailnet.Options)*tailnet.Conn {
Expand DownExpand Up@@ -467,9 +511,16 @@ func startClientOptions(t *testing.T, logger slog.Logger, serverURL *url.URL, me
_=conn.Close()
})

ctrl:=tailnet.NewTunnelSrcCoordController(logger,conn)
ctrl.AddDestination(peer.ID)
coordination:=ctrl.New(coord)
varcoordination tailnet.CloserWaiter
ifme.TunnelSrc {
ctrl:=tailnet.NewTunnelSrcCoordController(logger,conn)
ctrl.AddDestination(peer.ID)
coordination=ctrl.New(coord)
}else {
// use the "Agent" controller so that we act as a tunnel destination and send "ReadyForHandshake" acks.
ctrl:=tailnet.NewAgentCoordinationController(logger,conn)
coordination=ctrl.New(coord)
}
t.Cleanup(func() {
cctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitShort)
defercancel()
Expand All@@ -492,11 +543,17 @@ func basicDERPMap(serverURLStr string) (*tailcfg.DERPMap, error) {
}

hostname:=serverURL.Hostname()
ipv4:=""
ipv4:="none"
ipv6:="none"
ip,err:=netip.ParseAddr(hostname)
iferr==nil {
hostname=""
ipv4=ip.String()
ifip.Is4() {
ipv4=ip.String()
}
ifip.Is6() {
ipv6=ip.String()
}
}

return&tailcfg.DERPMap{
Expand All@@ -511,7 +568,7 @@ func basicDERPMap(serverURLStr string) (*tailcfg.DERPMap, error) {
RegionID:1,
HostName:hostname,
IPv4:ipv4,
IPv6:"none",
IPv6:ipv6,
DERPPort:port,
STUNPort:-1,
ForceHTTP:true,
Expand DownExpand Up@@ -648,3 +705,35 @@ func (w *testWriter) Flush() {
}
w.capturedLines=nil
}

typepacketLoggerstruct {
l slog.Logger
}

func (ppacketLogger)LogPacket(path capture.Path,when time.Time,pkt []byte,_ packet.CaptureMeta) {
q:=new(packet.Parsed)
q.Decode(pkt)
p.l.Info(context.Background(),"Packet",
slog.F("path",pathString(path)),
slog.F("when",when),
slog.F("decode",q.String()),
slog.F("len",len(pkt)),
)
}

funcpathString(path capture.Path)string {
switchpath {
casecapture.FromLocal:
return"Local"
casecapture.FromPeer:
return"Peer"
casecapture.SynthesizedToLocal:
return"SynthesizedToLocal"
casecapture.SynthesizedToPeer:
return"SynthesizedToPeer"
casecapture.PathDisco:
return"Disco"
default:
return"<<UNKNOWN>>"
}
}
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp