Skip to content

Commit 3ac949f

Browse files
committed
fix: make cli respect deployment --docs-url
1 parent cd7ce8e commit 3ac949f

File tree

10 files changed

+69
-49
lines changed

10 files changed

+69
-49
lines changed

cli/cliui/agent.go

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,18 @@ type AgentOptions struct {
2525
Fetch func(ctx context.Context, agentID uuid.UUID) (codersdk.WorkspaceAgent, error)
2626
FetchLogs func(ctx context.Context, agentID uuid.UUID, after int64, follow bool) (<-chan []codersdk.WorkspaceAgentLog, io.Closer, error)
2727
Wait 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.
3132
func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentOptions) error {
3233
ctx, cancel := context.WithCancel(ctx)
3334
defer cancel()
3435

36+
if opts.DocsURL == "" {
37+
opts.DocsURL = "https://coder.com/docs"
38+
}
39+
3540
if opts.FetchInterval == 0 {
3641
opts.FetchInterval = 500 * time.Millisecond
3742
}
@@ -119,7 +124,7 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
119124
if agent.Status == codersdk.WorkspaceAgentTimeout {
120125
now := time.Now()
121126
sw.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"))
127+
sw.Log(now, codersdk.LogLevelInfo, troubleshootingMessage(agent, fmt.Sprintf("%s/templates#agent-connection-issues", opts.DocsURL)))
123128
for agent.Status == codersdk.WorkspaceAgentTimeout {
124129
if agent, err = fetch(); err != nil {
125130
return xerrors.Errorf("fetch: %w", err)
@@ -224,13 +229,13 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
224229
sw.Fail(stage, safeDuration(sw, agent.ReadyAt, agent.StartedAt))
225230
// Use zero time (omitted) to separate these from the startup logs.
226231
sw.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"))
232+
sw.Log(time.Time{}, codersdk.LogLevelWarn, troubleshootingMessage(agent, fmt.Sprintf("%s/templates#startup-script-exited-with-an-error", opts.DocsURL)))
228233
default:
229234
switch {
230235
case agent.LifecycleState.Starting():
231236
// Use zero time (omitted) to separate these from the startup logs.
232237
sw.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"))
238+
sw.Log(time.Time{}, codersdk.LogLevelWarn, troubleshootingMessage(agent, fmt.Sprintf("%s/templates#your-workspace-may-be-incomplete", opts.DocsURL)))
234239
// Note: We don't complete or fail the stage here, it's
235240
// intentionally left open to indicate this stage didn't
236241
// complete.
@@ -252,7 +257,7 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
252257
stage := "The workspace agent lost connection"
253258
sw.Start(stage)
254259
sw.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"))
260+
sw.Log(time.Now(), codersdk.LogLevelWarn, troubleshootingMessage(agent, fmt.Sprintf("%s/templates#agent-connection-issues", opts.DocsURL)))
256261

257262
disconnectedAt := agent.DisconnectedAt
258263
for agent.Status == codersdk.WorkspaceAgentDisconnected {
@@ -351,16 +356,16 @@ func PeerDiagnostics(w io.Writer, d tailnet.PeerDiagnostics) {
351356
}
352357

353358
type 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
359+
ConnInfo workspacesdk.AgentConnectionInfo
360+
PingP2P bool
361+
DisableDirect bool
362+
LocalNetInfo *tailcfg.NetInfo
363+
LocalInterfaces *healthsdk.InterfacesReport
364+
AgentNetcheck *healthsdk.AgentNetcheckReport
365+
ClientIPIsAWS bool
366+
AgentIPIsAWS bool
367+
Verbose bool
368+
TroubleshootingURL string
364369
}
365370

366371
func (d ConnDiags) Write(w io.Writer) {
@@ -395,7 +400,7 @@ func (d ConnDiags) splitDiagnostics() (general, client, agent []string) {
395400
agent = append(agent, msg.Message)
396401
}
397402
if len(d.AgentNetcheck.Interfaces.Warnings) > 0 {
398-
agent[len(agent)-1] += "\nhttps://coder.com/docs/networking/troubleshooting#low-mtu"
403+
agent[len(agent)-1] += fmt.Sprintf("\n%s#low-mtu", d.TroubleshootingURL)
399404
}
400405
}
401406

@@ -404,7 +409,7 @@ func (d ConnDiags) splitDiagnostics() (general, client, agent []string) {
404409
client = append(client, msg.Message)
405410
}
406411
if len(d.LocalInterfaces.Warnings) > 0 {
407-
client[len(client)-1] += "\nhttps://coder.com/docs/networking/troubleshooting#low-mtu"
412+
client[len(client)-1] += fmt.Sprintf("\n%s#low-mtu", d.TroubleshootingURL)
408413
}
409414
}
410415

@@ -420,45 +425,45 @@ func (d ConnDiags) splitDiagnostics() (general, client, agent []string) {
420425
}
421426

422427
if 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")
428+
general = append(general,
429+
fmt.Sprintf("❗ Your Coder administrator has blocked direct connections\n %s#disabled-deployment-wide", d.TroubleshootingURL))
425430
if !d.Verbose {
426431
return general, client, agent
427432
}
428433
}
429434

430435
if !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")
436+
general = append(general,
437+
fmt.Sprintf("❗ The DERP map is not configured to use STUN\n %s#no-stun-servers", d.TroubleshootingURL))
433438
} 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")
439+
client = append(client,
440+
fmt.Sprintf("Client could not connect to STUN over UDP\n %s#udp-blocked", d.TroubleshootingURL))
436441
}
437442

438443
if 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")
444+
client = append(client,
445+
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))
441446
}
442447

