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

Commit047e871

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

File tree

8 files changed

+296
-5
lines changed

8 files changed

+296
-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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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"
9+
"github.com/coder/coder/v2/codersdk/healthsdk"
10+
)
11+
12+
func (a*agent)HandleNetcheck(rw http.ResponseWriter,r*http.Request) {
13+
ni:=a.TailnetConn().GetNetInfo()
14+
15+
ifReport,err:=healthsdk.RunInterfacesReport()
16+
iferr!=nil {
17+
httpapi.Write(r.Context(),rw,http.StatusInternalServerError, codersdk.Response{
18+
Message:"Failed to run interfaces report",
19+
Detail:err.Error(),
20+
})
21+
return
22+
}
23+
24+
httpapi.Write(r.Context(),rw,http.StatusOK, healthsdk.AgentNetcheckReport{
25+
BaseReport: healthsdk.BaseReport{
26+
Severity:health.SeverityOK,
27+
},
28+
NetInfo:ni,
29+
Interfaces:ifReport,
30+
})
31+
}

‎cli/cliui/agent.go

Lines changed: 49 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,49 @@ func PeerDiagnostics(w io.Writer, d tailnet.PeerDiagnostics) {
346349
_,_=fmt.Fprint(w,"✘ Wireguard is not connected\n")
347350
}
348351
}
352+
353+
typeConnDiagsstruct {
354+
ConnInfo*workspacesdk.AgentConnectionInfo
355+
PingP2Pbool
356+
LocalNetInfo*tailcfg.NetInfo
357+
LocalInterfaces*healthsdk.InterfacesReport
358+
AgentNetcheck*healthsdk.AgentNetcheckReport
359+
// TODO: More diagnostics
360+
}
361+
362+
funcConnDiagnostics(w io.Writer,dConnDiags) {
363+
ifd.AgentNetcheck!=nil {
364+
for_,msg:=ranged.AgentNetcheck.Interfaces.Warnings {
365+
_,_=fmt.Fprintf(w,"❗ Agent: %s\n",msg.Message)
366+
}
367+
}
368+
369+
ifd.LocalInterfaces!=nil {
370+
for_,msg:=ranged.LocalInterfaces.Warnings {
371+
_,_=fmt.Fprintf(w,"❗ Client: %s\n",msg.Message)
372+
}
373+
}
374+
375+
ifd.PingP2P {
376+
_,_=fmt.Fprint(w,"✔ You are connected directly, peer-to-peer (p2p)\n")
377+
return
378+
}
379+
_,_=fmt.Fprint(w,"❗ You are connected via a DERP relay, not directly, peer-to-peer (p2p)\n")
380+
381+
ifd.ConnInfo!=nil&&d.ConnInfo.DisableDirectConnections {
382+
_,_=fmt.Fprint(w,"❗ Your Coder administrator has blocked direct connections\n")
383+
return
384+
}
385+
386+
ifd.ConnInfo!=nil&&d.ConnInfo.DERPMap!=nil&&!d.ConnInfo.DERPMap.HasSTUN() {
387+
_,_=fmt.Fprint(w,"✘ The DERP map is not configured to use STUN, which will prevent direct connections from starting outside of local networks\n")
388+
}
389+
390+
ifd.LocalNetInfo!=nil&&d.LocalNetInfo.MappingVariesByDestIP.EqualBool(true) {
391+
_,_=fmt.Fprint(w,"❗ Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers\n")
392+
}
393+
394+
ifd.AgentNetcheck!=nil&&d.AgentNetcheck.NetInfo!=nil&&d.AgentNetcheck.NetInfo.MappingVariesByDestIP.EqualBool(true) {
395+
_,_=fmt.Fprint(w,"❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers\n")
396+
}
397+
}

