Skip to content

Commit 8e1e0f0

Browse files
authored
feat(cli): support bundle: show links to docs/admin/healthcheck (#12974)
1 parent b598aef commit 8e1e0f0

File tree

5 files changed

+86
-12
lines changed

5 files changed

+86
-12
lines changed

cli/support.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,12 @@ func (r *RootCmd) supportBundle() *serpent.Command {
184184
_ = os.Remove(outputPath) // best effort
185185
return xerrors.Errorf("create support bundle: %w", err)
186186
}
187-
deployHealthSummary := bun.Deployment.HealthReport.Summarize()
187+
docsURL := bun.Deployment.Config.Values.DocsURL.String()
188+
deployHealthSummary := bun.Deployment.HealthReport.Summarize(docsURL)
188189
if len(deployHealthSummary) > 0 {
189190
cliui.Warn(inv.Stdout, "Deployment health issues detected:", deployHealthSummary...)
190191
}
191-
clientNetcheckSummary := bun.Network.Netcheck.Summarize("Client netcheck:")
192+
clientNetcheckSummary := bun.Network.Netcheck.Summarize("Client netcheck:", docsURL)
192193
if len(clientNetcheckSummary) > 0 {
193194
cliui.Warn(inv.Stdout, "Networking issues detected:", deployHealthSummary...)
194195
}

coderd/healthcheck/health/model.go

+30
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"strings"
66

7+
"github.com/coder/coder/v2/buildinfo"
78
"github.com/coder/coder/v2/coderd/util/ptr"
89
)
910

@@ -44,6 +45,11 @@ const (
4445
CodeProvisionerDaemonAPIMajorVersionDeprecated Code = `EPD03`
4546
)
4647

48+
// Default docs URL
49+
var (
50+
docsURLDefault = "https://coder.com/docs/v2"
51+
)
52+
4753
// @typescript-generate Severity
4854
type Severity string
4955

@@ -72,6 +78,30 @@ func (m Message) String() string {
7278
return sb.String()
7379
}
7480

81+
// URL returns a link to the admin/healthcheck docs page for the given Message.
82+
// NOTE: if using a custom docs URL, specify base.
83+
func (m Message) URL(base string) string {
84+
var codeAnchor string
85+
if m.Code == "" {
86+
codeAnchor = strings.ToLower(string(CodeUnknown))
87+
} else {
88+
codeAnchor = strings.ToLower(string(m.Code))
89+
}
90+
91+
if base == "" {
92+
base = docsURLDefault
93+
versionPath := buildinfo.Version()
94+
if buildinfo.IsDev() {
95+
// for development versions, just use latest
96+
versionPath = "latest"
97+
}
98+
return fmt.Sprintf("%s/%s/admin/healthcheck#%s", base, versionPath, codeAnchor)
99+
}
100+
101+
// We don't assume that custom docs URLs are versioned.
102+
return fmt.Sprintf("%s/admin/healthcheck#%s", base, codeAnchor)
103+
}
104+
75105
// Code is a stable identifier used to link to documentation.
76106
// @typescript-generate Code
77107
type Code string
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package health_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/coder/coder/v2/coderd/healthcheck/health"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func Test_MessageURL(t *testing.T) {
12+
t.Parallel()
13+
14+
for _, tt := range []struct {
15+
name string
16+
code health.Code
17+
base string
18+
expected string
19+
}{
20+
{"empty", "", "", "https://coder.com/docs/v2/latest/admin/healthcheck#eunknown"},
21+
{"default", health.CodeAccessURLFetch, "", "https://coder.com/docs/v2/latest/admin/healthcheck#eacs03"},
22+
{"custom docs base", health.CodeAccessURLFetch, "https://example.com/docs", "https://example.com/docs/admin/healthcheck#eacs03"},
23+
} {
24+
tt := tt
25+
t.Run(tt.name, func(t *testing.T) {
26+
t.Parallel()
27+
uut := health.Message{Code: tt.code}
28+
actual := uut.URL(tt.base)
29+
assert.Equal(t, tt.expected, actual)
30+
})
31+
}
32+
}

codersdk/healthsdk/healthsdk.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,14 @@ type HealthcheckReport struct {
120120
}
121121

