@@ -7,11 +7,13 @@ import (
77"strconv"
88"strings"
99"time"
10+ "unicode"
1011
1112"github.com/google/uuid"
1213"golang.org/x/xerrors"
1314"tailscale.com/tailcfg"
1415
16+ "github.com/coder/coder/v2/coderd/healthcheck/health"
1517"github.com/coder/coder/v2/codersdk"
1618"github.com/coder/coder/v2/codersdk/healthsdk"
1719"github.com/coder/coder/v2/codersdk/workspacesdk"
@@ -351,72 +353,109 @@ func PeerDiagnostics(w io.Writer, d tailnet.PeerDiagnostics) {
351353}
352354
353355type ConnDiags struct {
354- ConnInfo * workspacesdk.AgentConnectionInfo
356+ ConnInfo workspacesdk.AgentConnectionInfo
355357PingP2P bool
356358DisableDirect bool
357359LocalNetInfo * tailcfg.NetInfo
358360LocalInterfaces * healthsdk.InterfacesReport
359361AgentNetcheck * healthsdk.AgentNetcheckReport
360362ClientIPIsAWS bool
361363AgentIPIsAWS bool
364+ Verbose bool
362365// TODO: More diagnostics
363366}
364367
365- func ConnDiagnostics (w io.Writer ,d ConnDiags ) {
368+ func (d ConnDiags )Write (w io.Writer ) {
369+ _ ,_ = fmt .Fprintln (w ,"" )
370+ general ,client ,agent := d .splitDiagnostics ()
371+ for _ ,msg := range general {
372+ _ ,_ = fmt .Fprintln (w ,msg )
373+ }
374+ if len (client )> 0 {
375+ _ ,_ = fmt .Fprint (w ,"Possible client-side issues with direct connection:\n \n " )
376+ for _ ,msg := range client {
377+ _ ,_ = fmt .Fprintf (w ," - %s\n \n " ,msg )
378+ }
379+ }
380+ if len (agent )> 0 {
381+ _ ,_ = fmt .Fprint (w ,"Possible agent-side issues with direct connections:\n \n " )
382+ for _ ,msg := range agent {
383+ _ ,_ = fmt .Fprintf (w ," - %s\n \n " ,msg )
384+ }
385+ }
386+ }
387+
388+ func (d ConnDiags )splitDiagnostics () (general ,client ,agent []string ) {
366389if d .PingP2P {
367- _ , _ = fmt . Fprint ( w ,"✔ You are connected directly (p2p)\n " )
390+ general = append ( general ,"✔ You are connected directly (p2p)" )
368391}else {
369- _ , _ = fmt . Fprint ( w ,"❗ You are connected via a DERP relay, not directly (p2p)\n " )
392+ general = append ( general ,"❗ You are connected via a DERP relay, not directly (p2p)" )
370393}
371394
372395if d .AgentNetcheck != nil {
373396for _ ,msg := range d .AgentNetcheck .Interfaces .Warnings {
374- _ , _ = fmt . Fprintf ( w , "❗ Agent: %s \n " , msg . Message )
397+ agent = append ( agent , formatHealthMessage ( msg ) )
375398}
376399}
377400
378401if d .LocalInterfaces != nil {
379402for _ ,msg := range d .LocalInterfaces .Warnings {
380- _ , _ = fmt . Fprintf ( w , "❗ Client: %s \n " , msg . Message )
403+ client = append ( client , formatHealthMessage ( msg ) )
381404}
382405}
383406
384- if d .DisableDirect {
385- _ ,_ = fmt .Fprint (w ,"❗ Direct connections are disabled locally, by `--disable-direct` or `CODER_DISABLE_DIRECT`\n " )
386- return
407+ if d .PingP2P && ! d .Verbose {
408+ return general ,client ,agent
387409}
388410
389- if d .ConnInfo != nil && d .ConnInfo .DisableDirectConnections {
390- _ ,_ = fmt .Fprint (w ,"❗ Your Coder administrator has blocked direct connections\n " )
391- return
411+ if d .DisableDirect {
412+ general = append (general ,"❗ Direct connections are disabled locally, by `--disable-direct` or `CODER_DISABLE_DIRECT`" )
413+ if ! d .Verbose {
414+ return general ,client ,agent
415+ }
392416}
393417
394- if d .ConnInfo != nil && d .ConnInfo .DERPMap != nil {
395- if ! d .ConnInfo .DERPMap .HasSTUN () {
396- _ ,_ = fmt .Fprint (w ,"✘ The DERP map is not configured to use STUN, which will prevent direct connections from starting outside of local networks\n " )
397- }else if d .LocalNetInfo != nil && ! d .LocalNetInfo .UDP {
398- _ ,_ = fmt .Fprint (w ,"❗ Client could not connect to STUN over UDP, which may be preventing a direct connection\n " )
418+ if d .ConnInfo .DisableDirectConnections {
419+ general = append (general ,"❗ Your Coder administrator has blocked direct connections" )
420+ if ! d .Verbose {
421+ return general ,client ,agent
399422}
400423}
401424
425+ if ! d .ConnInfo .DERPMap .HasSTUN () {
426+ general = append (general ,"✘ The DERP map is not configured to use STUN" )
427+ }else if d .LocalNetInfo != nil && ! d .LocalNetInfo .UDP {
428+ client = append (client ,"✘ Client could not connect to STUN over UDP" )
429+ }
430+
402431if d .LocalNetInfo != nil && d .LocalNetInfo .MappingVariesByDestIP .EqualBool (true ) {
403- _ , _ = fmt . Fprint ( w ,"❗ Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers\n " )
432+ client = append ( client ,"❗ Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers" )
404433}
405434
406435if d .AgentNetcheck != nil && d .AgentNetcheck .NetInfo != nil {
407436if d .AgentNetcheck .NetInfo .MappingVariesByDestIP .EqualBool (true ) {
408- _ , _ = fmt . Fprint ( w ,"❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers\n " )
437+ agent = append ( agent ,"❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers" )
409438}
410439if ! d .AgentNetcheck .NetInfo .UDP {
411- _ , _ = fmt . Fprint ( w ,"❗ Agent could not connect to STUN over UDP, which may be preventing a direct connection \n " )
440+ agent = append ( agent ,"✘ Agent could not connect to STUN over UDP" )
412441}
413442}
414443
415444if d .ClientIPIsAWS {
416- _ , _ = fmt . Fprint ( w ,"❗ Client IP address is within an AWS range, which is known to cause problems with forming direct connections (AWS uses hard NAT)\n " )
445+ client = append ( client ,"❗ Client IP address is within an AWS range (AWS uses hard NAT)" )
417446}
418447
419448if d .AgentIPIsAWS {
420- _ ,_ = fmt .Fprint (w ,"❗ Agent IP address is within an AWS range, which is known to cause problems with forming direct connections (AWS uses hard NAT)\n " )
449+ agent = append (agent ,"❗ Agent IP address is within an AWS range (AWS uses hard NAT)" )
450+ }
451+ return general ,client ,agent
452+ }
453+
454+ func formatHealthMessage (msg health.Message )string {
455+ if msg .Code != health .CodeInterfaceSmallMTU {
456+ return msg .Message
421457}
458+ r := []rune (strings .Replace (msg .Message ,", which may cause problems with direct connections" ,"" ,- 1 ))
459+ out := string (append ([]rune {unicode .ToUpper (r [0 ])},r [1 :]... ))
460+ return fmt .Sprintf ("❗ %s, which may degrade the quality of direct connections" ,out )
422461}