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

Commit5e7d212

Browse files
committed
feat(cli): add p2p diagnostics to ping
1 parentc8eacc6 commit5e7d212

File tree

8 files changed

+240
-5
lines changed

8 files changed

+240
-5
lines changed

‎agent/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func (a *agent) apiHandler() http.Handler {
3737
}
3838
promHandler:=PrometheusMetricsHandler(a.prometheusRegistry,a.logger)
3939
r.Get("/api/v0/listening-ports",lp.handler)
40+
r.Get("/api/v0/netcheck",a.HandleNetCheck)
4041
r.Get("/debug/logs",a.HandleHTTPDebugLogs)
4142
r.Get("/debug/magicsock",a.HandleHTTPDebugMagicsock)
4243
r.Get("/debug/magicsock/debug-logging/{state}",a.HandleHTTPMagicsockDebugLoggingState)

‎agent/health.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package agent
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/coder/coder/v2/coderd/healthcheck/health"
7+
"github.com/coder/coder/v2/coderd/httpapi"
8+
"github.com/coder/coder/v2/codersdk/healthsdk"
9+
)
10+
11+
func (a*agent)HandleNetCheck(rw http.ResponseWriter,r*http.Request) {
12+
ni:=a.TailnetConn().GetNetInfo()
13+
14+
httpapi.Write(r.Context(),rw,http.StatusOK, healthsdk.AgentNetcheckReport{
15+
BaseReport: healthsdk.BaseReport{
16+
Severity:health.SeverityOK,
17+
},
18+
NetInfo:ni,
19+
})
20+
}

‎cli/cliui/agent.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ import (
1010

1111
"github.com/google/uuid"
1212
"golang.org/x/xerrors"
13+
"tailscale.com/tailcfg"
1314

1415
"github.com/coder/coder/v2/codersdk"
16+
"github.com/coder/coder/v2/codersdk/healthsdk"
17+
"github.com/coder/coder/v2/codersdk/workspacesdk"
1518
"github.com/coder/coder/v2/tailnet"
1619
)
1720

@@ -346,3 +349,42 @@ func PeerDiagnostics(w io.Writer, d tailnet.PeerDiagnostics) {
346349
_,_=fmt.Fprint(w,"✘ Wireguard is not connected\n")
347350
}
348351
}
352+
353+
typeConnDiagsstruct {
354+
Info*workspacesdk.AgentConnectionInfo
355+
PingP2Pbool
356+
LocalNetInfo*tailcfg.NetInfo
357+
FetchAgentNetcheckfunc() (healthsdk.AgentNetcheckReport,error)
358+
// TODO: More diagnostics
359+
}
360+
361+
funcConnDiagnostics(w io.Writer,dConnDiags) {
362+
ifd.PingP2P {
363+
_,_=fmt.Fprint(w,"✔ You are connected directly, peer-to-peer (p2p).\n")
364+
return
365+
}
366+
_,_=fmt.Fprint(w,"❗ You are connected via a DERP relay, not directly, peer-to-peer (p2p).\n")
367+
368+
ifd.Info!=nil&&d.Info.DisableDirectConnections {
369+
_,_=fmt.Fprint(w,"❗ Your Coder administrator has blocked direct connections.\n")
370+
return
371+
}
372+
373+
ifd.Info!=nil&&d.Info.DERPMap!=nil&&!d.Info.DERPMap.HasSTUN() {
374+
_,_=fmt.Fprint(w,"✘ The workspace agent appears to be unable to reach any STUN servers.\nhttps://coder.com/docs/networking/stun")
375+
}
376+
377+
ifd.LocalNetInfo!=nil&&d.LocalNetInfo.MappingVariesByDestIP.EqualBool(true) {
378+
_,_=fmt.Fprint(w,"❗ Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers.\n")
379+
}
380+
381+
agentReport,err:=d.FetchAgentNetcheck()
382+
iferr!=nil {
383+
_,_=fmt.Fprintf(w,"Failed to retrieve netcheck report from agent: %v\n",err)
384+
return
385+
}
386+
387+
ifagentReport.NetInfo!=nil&&agentReport.NetInfo.MappingVariesByDestIP.EqualBool(true) {
388+
_,_=fmt.Fprint(w,"❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers.\n")
389+
}
390+
}