122122
// Summarize returns a summary of all errors and warnings of components of HealthcheckReport.
123-
func (r *HealthcheckReport) Summarize() []string {
123+
func (r *HealthcheckReport) Summarize(docsURL string) []string {
124124
var msgs []string
125-
msgs = append(msgs, r.AccessURL.Summarize("Access URL:")...)
126-
msgs = append(msgs, r.Database.Summarize("Database:")...)
127-
msgs = append(msgs, r.DERP.Summarize("DERP:")...)
128-
msgs = append(msgs, r.ProvisionerDaemons.Summarize("Provisioner Daemons:")...)
129-
msgs = append(msgs, r.Websocket.Summarize("Websocket:")...)
130-
msgs = append(msgs, r.WorkspaceProxy.Summarize("Workspace Proxies:")...)
125+
msgs = append(msgs, r.AccessURL.Summarize("Access URL:", docsURL)...)
126+
msgs = append(msgs, r.Database.Summarize("Database:", docsURL)...)
127+
msgs = append(msgs, r.DERP.Summarize("DERP:", docsURL)...)
128+
msgs = append(msgs, r.ProvisionerDaemons.Summarize("Provisioner Daemons:", docsURL)...)
129+
msgs = append(msgs, r.Websocket.Summarize("Websocket:", docsURL)...)
130+
msgs = append(msgs, r.WorkspaceProxy.Summarize("Workspace Proxies:", docsURL)...)
131131
return msgs
132132
}
133133

@@ -141,7 +141,7 @@ type BaseReport struct {
141141

142142
// Summarize returns a list of strings containing the errors and warnings of BaseReport, if present.
143143
// All strings are prefixed with prefix.
144-
func (b *BaseReport) Summarize(prefix string) []string {
144+
func (b *BaseReport) Summarize(prefix, docsURL string) []string {
145145
if b == nil {
146146
return []string{}
147147
}
@@ -165,6 +165,7 @@ func (b *BaseReport) Summarize(prefix string) []string {
165165
_, _ = sb.WriteString("Warn: ")
166166
_, _ = sb.WriteString(warn.String())
167167
msgs = append(msgs, sb.String())
168+
msgs = append(msgs, "See: "+warn.URL(docsURL))
168169
}
169170
return msgs
170171
}

codersdk/healthsdk/healthsdk_test.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,24 @@ func TestSummarize(t *testing.T) {
4141
expected := []string{
4242
"Access URL: Error: test error",
4343
"Access URL: Warn: TEST: testing",
44+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
4445
"Database: Error: test error",
4546
"Database: Warn: TEST: testing",
47+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
4648
"DERP: Error: test error",
4749
"DERP: Warn: TEST: testing",
50+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
4851
"Provisioner Daemons: Error: test error",
4952
"Provisioner Daemons: Warn: TEST: testing",
53+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
5054
"Websocket: Error: test error",
5155
"Websocket: Warn: TEST: testing",
56+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
5257
"Workspace Proxies: Error: test error",
5358
"Workspace Proxies: Warn: TEST: testing",
59+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
5460
}
55-
actual := hr.Summarize()
61+
actual := hr.Summarize("")
5662
assert.Equal(t, expected, actual)
5763
})
5864

@@ -87,7 +93,9 @@ func TestSummarize(t *testing.T) {
8793
expected: []string{
8894
"Error: testing",
8995
"Warn: TEST01: testing one",
96+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test01",
9097
"Warn: TEST02: testing two",
98+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test02",
9199
},
92100
},
93101
{
@@ -109,14 +117,16 @@ func TestSummarize(t *testing.T) {
109117
expected: []string{
110118
"TEST: Error: testing",
111119
"TEST: Warn: TEST01: testing one",
120+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test01",
112121
"TEST: Warn: TEST02: testing two",
122+
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test02",
113123
},
114124
},
115125
} {
116126
tt := tt
117127
t.Run(tt.name, func(t *testing.T) {
118128
t.Parallel()
119-
actual := tt.br.Summarize(tt.pfx)
129+
actual := tt.br.Summarize(tt.pfx, "")
120130
if len(tt.expected) == 0 {
121131
assert.Empty(t, actual)
122132
return

0 commit comments

Comments
 (0)