443448
if d.AgentNetcheck != nil && d.AgentNetcheck.NetInfo != nil {
444449
if 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")
450+
agent = append(agent,
451+
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))
447452
}
448453
if !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")
454+
agent = append(agent,
455+
fmt.Sprintf("Agent could not connect to STUN over UDP\n %s#udp-blocked", d.TroubleshootingURL))
451456
}
452457
}
453458

454459
if 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")
460+
client = append(client,
461+
fmt.Sprintf("Client IP address is within an AWS range (AWS uses hard NAT)\n %s#endpoint-dependent-nat-hard-nat", d.TroubleshootingURL))
457462
}
458463

459464
if 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")
465+
agent = append(agent,
466+
fmt.Sprintf("Agent IP address is within an AWS range (AWS uses hard NAT)\n %s#endpoint-dependent-nat-hard-nat", d.TroubleshootingURL))
462467
}
463468
return general, client, agent
464469
}

cli/open.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func (r *RootCmd) openVSCode() *serpent.Command {
7979
Fetch: client.WorkspaceAgent,
8080
FetchLogs: nil,
8181
Wait: false,
82+
DocsURL: docsURL(ctx, client),
8283
})
8384
if err != nil {
8485
if xerrors.Is(err, context.Canceled) {

cli/ping.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ func (r *RootCmd) ping() *serpent.Command {
6767
if !r.disableNetworkTelemetry {
6868
opts.EnableTelemetry = true
6969
}
70-
client := workspacesdk.New(client)
71-
conn, err := client.DialAgent(ctx, workspaceAgent.ID, opts)
70+
wsClient := workspacesdk.New(client)
71+
conn, err := wsClient.DialAgent(ctx, workspaceAgent.ID, opts)
7272
if err != nil {
7373
return err
7474
}
@@ -155,10 +155,11 @@ func (r *RootCmd) ping() *serpent.Command {
155155

156156
ni := conn.GetNetInfo()
157157
connDiags := cliui.ConnDiags{
158-
PingP2P: didP2p,
159-
DisableDirect: r.disableDirect,
160-
LocalNetInfo: ni,
161-
Verbose: r.verbose,
158+
PingP2P: didP2p,
159+
DisableDirect: r.disableDirect,
160+
LocalNetInfo: ni,
161+
Verbose: r.verbose,
162+
TroubleshootingURL: docsURL(diagCtx, client) + "/networking/troubleshooting",
162163
}
163164

164165
awsRanges, err := cliutil.FetchAWSIPRanges(diagCtx, cliutil.AWSIPRangesURL)
@@ -168,7 +169,7 @@ func (r *RootCmd) ping() *serpent.Command {
168169

169170
connDiags.ClientIPIsAWS = isAWSIP(awsRanges, ni)
170171

171-
connInfo, err := client.AgentConnectionInfoGeneric(diagCtx)
172+
connInfo, err := wsClient.AgentConnectionInfoGeneric(diagCtx)
172173
if err != nil || connInfo.DERPMap == nil {
173174
return xerrors.Errorf("Failed to retrieve connection info from server: %w\n", err)
174175
}

cli/portforward.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ func (r *RootCmd) portForward() *serpent.Command {
8888
}
8989

9090
err = cliui.Agent(ctx, inv.Stderr, workspaceAgent.ID, cliui.AgentOptions{
91-
Fetch: client.WorkspaceAgent,
92-
Wait: false,
91+
Fetch: client.WorkspaceAgent,
92+
Wait: false,
93+
DocsURL: docsURL(ctx, client),
9394
})
9495
if err != nil {
9596
return xerrors.Errorf("await agent: %w", err)

cli/rename.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (r *RootCmd) rename() *serpent.Command {
3131
_, _ = fmt.Fprintf(inv.Stdout, "%s\n\n",
3232
pretty.Sprint(cliui.DefaultStyles.Wrap, "WARNING: A rename can result in data loss if a resource references the workspace name in the template (e.g volumes). Please backup any data before proceeding."),
3333
)
34-
_, _ = fmt.Fprintf(inv.Stdout, "See: %s\n\n", "https://coder.com/docs/templates/resource-persistence#%EF%B8%8F-persistence-pitfalls")
34+
_, _ = fmt.Fprintf(inv.Stdout, "See: %s%s\n\n", docsURL(inv.Context(), client), "/templates/resource-persistence#%EF%B8%8F-persistence-pitfalls")
3535
_, err = cliui.Prompt(inv, cliui.PromptOptions{
3636
Text: fmt.Sprintf("Type %q to confirm rename:", workspace.Name),
3737
Validate: func(s string) error {

cli/root.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,14 @@ func namedWorkspace(ctx context.Context, client *codersdk.Client, identifier str
687687
return client.WorkspaceByOwnerAndName(ctx, owner, name, codersdk.WorkspaceOptions{})
688688
}
689689

690+
func docsURL(ctx context.Context, client *codersdk.Client) string {
691+
deploymentConfig, err := client.DeploymentConfig(ctx)
692+
if err == nil {
693+
return deploymentConfig.Values.DocsURL.String()
694+
}
695+
return "https://docs.coder.com"
696+
}
697+
690698
// createConfig consumes the global configuration flag to produce a config root.
691699
func (r *RootCmd) createConfig() config.Root {
692700
return config.Root(r.globalConfig)

cli/server.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
634634
"new version of coder available",
635635
slog.F("new_version", r.Version),
636636
slog.F("url", r.URL),
637-
slog.F("upgrade_instructions", "https://coder.com/docs/admin/upgrade"),
637+
slog.F("upgrade_instructions", fmt.Sprintf("%s/admin/upgrade", vals.DocsURL.String())),
638638
)
639639
}
640640
},
@@ -858,7 +858,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
858858
}
859859
defer options.Telemetry.Close()
860860
} else {
861-
logger.Warn(ctx, `telemetry disabled, unable to notify of security issues. Read more: https://coder.com/docs/admin/telemetry`)
861+
logger.Warn(ctx, fmt.Sprintf(`telemetry disabled, unable to notify of security issues. Read more: %s/admin/telemetry`, vals.DocsURL.String()))
862862
}
863863

864864
// This prevents the pprof import from being accidentally deleted.

cli/speedtest.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@ func (r *RootCmd) speedtest() *serpent.Command {
8787
}
8888

8989
err = cliui.Agent(ctx, inv.Stderr, workspaceAgent.ID, cliui.AgentOptions{
90-
Fetch: client.WorkspaceAgent,
91-
Wait: false,
90+
Fetch: client.WorkspaceAgent,
91+
Wait: false,
92+
DocsURL: docsURL(ctx, client),
9293
})
9394
if err != nil {
9495
return xerrors.Errorf("await agent: %w", err)

cli/ssh.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,11 @@ func (r *RootCmd) ssh() *serpent.Command {
227227
// OpenSSH passes stderr directly to the calling TTY.
228228
// This is required in "stdio" mode so a connecting indicator can be displayed.
229229
err = cliui.Agent(ctx, inv.Stderr, workspaceAgent.ID, cliui.AgentOptions{
230-
Fetch: client.WorkspaceAgent,
231-
FetchLogs: client.WorkspaceAgentLogsAfter,
232-
Wait: wait,
230+
FetchInterval: 0,
231+
Fetch: client.WorkspaceAgent,
232+
FetchLogs: client.WorkspaceAgentLogsAfter,
233+
Wait: wait,
234+
DocsURL: docsURL(ctx, client),
233235
})
234236
if err != nil {
235237
if xerrors.Is(err, context.Canceled) {

cli/vscodessh.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
137137
Fetch: client.WorkspaceAgent,
138138
FetchLogs: client.WorkspaceAgentLogsAfter,
139139
Wait: wait,
140+
DocsURL: docsURL(ctx, client),
140141
})
141142
if err != nil {
142143
if xerrors.Is(err, context.Canceled) {

0 commit comments

Comments
 (0)