‎cli/cliui/agent_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
"github.com/coder/coder/v2/cli/cliui"
2323
"github.com/coder/coder/v2/coderd/util/ptr"
2424
"github.com/coder/coder/v2/codersdk"
25+
"github.com/coder/coder/v2/codersdk/healthsdk"
26+
"github.com/coder/coder/v2/codersdk/workspacesdk"
2527
"github.com/coder/coder/v2/tailnet"
2628
"github.com/coder/coder/v2/testutil"
2729
"github.com/coder/serpent"
@@ -672,3 +674,117 @@ func TestPeerDiagnostics(t *testing.T) {
672674
})
673675
}
674676
}
677+
678+
funcTestConnDiagnostics(t*testing.T) {
679+
t.Parallel()
680+
testCases:= []struct {
681+
namestring
682+
diags cliui.ConnDiags
683+
want []*regexp.Regexp
684+
}{
685+
{
686+
name:"Direct",
687+
diags: cliui.ConnDiags{
688+
Info:&workspacesdk.AgentConnectionInfo{},
689+
PingP2P:true,
690+
LocalNetInfo:&tailcfg.NetInfo{},
691+
FetchAgentNetcheck:func() (healthsdk.AgentNetcheckReport,error) {
692+
return healthsdk.AgentNetcheckReport{},nil
693+
},
694+
},
695+
want: []*regexp.Regexp{
696+
regexp.MustCompile(`^✔ You are connected directly, peer-to-peer \(p2p\).$`),
697+
},
698+
},
699+
{
700+
name:"DirectBlocked",
701+
diags: cliui.ConnDiags{
702+
Info:&workspacesdk.AgentConnectionInfo{
703+
DisableDirectConnections:true,
704+
},
705+
FetchAgentNetcheck:func() (healthsdk.AgentNetcheckReport,error) {
706+
return healthsdk.AgentNetcheckReport{},nil
707+
},
708+
},
709+
want: []*regexp.Regexp{
710+
regexp.MustCompile(`^❗ You are connected via a DERP relay, not directly, peer-to-peer \(p2p\).$`),
711+
regexp.MustCompile(`^❗ Your Coder administrator has blocked direct connections.$`),
712+
},
713+
},
714+
{
715+
name:"NoStun",
716+
diags: cliui.ConnDiags{
717+
Info:&workspacesdk.AgentConnectionInfo{
718+
DERPMap:&tailcfg.DERPMap{},
719+
},
720+
LocalNetInfo:&tailcfg.NetInfo{},
721+
FetchAgentNetcheck:func() (healthsdk.AgentNetcheckReport,error) {
722+
return healthsdk.AgentNetcheckReport{},nil
723+
},
724+
},
725+
want: []*regexp.Regexp{
726+
regexp.MustCompile(`^❗ You are connected via a DERP relay, not directly, peer-to-peer \(p2p\).$`),
727+
regexp.MustCompile(`^✘ The workspace agent appears to be unable to reach any STUN servers.$`),
728+
},
729+
},
730+
{
731+
name:"ClientHardNat",
732+
diags: cliui.ConnDiags{
733+
LocalNetInfo:&tailcfg.NetInfo{
734+
MappingVariesByDestIP:"true",
735+
},
736+
FetchAgentNetcheck:func() (healthsdk.AgentNetcheckReport,error) {
737+
return healthsdk.AgentNetcheckReport{},nil
738+
},
739+
},
740+
want: []*regexp.Regexp{
741+
regexp.MustCompile(`^❗ You are connected via a DERP relay, not directly, peer-to-peer \(p2p\).$`),
742+
regexp.MustCompile(`^❗ Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers.`),
743+
},
744+
},
745+
{
746+
name:"AgentHardNat",
747+
diags: cliui.ConnDiags{
748+
Info:&workspacesdk.AgentConnectionInfo{},
749+
PingP2P:false,
750+
LocalNetInfo:&tailcfg.NetInfo{},
751+
FetchAgentNetcheck:func() (healthsdk.AgentNetcheckReport,error) {
752+
return healthsdk.AgentNetcheckReport{
753+
NetInfo:&tailcfg.NetInfo{MappingVariesByDestIP:"true"},
754+
},nil
755+
},
756+
},
757+
want: []*regexp.Regexp{
758+
regexp.MustCompile(`^❗ You are connected via a DERP relay, not directly, peer-to-peer \(p2p\).$`),
759+
regexp.MustCompile(`^❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers.`),
760+
},
761+
},
762+
}
763+
for_,tc:=rangetestCases {
764+
tc:=tc
765+
t.Run(tc.name,func(t*testing.T) {
766+
t.Parallel()
767+
r,w:=io.Pipe()
768+
gofunc() {
769+
deferw.Close()
770+
cliui.ConnDiagnostics(w,tc.diags)
771+
}()
772+
s:=bufio.NewScanner(r)
773+
i:=0
774+
got:=make([]string,0)
775+
fors.Scan() {
776+
got=append(got,s.Text())
777+
ifi<len(tc.want) {
778+
reg:=tc.want[i]
779+
ifreg.Match(s.Bytes()) {
780+
i++
781+
}
782+
}
783+
}
784+
ifi<len(tc.want) {
785+
t.Logf("failed to match regexp: %s\ngot:\n%s",tc.want[i].String(),strings.Join(got,"\n"))
786+
t.FailNow()
787+
}
788+
})
789+
}
790+
}