‎cli/cliui/agent_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ import (
2020

2121
"github.com/coder/coder/v2/cli/clitest"
2222
"github.com/coder/coder/v2/cli/cliui"
23+
"github.com/coder/coder/v2/coderd/healthcheck/health"
2324
"github.com/coder/coder/v2/coderd/util/ptr"
2425
"github.com/coder/coder/v2/codersdk"
26+
"github.com/coder/coder/v2/codersdk/healthsdk"
27+
"github.com/coder/coder/v2/codersdk/workspacesdk"
2528
"github.com/coder/coder/v2/tailnet"
2629
"github.com/coder/coder/v2/testutil"
2730
"github.com/coder/serpent"
@@ -672,3 +675,139 @@ func TestPeerDiagnostics(t *testing.T) {
672675
})
673676
}
674677
}
678+
679+
funcTestConnDiagnostics(t*testing.T) {
680+
t.Parallel()
681+
testCases:= []struct {
682+
namestring
683+
diags cliui.ConnDiags
684+
want []*regexp.Regexp
685+
}{
686+
{
687+
name:"Direct",
688+
diags: cliui.ConnDiags{
689+
ConnInfo:&workspacesdk.AgentConnectionInfo{},
690+
PingP2P:true,
691+
LocalNetInfo:&tailcfg.NetInfo{},
692+
},
693+
want: []*regexp.Regexp{
694+
regexp.MustCompile(`^✔ You are connected directly, peer-to-peer \(p2p\)$`),
695+
},
696+
},
697+
{
698+
name:"DirectBlocked",
699+
diags: cliui.ConnDiags{
700+
ConnInfo:&workspacesdk.AgentConnectionInfo{
701+
DisableDirectConnections:true,
702+
},
703+
},
704+
want: []*regexp.Regexp{
705+
regexp.MustCompile(`^❗ You are connected via a DERP relay, not directly, peer-to-peer \(p2p\)$`),
706+
regexp.MustCompile(`^❗ Your Coder administrator has blocked direct connections$`),
707+
},
708+
},
709+
{
710+
name:"NoStun",
711+
diags: cliui.ConnDiags{
712+
ConnInfo:&workspacesdk.AgentConnectionInfo{
713+
DERPMap:&tailcfg.DERPMap{},
714+
},
715+
LocalNetInfo:&tailcfg.NetInfo{},
716+
},
717+
want: []*regexp.Regexp{
718+
regexp.MustCompile(`^❗ You are connected via a DERP relay, not directly, peer-to-peer \(p2p\)$`),
719+
regexp.MustCompile(`^✘ The DERP map is not configured to use STUN, which will prevent direct connections from starting outside of local networks$`),
720+
},
721+
},
722+
{
723+
name:"ClientHardNat",
724+
diags: cliui.ConnDiags{
725+
LocalNetInfo:&tailcfg.NetInfo{
726+
MappingVariesByDestIP:"true",
727+
},
728+
},
729+
want: []*regexp.Regexp{
730+
regexp.MustCompile(`^❗ You are connected via a DERP relay, not directly, peer-to-peer \(p2p\)$`),
731+
regexp.MustCompile(`^❗ Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers$`),
732+
},
733+
},
734+
{
735+
name:"AgentHardNat",
736+
diags: cliui.ConnDiags{
737+
ConnInfo:&workspacesdk.AgentConnectionInfo{},
738+
PingP2P:false,
739+
LocalNetInfo:&tailcfg.NetInfo{},
740+
AgentNetcheck:&healthsdk.AgentNetcheckReport{
741+
NetInfo:&tailcfg.NetInfo{MappingVariesByDestIP:"true"},
742+
},
743+
},
744+
want: []*regexp.Regexp{
745+
regexp.MustCompile(`^❗ You are connected via a DERP relay, not directly, peer-to-peer \(p2p\)$`),
746+
regexp.MustCompile(`^❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers$`),
747+
},
748+
},
749+
{
750+
name:"AgentInterfaceWarnings",
751+
diags: cliui.ConnDiags{
752+
PingP2P:true,
753+
AgentNetcheck:&healthsdk.AgentNetcheckReport{
754+
Interfaces: healthsdk.InterfacesReport{
755+
BaseReport: healthsdk.BaseReport{
756+
Warnings: []health.Message{
757+
health.Messagef(health.CodeInterfaceSmallMTU,"network interface eth0 has MTU 1280, (less than 1378), which may cause problems with direct connections"),
758+
},
759+
},
760+
},
761+
},
762+
},
763+
want: []*regexp.Regexp{
764+
regexp.MustCompile(`^❗ Agent: network interface eth0 has MTU 1280, \(less than 1378\), which may cause problems with direct connections$`),
765+
regexp.MustCompile(`^✔ You are connected directly, peer-to-peer \(p2p\)$`),
766+
},
767+
},
768+
{
769+
name:"LocalInterfaceWarnings",
770+
diags: cliui.ConnDiags{
771+
PingP2P:true,
772+
LocalInterfaces:&healthsdk.InterfacesReport{
773+
BaseReport: healthsdk.BaseReport{
774+
Warnings: []health.Message{
775+
health.Messagef(health.CodeInterfaceSmallMTU,"network interface eth1 has MTU 1310, (less than 1378), which may cause problems with direct connections"),
776+
},
777+
},
778+
},
779+
},
780+
want: []*regexp.Regexp{
781+
regexp.MustCompile(`^❗ Client: network interface eth1 has MTU 1310, \(less than 1378\), which may cause problems with direct connections$`),
782+
regexp.MustCompile(`^✔ You are connected directly, peer-to-peer \(p2p\)$`),
783+
},
784+
},
785+
}
786+
for_,tc:=rangetestCases {
787+
tc:=tc
788+
t.Run(tc.name,func(t*testing.T) {
789+
t.Parallel()
790+
r,w:=io.Pipe()
791+
gofunc() {
792+
deferw.Close()
793+
cliui.ConnDiagnostics(w,tc.diags)
794+
}()
795+
s:=bufio.NewScanner(r)
796+
i:=0
797+
got:=make([]string,0)
798+
fors.Scan() {
799+
got=append(got,s.Text())
800+
ifi<len(tc.want) {
801+
reg:=tc.want[i]
802+
ifreg.Match(s.Bytes()) {
803+
i++
804+
}
805+
}
806+
}
807+
ifi<len(tc.want) {
808+
t.Logf("failed to match regexp: %s\ngot:\n%s",tc.want[i].String(),strings.Join(got,"\n"))
809+
t.FailNow()
810+
}
811+
})
812+
}
813+
}

