mirror of https://github.com/coder/coder.git
feat(cli): support bundle: show links to docs/admin/healthcheck (#12974)
This commit is contained in:
parent
b598aef543
commit
8e1e0f04a4
|
@ -184,11 +184,12 @@ func (r *RootCmd) supportBundle() *serpent.Command {
|
||||||
_ = os.Remove(outputPath) // best effort
|
_ = os.Remove(outputPath) // best effort
|
||||||
return xerrors.Errorf("create support bundle: %w", err)
|
return xerrors.Errorf("create support bundle: %w", err)
|
||||||
}
|
}
|
||||||
deployHealthSummary := bun.Deployment.HealthReport.Summarize()
|
docsURL := bun.Deployment.Config.Values.DocsURL.String()
|
||||||
|
deployHealthSummary := bun.Deployment.HealthReport.Summarize(docsURL)
|
||||||
if len(deployHealthSummary) > 0 {
|
if len(deployHealthSummary) > 0 {
|
||||||
cliui.Warn(inv.Stdout, "Deployment health issues detected:", deployHealthSummary...)
|
cliui.Warn(inv.Stdout, "Deployment health issues detected:", deployHealthSummary...)
|
||||||
}
|
}
|
||||||
clientNetcheckSummary := bun.Network.Netcheck.Summarize("Client netcheck:")
|
clientNetcheckSummary := bun.Network.Netcheck.Summarize("Client netcheck:", docsURL)
|
||||||
if len(clientNetcheckSummary) > 0 {
|
if len(clientNetcheckSummary) > 0 {
|
||||||
cliui.Warn(inv.Stdout, "Networking issues detected:", deployHealthSummary...)
|
cliui.Warn(inv.Stdout, "Networking issues detected:", deployHealthSummary...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/buildinfo"
|
||||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,6 +45,11 @@ const (
|
||||||
CodeProvisionerDaemonAPIMajorVersionDeprecated Code = `EPD03`
|
CodeProvisionerDaemonAPIMajorVersionDeprecated Code = `EPD03`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Default docs URL
|
||||||
|
var (
|
||||||
|
docsURLDefault = "https://coder.com/docs/v2"
|
||||||
|
)
|
||||||
|
|
||||||
// @typescript-generate Severity
|
// @typescript-generate Severity
|
||||||
type Severity string
|
type Severity string
|
||||||
|
|
||||||
|
@ -72,6 +78,30 @@ func (m Message) String() string {
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// URL returns a link to the admin/healthcheck docs page for the given Message.
|
||||||
|
// NOTE: if using a custom docs URL, specify base.
|
||||||
|
func (m Message) URL(base string) string {
|
||||||
|
var codeAnchor string
|
||||||
|
if m.Code == "" {
|
||||||
|
codeAnchor = strings.ToLower(string(CodeUnknown))
|
||||||
|
} else {
|
||||||
|
codeAnchor = strings.ToLower(string(m.Code))
|
||||||
|
}
|
||||||
|
|
||||||
|
if base == "" {
|
||||||
|
base = docsURLDefault
|
||||||
|
versionPath := buildinfo.Version()
|
||||||
|
if buildinfo.IsDev() {
|
||||||
|
// for development versions, just use latest
|
||||||
|
versionPath = "latest"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s/admin/healthcheck#%s", base, versionPath, codeAnchor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't assume that custom docs URLs are versioned.
|
||||||
|
return fmt.Sprintf("%s/admin/healthcheck#%s", base, codeAnchor)
|
||||||
|
}
|
||||||
|
|
||||||
// Code is a stable identifier used to link to documentation.
|
// Code is a stable identifier used to link to documentation.
|
||||||
// @typescript-generate Code
|
// @typescript-generate Code
|
||||||
type Code string
|
type Code string
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package health_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coder/coder/v2/coderd/healthcheck/health"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_MessageURL(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
code health.Code
|
||||||
|
base string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"empty", "", "", "https://coder.com/docs/v2/latest/admin/healthcheck#eunknown"},
|
||||||
|
{"default", health.CodeAccessURLFetch, "", "https://coder.com/docs/v2/latest/admin/healthcheck#eacs03"},
|
||||||
|
{"custom docs base", health.CodeAccessURLFetch, "https://example.com/docs", "https://example.com/docs/admin/healthcheck#eacs03"},
|
||||||
|
} {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
uut := health.Message{Code: tt.code}
|
||||||
|
actual := uut.URL(tt.base)
|
||||||
|
assert.Equal(t, tt.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -120,14 +120,14 @@ type HealthcheckReport struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Summarize returns a summary of all errors and warnings of components of HealthcheckReport.
|
// Summarize returns a summary of all errors and warnings of components of HealthcheckReport.
|
||||||
func (r *HealthcheckReport) Summarize() []string {
|
func (r *HealthcheckReport) Summarize(docsURL string) []string {
|
||||||
var msgs []string
|
var msgs []string
|
||||||
msgs = append(msgs, r.AccessURL.Summarize("Access URL:")...)
|
msgs = append(msgs, r.AccessURL.Summarize("Access URL:", docsURL)...)
|
||||||
msgs = append(msgs, r.Database.Summarize("Database:")...)
|
msgs = append(msgs, r.Database.Summarize("Database:", docsURL)...)
|
||||||
msgs = append(msgs, r.DERP.Summarize("DERP:")...)
|
msgs = append(msgs, r.DERP.Summarize("DERP:", docsURL)...)
|
||||||
msgs = append(msgs, r.ProvisionerDaemons.Summarize("Provisioner Daemons:")...)
|
msgs = append(msgs, r.ProvisionerDaemons.Summarize("Provisioner Daemons:", docsURL)...)
|
||||||
msgs = append(msgs, r.Websocket.Summarize("Websocket:")...)
|
msgs = append(msgs, r.Websocket.Summarize("Websocket:", docsURL)...)
|
||||||
msgs = append(msgs, r.WorkspaceProxy.Summarize("Workspace Proxies:")...)
|
msgs = append(msgs, r.WorkspaceProxy.Summarize("Workspace Proxies:", docsURL)...)
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ type BaseReport struct {
|
||||||
|
|
||||||
// Summarize returns a list of strings containing the errors and warnings of BaseReport, if present.
|
// Summarize returns a list of strings containing the errors and warnings of BaseReport, if present.
|
||||||
// All strings are prefixed with prefix.
|
// All strings are prefixed with prefix.
|
||||||
func (b *BaseReport) Summarize(prefix string) []string {
|
func (b *BaseReport) Summarize(prefix, docsURL string) []string {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,7 @@ func (b *BaseReport) Summarize(prefix string) []string {
|
||||||
_, _ = sb.WriteString("Warn: ")
|
_, _ = sb.WriteString("Warn: ")
|
||||||
_, _ = sb.WriteString(warn.String())
|
_, _ = sb.WriteString(warn.String())
|
||||||
msgs = append(msgs, sb.String())
|
msgs = append(msgs, sb.String())
|
||||||
|
msgs = append(msgs, "See: "+warn.URL(docsURL))
|
||||||
}
|
}
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,18 +41,24 @@ func TestSummarize(t *testing.T) {
|
||||||
expected := []string{
|
expected := []string{
|
||||||
"Access URL: Error: test error",
|
"Access URL: Error: test error",
|
||||||
"Access URL: Warn: TEST: testing",
|
"Access URL: Warn: TEST: testing",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
|
||||||
"Database: Error: test error",
|
"Database: Error: test error",
|
||||||
"Database: Warn: TEST: testing",
|
"Database: Warn: TEST: testing",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
|
||||||
"DERP: Error: test error",
|
"DERP: Error: test error",
|
||||||
"DERP: Warn: TEST: testing",
|
"DERP: Warn: TEST: testing",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
|
||||||
"Provisioner Daemons: Error: test error",
|
"Provisioner Daemons: Error: test error",
|
||||||
"Provisioner Daemons: Warn: TEST: testing",
|
"Provisioner Daemons: Warn: TEST: testing",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
|
||||||
"Websocket: Error: test error",
|
"Websocket: Error: test error",
|
||||||
"Websocket: Warn: TEST: testing",
|
"Websocket: Warn: TEST: testing",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
|
||||||
"Workspace Proxies: Error: test error",
|
"Workspace Proxies: Error: test error",
|
||||||
"Workspace Proxies: Warn: TEST: testing",
|
"Workspace Proxies: Warn: TEST: testing",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test",
|
||||||
}
|
}
|
||||||
actual := hr.Summarize()
|
actual := hr.Summarize("")
|
||||||
assert.Equal(t, expected, actual)
|
assert.Equal(t, expected, actual)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -87,7 +93,9 @@ func TestSummarize(t *testing.T) {
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"Error: testing",
|
"Error: testing",
|
||||||
"Warn: TEST01: testing one",
|
"Warn: TEST01: testing one",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test01",
|
||||||
"Warn: TEST02: testing two",
|
"Warn: TEST02: testing two",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test02",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -109,14 +117,16 @@ func TestSummarize(t *testing.T) {
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"TEST: Error: testing",
|
"TEST: Error: testing",
|
||||||
"TEST: Warn: TEST01: testing one",
|
"TEST: Warn: TEST01: testing one",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test01",
|
||||||
"TEST: Warn: TEST02: testing two",
|
"TEST: Warn: TEST02: testing two",
|
||||||
|
"See: https://coder.com/docs/v2/latest/admin/healthcheck#test02",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
actual := tt.br.Summarize(tt.pfx)
|
actual := tt.br.Summarize(tt.pfx, "")
|
||||||
if len(tt.expected) == 0 {
|
if len(tt.expected) == 0 {
|
||||||
assert.Empty(t, actual)
|
assert.Empty(t, actual)
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in New Issue