‎cli/ping.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/coder/coder/v2/cli/cliui"
1616
"github.com/coder/coder/v2/codersdk"
17+
"github.com/coder/coder/v2/codersdk/healthsdk"
1718
"github.com/coder/coder/v2/codersdk/workspacesdk"
1819
"github.com/coder/serpent"
1920
)
@@ -61,7 +62,8 @@ func (r *RootCmd) ping() *serpent.Command {
6162
if!r.disableNetworkTelemetry {
6263
opts.EnableTelemetry=true
6364
}
64-
conn,err:=workspacesdk.New(client).DialAgent(ctx,workspaceAgent.ID,opts)
65+
client:=workspacesdk.New(client)
66+
conn,err:=client.DialAgent(ctx,workspaceAgent.ID,opts)
6567
iferr!=nil {
6668
returnerr
6769
}
@@ -138,11 +140,26 @@ func (r *RootCmd) ping() *serpent.Command {
138140
)
139141

140142
ifn==int(pingNum) {
141-
diags:=conn.GetPeerDiagnostics()
142-
cliui.PeerDiagnostics(inv.Stdout,diags)
143-
returnnil
143+
break
144144
}
145145
}
146+
ctx,cancel=context.WithTimeout(inv.Context(),30*time.Second)
147+
defercancel()
148+
diags:=conn.GetPeerDiagnostics()
149+
cliui.PeerDiagnostics(inv.Stdout,diags)
150+
151+
connDiags:= cliui.ConnDiags{
152+
PingP2P:didP2p,
153+
FetchAgentNetcheck:func() (healthsdk.AgentNetcheckReport,error) {
154+
returnconn.NetCheck(ctx)
155+
},
156+
}
157+
connInfo,err:=client.AgentConnectionInfoGeneric(ctx)
158+
iferr==nil {
159+
connDiags.Info=&connInfo
160+
}
161+
cliui.ConnDiagnostics(inv.Stdout,connDiags)
162+
returnnil
146163
},
147164
}
148165