‎cli/ping.go

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package cli
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
7+
"net/http"
68
"time"
79

810
"golang.org/x/xerrors"
@@ -14,6 +16,7 @@ import (
1416

1517
"github.com/coder/coder/v2/cli/cliui"
1618
"github.com/coder/coder/v2/codersdk"
19+
"github.com/coder/coder/v2/codersdk/healthsdk"
1720
"github.com/coder/coder/v2/codersdk/workspacesdk"
1821
"github.com/coder/serpent"
1922
)
@@ -61,7 +64,8 @@ func (r *RootCmd) ping() *serpent.Command {
6164
if!r.disableNetworkTelemetry {
6265
opts.EnableTelemetry=true
6366
}
64-
conn,err:=workspacesdk.New(client).DialAgent(ctx,workspaceAgent.ID,opts)
67+
client:=workspacesdk.New(client)
68+
conn,err:=client.DialAgent(ctx,workspaceAgent.ID,opts)
6569
iferr!=nil {
6670
returnerr
6771
}
@@ -138,11 +142,38 @@ func (r *RootCmd) ping() *serpent.Command {
138142
)
139143

140144
ifn==int(pingNum) {
141-
diags:=conn.GetPeerDiagnostics()
142-
cliui.PeerDiagnostics(inv.Stdout,diags)
143-
returnnil
145+
break
144146
}
145147
}
148+
ctx,cancel=context.WithTimeout(inv.Context(),30*time.Second)
149+
defercancel()
150+
diags:=conn.GetPeerDiagnostics()
151+
cliui.PeerDiagnostics(inv.Stdout,diags)
152+
153+
connDiags:= cliui.ConnDiags{
154+
PingP2P:didP2p,
155+
}
156+
connInfo,err:=client.AgentConnectionInfoGeneric(ctx)
157+
iferr==nil {
158+
connDiags.ConnInfo=&connInfo
159+
}
160+
ifReport,err:=healthsdk.RunInterfacesReport()
161+
iferr==nil {
162+
connDiags.LocalInterfaces=&ifReport
163+
}
164+
agentNetcheck,err:=conn.Netcheck(ctx)
165+
iferr==nil {
166+
connDiags.AgentNetcheck=&agentNetcheck
167+
}else {
168+
varsdkErr*codersdk.Error
169+
iferrors.As(err,&sdkErr)&&sdkErr.StatusCode()==http.StatusNotFound {
170+
_,_=fmt.Fprint(inv.Stdout,"Could not generate full connection report as the workspace agent is outdated\n")
171+
}else {
172+
_,_=fmt.Fprintf(inv.Stdout,"Failed to retrieve connection report from agent: %v\n",err)
173+
}
174+
}
175+
cliui.ConnDiagnostics(inv.Stdout,connDiags)
176+
returnnil
146177
},
147178
}
148179

‎codersdk/healthsdk/healthsdk.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,10 @@ 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+
InterfacesInterfacesReport`json:"interfaces"`
282+
}

‎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)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp