@@ -25,6 +25,7 @@ type AgentOptions struct {
2525Fetch func (ctx context.Context ,agentID uuid.UUID ) (codersdk.WorkspaceAgent ,error )
2626FetchLogs func (ctx context.Context ,agentID uuid.UUID ,after int64 ,follow bool ) (<- chan []codersdk.WorkspaceAgentLog , io.Closer ,error )
2727Wait bool // If true, wait for the agent to be ready (startup script).
28+ DocsURL string
2829}
2930
3031// Agent displays a spinning indicator that waits for a workspace agent to connect.
@@ -119,7 +120,7 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
119120if agent .Status == codersdk .WorkspaceAgentTimeout {
120121now := time .Now ()
121122sw .Log (now ,codersdk .LogLevelInfo ,"The workspace agent is having trouble connecting, wait for it to connect or restart your workspace." )
122- sw .Log (now ,codersdk .LogLevelInfo ,troubleshootingMessage (agent ,"https://coder.com/docs/ templates#agent-connection-issues" ))
123+ sw .Log (now ,codersdk .LogLevelInfo ,troubleshootingMessage (agent ,fmt . Sprintf ( "%s/ templates#agent-connection-issues", opts . DocsURL ) ))
123124for agent .Status == codersdk .WorkspaceAgentTimeout {
124125if agent ,err = fetch ();err != nil {
125126return xerrors .Errorf ("fetch: %w" ,err )
@@ -224,13 +225,13 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
224225sw .Fail (stage ,safeDuration (sw ,agent .ReadyAt ,agent .StartedAt ))
225226// Use zero time (omitted) to separate these from the startup logs.
226227sw .Log (time.Time {},codersdk .LogLevelWarn ,"Warning: A startup script exited with an error and your workspace may be incomplete." )
227- sw .Log (time.Time {},codersdk .LogLevelWarn ,troubleshootingMessage (agent ,"https://coder.com/docs/ templates/troubleshooting #startup-script-exited-with-an-error" ))
228+ sw .Log (time.Time {},codersdk .LogLevelWarn ,troubleshootingMessage (agent ,fmt . Sprintf ( "%s/ templates#startup-script-exited-with-an-error", opts . DocsURL ) ))
228229default :
229230switch {
230231case agent .LifecycleState .Starting ():
231232// Use zero time (omitted) to separate these from the startup logs.
232233sw .Log (time.Time {},codersdk .LogLevelWarn ,"Notice: The startup scripts are still running and your workspace may be incomplete." )
233- sw .Log (time.Time {},codersdk .LogLevelWarn ,troubleshootingMessage (agent ,"https://coder.com/docs/ templates/troubleshooting #your-workspace-may-be-incomplete" ))
234+ sw .Log (time.Time {},codersdk .LogLevelWarn ,troubleshootingMessage (agent ,fmt . Sprintf ( "%s/ templates#your-workspace-may-be-incomplete", opts . DocsURL ) ))
234235// Note: We don't complete or fail the stage here, it's
235236// intentionally left open to indicate this stage didn't
236237// complete.
@@ -252,7 +253,7 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
252253stage := "The workspace agent lost connection"
253254sw .Start (stage )
254255sw .Log (time .Now (),codersdk .LogLevelWarn ,"Wait for it to reconnect or restart your workspace." )
255- sw .Log (time .Now (),codersdk .LogLevelWarn ,troubleshootingMessage (agent ,"https://coder.com/docs/ templates/troubleshooting #agent-connection-issues" ))
256+ sw .Log (time .Now (),codersdk .LogLevelWarn ,troubleshootingMessage (agent ,fmt . Sprintf ( "%s/ templates#agent-connection-issues", opts . DocsURL ) ))
256257
257258disconnectedAt := agent .DisconnectedAt
258259for agent .Status == codersdk .WorkspaceAgentDisconnected {
@@ -351,16 +352,16 @@ func PeerDiagnostics(w io.Writer, d tailnet.PeerDiagnostics) {
351352}
352353
353354type ConnDiags struct {
354- ConnInfo workspacesdk.AgentConnectionInfo
355- PingP2P bool
356- DisableDirect bool
357- LocalNetInfo * tailcfg.NetInfo
358- LocalInterfaces * healthsdk.InterfacesReport
359- AgentNetcheck * healthsdk.AgentNetcheckReport
360- ClientIPIsAWS bool
361- AgentIPIsAWS bool
362- Verbose bool
363- // TODO: More diagnostics
355+ ConnInfo workspacesdk.AgentConnectionInfo
356+ PingP2P bool
357+ DisableDirect bool
358+ LocalNetInfo * tailcfg.NetInfo
359+ LocalInterfaces * healthsdk.InterfacesReport
360+ AgentNetcheck * healthsdk.AgentNetcheckReport
361+ ClientIPIsAWS bool
362+ AgentIPIsAWS bool
363+ Verbose bool
364+ TroubleshootingURL string
364365}
365366
366367func (d ConnDiags )Write (w io.Writer ) {
@@ -395,7 +396,7 @@ func (d ConnDiags) splitDiagnostics() (general, client, agent []string) {
395396agent = append (agent ,msg .Message )
396397}
397398if len (d .AgentNetcheck .Interfaces .Warnings )> 0 {
398- agent [len (agent )- 1 ]+= " \n https://coder.com/docs/networking/troubleshooting #low-mtu"
399+ agent [len (agent )- 1 ]+= fmt . Sprintf ( " \n %s #low-mtu", d . TroubleshootingURL )
399400}
400401}
401402
@@ -404,7 +405,7 @@ func (d ConnDiags) splitDiagnostics() (general, client, agent []string) {
404405client = append (client ,msg .Message )
405406}
406407if len (d .LocalInterfaces .Warnings )> 0 {
407- client [len (client )- 1 ]+= " \n https://coder.com/docs/networking/troubleshooting #low-mtu"
408+ client [len (client )- 1 ]+= fmt . Sprintf ( " \n %s #low-mtu", d . TroubleshootingURL )
408409}
409410}
410411
@@ -420,45 +421,45 @@ func (d ConnDiags) splitDiagnostics() (general, client, agent []string) {
420421}
421422
422423if d .ConnInfo .DisableDirectConnections {
423- general = append (general ,"❗ Your Coder administrator has blocked direct connections \n " +
424- " https://coder.com/docs/networking/troubleshooting #disabled-deployment-wide" )
424+ general = append (general ,
425+ fmt . Sprintf ( "❗ Your Coder administrator has blocked direct connections \n %s #disabled-deployment-wide", d . TroubleshootingURL ) )
425426if ! d .Verbose {
426427return general ,client ,agent
427428}
428429}
429430
430431if ! d .ConnInfo .DERPMap .HasSTUN () {
431- general = append (general ,"❗ The DERP map is not configured to use STUN \n " +
432- " https://coder.com/docs/networking/troubleshooting #no-stun-servers" )
432+ general = append (general ,
433+ fmt . Sprintf ( "❗ The DERP map is not configured to use STUN \n %s #no-stun-servers", d . TroubleshootingURL ) )
433434}else if d .LocalNetInfo != nil && ! d .LocalNetInfo .UDP {
434- client = append (client ,"Client could not connect to STUN over UDP \n " +
435- " https://coder.com/docs/networking/troubleshooting #udp-blocked" )
435+ client = append (client ,
436+ fmt . Sprintf ( "Client could not connect to STUN over UDP \n %s #udp-blocked", d . TroubleshootingURL ) )
436437}
437438
438439if d .LocalNetInfo != nil && d .LocalNetInfo .MappingVariesByDestIP .EqualBool (true ) {
439- client = append (client ,"Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers \n " +
440- " https://coder.com/docs/networking/troubleshooting#Endpoint-Dependent-Nat-Hard-NAT" )
440+ client = append (client ,
441+ fmt . Sprintf ( "Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers \n %s#endpoint-dependent-nat-hard-nat" , d . TroubleshootingURL ) )
441442}
442443
443444if d .AgentNetcheck != nil && d .AgentNetcheck .NetInfo != nil {
444445if d .AgentNetcheck .NetInfo .MappingVariesByDestIP .EqualBool (true ) {
445- agent = append (agent ,"Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers \n " +
446- " https://coder.com/docs/networking/troubleshooting#Endpoint-Dependent-Nat-Hard-NAT" )
446+ agent = append (agent ,
447+ fmt . Sprintf ( "Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers \n %s#endpoint-dependent-nat-hard-nat" , d . TroubleshootingURL ) )
447448}
448449if ! d .AgentNetcheck .NetInfo .UDP {
449- agent = append (agent ,"Agent could not connect to STUN over UDP \n " +
450- " https://coder.com/docs/networking/troubleshooting #udp-blocked" )
450+ agent = append (agent ,
451+ fmt . Sprintf ( "Agent could not connect to STUN over UDP \n %s #udp-blocked", d . TroubleshootingURL ) )
451452}
452453}
453454
454455if d .ClientIPIsAWS {
455- client = append (client ,"Client IP address is within an AWS range (AWS uses hard NAT) \n " +
456- " https://coder.com/docs/networking/troubleshooting#Endpoint-Dependent-Nat-Hard-NAT" )
456+ client = append (client ,
457+ fmt . Sprintf ( "Client IP address is within an AWS range (AWS uses hard NAT) \n %s#endpoint-dependent-nat-hard-nat" , d . TroubleshootingURL ) )
457458}
458459
459460if d .AgentIPIsAWS {
460- agent = append (agent ,"Agent IP address is within an AWS range (AWS uses hard NAT) \n " +
461- " https://coder.com/docs/networking/troubleshooting#Endpoint-Dependent-Nat-Hard-NAT" )
461+ agent = append (agent ,
462+ fmt . Sprintf ( "Agent IP address is within an AWS range (AWS uses hard NAT) \n %s#endpoint-dependent-nat-hard-nat" , d . TroubleshootingURL ) )
462463}
463464return general ,client ,agent
464465}