‎codersdk/healthsdk/healthsdk.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,9 @@ type ClientNetcheckReport struct {
273273
DERPDERPHealthReport`json:"derp"`
274274
InterfacesInterfacesReport`json:"interfaces"`
275275
}
276+
277+
// @typescript-ignore AgentNetcheckReport
278+
typeAgentNetcheckReportstruct {
279+
BaseReport
280+
NetInfo*tailcfg.NetInfo`json:"net_info"`
281+
}

‎codersdk/workspacesdk/agentconn.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/coder/coder/v2/coderd/tracing"
2424
"github.com/coder/coder/v2/codersdk"
25+
"github.com/coder/coder/v2/codersdk/healthsdk"
2526
"github.com/coder/coder/v2/tailnet"
2627
)
2728

@@ -241,6 +242,23 @@ func (c *AgentConn) ListeningPorts(ctx context.Context) (codersdk.WorkspaceAgent
241242
returnresp,json.NewDecoder(res.Body).Decode(&resp)
242243
}
243244

245+
// NetCheck returns a network check report from the workspace agent.
246+
func (c*AgentConn)NetCheck(ctx context.Context) (healthsdk.AgentNetcheckReport,error) {
247+
ctx,span:=tracing.StartSpan(ctx)
248+
deferspan.End()
249+
res,err:=c.apiRequest(ctx,http.MethodGet,"/api/v0/netcheck",nil)
250+
iferr!=nil {
251+
return healthsdk.AgentNetcheckReport{},xerrors.Errorf("do request: %w",err)
252+
}
253+
deferres.Body.Close()
254+
ifres.StatusCode!=http.StatusOK {
255+
return healthsdk.AgentNetcheckReport{},codersdk.ReadBodyAsError(res)
256+
}
257+
258+
varresp healthsdk.AgentNetcheckReport
259+
returnresp,json.NewDecoder(res.Body).Decode(&resp)
260+
}
261+
244262
// DebugMagicsock makes a request to the workspace agent's magicsock debug endpoint.
245263
func (c*AgentConn)DebugMagicsock(ctx context.Context) ([]byte,error) {
246264
ctx,span:=tracing.StartSpan(ctx)

‎tailnet/conn.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@ func NewConn(options *Options) (conn *Conn, err error) {
294294
}()
295295
ifserver.telemetryStore!=nil {
296296
server.wireguardEngine.SetNetInfoCallback(func(ni*tailcfg.NetInfo) {
297+
server.mutex.Lock()
298+
deferserver.mutex.Unlock()
299+
server.lastNetInfo=ni.Clone()
297300
server.telemetryStore.setNetInfo(ni)
298301
nodeUp.setNetInfo(ni)
299302
server.telemetryStore.pingPeer(server)
@@ -304,7 +307,12 @@ func NewConn(options *Options) (conn *Conn, err error) {
304307
})
305308
goserver.watchConnChange()
306309
}else {
307-
server.wireguardEngine.SetNetInfoCallback(nodeUp.setNetInfo)
310+
server.wireguardEngine.SetNetInfoCallback(func(ni*tailcfg.NetInfo) {
311+
server.mutex.Lock()
312+
deferserver.mutex.Unlock()
313+
server.lastNetInfo=ni.Clone()
314+
nodeUp.setNetInfo(ni)
315+
})
308316
}
309317
server.wireguardEngine.SetStatusCallback(nodeUp.setStatus)
310318
server.magicConn.SetDERPForcedWebsocketCallback(nodeUp.setDERPForcedWebsocket)
@@ -373,6 +381,13 @@ type Conn struct {
373381
watchCancelfunc()
374382

375383
trafficStats*connstats.Statistics
384+
lastNetInfo*tailcfg.NetInfo
385+
}
386+
387+
func (c*Conn)GetNetInfo()*tailcfg.NetInfo {
388+
c.mutex.Lock()
389+
deferc.mutex.Unlock()
390+
returnc.lastNetInfo.Clone()
376391
}
377392

378393
func (c*Conn)SetTunnelDestination(id uuid.UUID) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp