mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
refactor(coderd/healthcheck): make Warnings an object with { Code, Message } (#10950)
- Adds health.Message { code string, mesasge string } - Refactors existing warnings []string to be of type []health.Message instead
This commit is contained in:
62
coderd/apidoc/docs.go
generated
62
coderd/apidoc/docs.go
generated
@ -12258,7 +12258,7 @@ const docTemplate = `{
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12297,7 +12297,7 @@ const docTemplate = `{
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12348,7 +12348,7 @@ const docTemplate = `{
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12367,6 +12367,56 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"health.Code": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"EUNKNOWN",
|
||||
"EWP01",
|
||||
"EWP02",
|
||||
"EWP03",
|
||||
"EWP04",
|
||||
"EDB01",
|
||||
"EDB02",
|
||||
"EWS01",
|
||||
"EWS02",
|
||||
"EWS03",
|
||||
"EACS01",
|
||||
"EACS02",
|
||||
"EACS03",
|
||||
"EACS04",
|
||||
"EDERP01",
|
||||
"EDERP02"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"CodeUnknown",
|
||||
"CodeProxyUpdate",
|
||||
"CodeProxyFetch",
|
||||
"CodeProxyVersionMismatch",
|
||||
"CodeProxyUnhealthy",
|
||||
"CodeDatabasePingFailed",
|
||||
"CodeDatabasePingSlow",
|
||||
"CodeWebsocketDial",
|
||||
"CodeWebsocketEcho",
|
||||
"CodeWebsocketMsg",
|
||||
"CodeAccessURLNotSet",
|
||||
"CodeAccessURLInvalid",
|
||||
"CodeAccessURLFetch",
|
||||
"CodeAccessURLNotOK",
|
||||
"CodeDERPNodeUsesWebsocket",
|
||||
"CodeDERPOneNodeUnhealthy"
|
||||
]
|
||||
},
|
||||
"health.Message": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"$ref": "#/definitions/health.Code"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"health.Severity": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@ -12420,7 +12470,7 @@ const docTemplate = `{
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12465,7 +12515,7 @@ const docTemplate = `{
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12579,7 +12629,7 @@ const docTemplate = `{
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
},
|
||||
"workspace_proxies": {
|
||||
|
62
coderd/apidoc/swagger.json
generated
62
coderd/apidoc/swagger.json
generated
@ -11163,7 +11163,7 @@
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11198,7 +11198,7 @@
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11245,7 +11245,7 @@
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11264,6 +11264,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"health.Code": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"EUNKNOWN",
|
||||
"EWP01",
|
||||
"EWP02",
|
||||
"EWP03",
|
||||
"EWP04",
|
||||
"EDB01",
|
||||
"EDB02",
|
||||
"EWS01",
|
||||
"EWS02",
|
||||
"EWS03",
|
||||
"EACS01",
|
||||
"EACS02",
|
||||
"EACS03",
|
||||
"EACS04",
|
||||
"EDERP01",
|
||||
"EDERP02"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"CodeUnknown",
|
||||
"CodeProxyUpdate",
|
||||
"CodeProxyFetch",
|
||||
"CodeProxyVersionMismatch",
|
||||
"CodeProxyUnhealthy",
|
||||
"CodeDatabasePingFailed",
|
||||
"CodeDatabasePingSlow",
|
||||
"CodeWebsocketDial",
|
||||
"CodeWebsocketEcho",
|
||||
"CodeWebsocketMsg",
|
||||
"CodeAccessURLNotSet",
|
||||
"CodeAccessURLInvalid",
|
||||
"CodeAccessURLFetch",
|
||||
"CodeAccessURLNotOK",
|
||||
"CodeDERPNodeUsesWebsocket",
|
||||
"CodeDERPOneNodeUnhealthy"
|
||||
]
|
||||
},
|
||||
"health.Message": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"$ref": "#/definitions/health.Code"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"health.Severity": {
|
||||
"type": "string",
|
||||
"enum": ["ok", "warning", "error"],
|
||||
@ -11305,7 +11355,7 @@
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11346,7 +11396,7 @@
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11452,7 +11502,7 @@
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/health.Message"
|
||||
}
|
||||
},
|
||||
"workspace_proxies": {
|
||||
|
@ -8,16 +8,15 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/healthcheck/health"
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
)
|
||||
|
||||
// @typescript-generate AccessURLReport
|
||||
type AccessURLReport struct {
|
||||
// Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead.
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []health.Message `json:"warnings"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
|
||||
AccessURL string `json:"access_url"`
|
||||
Reachable bool `json:"reachable"`
|
||||
@ -38,11 +37,11 @@ func (r *AccessURLReport) Run(ctx context.Context, opts *AccessURLReportOptions)
|
||||
defer cancel()
|
||||
|
||||
r.Severity = health.SeverityOK
|
||||
r.Warnings = []string{}
|
||||
r.Warnings = []health.Message{}
|
||||
r.Dismissed = opts.Dismissed
|
||||
|
||||
if opts.AccessURL == nil {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeAccessURLNotSet, "Access URL not set"))
|
||||
r.Error = health.Errorf(health.CodeAccessURLNotSet, "Access URL not set")
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
@ -54,21 +53,21 @@ func (r *AccessURLReport) Run(ctx context.Context, opts *AccessURLReportOptions)
|
||||
|
||||
accessURL, err := opts.AccessURL.Parse("/healthz")
|
||||
if err != nil {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeAccessURLInvalid, "parse healthz endpoint: %s", err))
|
||||
r.Error = health.Errorf(health.CodeAccessURLInvalid, "parse healthz endpoint: %s", err)
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", accessURL.String(), nil)
|
||||
if err != nil {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeAccessURLFetch, "create healthz request: %s", err))
|
||||
r.Error = health.Errorf(health.CodeAccessURLFetch, "create healthz request: %s", err)
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
|
||||
res, err := opts.Client.Do(req)
|
||||
if err != nil {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeAccessURLFetch, "get healthz endpoint: %s", err))
|
||||
r.Error = health.Errorf(health.CodeAccessURLFetch, "get healthz endpoint: %s", err)
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
@ -76,7 +75,7 @@ func (r *AccessURLReport) Run(ctx context.Context, opts *AccessURLReportOptions)
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeAccessURLFetch, "read healthz response: %s", err))
|
||||
r.Error = health.Errorf(health.CodeAccessURLFetch, "read healthz response: %s", err)
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ func TestAccessURL(t *testing.T) {
|
||||
assert.Equal(t, string(resp), report.HealthzResponse)
|
||||
assert.Nil(t, report.Error)
|
||||
if assert.NotEmpty(t, report.Warnings) {
|
||||
assert.Contains(t, report.Warnings[0], health.CodeAccessURLNotOK)
|
||||
assert.Equal(t, report.Warnings[0].Code, health.CodeAccessURLNotOK)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -4,11 +4,10 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/healthcheck/health"
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -18,10 +17,10 @@ const (
|
||||
// @typescript-generate DatabaseReport
|
||||
type DatabaseReport struct {
|
||||
// Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead.
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []health.Message `json:"warnings"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
|
||||
Reachable bool `json:"reachable"`
|
||||
Latency string `json:"latency"`
|
||||
@ -38,7 +37,7 @@ type DatabaseReportOptions struct {
|
||||
}
|
||||
|
||||
func (r *DatabaseReport) Run(ctx context.Context, opts *DatabaseReportOptions) {
|
||||
r.Warnings = []string{}
|
||||
r.Warnings = []health.Message{}
|
||||
r.Severity = health.SeverityOK
|
||||
r.Dismissed = opts.Dismissed
|
||||
|
||||
@ -55,7 +54,7 @@ func (r *DatabaseReport) Run(ctx context.Context, opts *DatabaseReportOptions) {
|
||||
for i := 0; i < pingCount; i++ {
|
||||
pong, err := opts.DB.Ping(ctx)
|
||||
if err != nil {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeDatabasePingFailed, "ping database: %s", err))
|
||||
r.Error = health.Errorf(health.CodeDatabasePingFailed, "ping database: %s", err)
|
||||
r.Severity = health.SeverityError
|
||||
|
||||
return
|
||||
|
@ -143,7 +143,7 @@ func TestDatabase(t *testing.T) {
|
||||
assert.Equal(t, time.Second.Milliseconds(), report.ThresholdMS)
|
||||
assert.Nil(t, report.Error)
|
||||
if assert.NotEmpty(t, report.Warnings) {
|
||||
assert.Contains(t, report.Warnings[0], health.CodeDatabasePingSlow)
|
||||
assert.Equal(t, report.Warnings[0].Code, health.CodeDatabasePingSlow)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -36,10 +36,10 @@ const (
|
||||
// @typescript-generate Report
|
||||
type Report struct {
|
||||
// Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead.
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []health.Message `json:"warnings"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
|
||||
Regions map[int]*RegionReport `json:"regions"`
|
||||
|
||||
@ -55,9 +55,9 @@ type RegionReport struct {
|
||||
mu sync.Mutex
|
||||
|
||||
// Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead.
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []health.Message `json:"warnings"`
|
||||
|
||||
Region *tailcfg.DERPRegion `json:"region"`
|
||||
NodeReports []*NodeReport `json:"node_reports"`
|
||||
@ -70,9 +70,9 @@ type NodeReport struct {
|
||||
clientCounter int
|
||||
|
||||
// Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead.
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity" enums:"ok,warning,error"`
|
||||
Warnings []health.Message `json:"warnings"`
|
||||
|
||||
Node *tailcfg.DERPNode `json:"node"`
|
||||
|
||||
@ -104,7 +104,7 @@ type ReportOptions struct {
|
||||
func (r *Report) Run(ctx context.Context, opts *ReportOptions) {
|
||||
r.Healthy = true
|
||||
r.Severity = health.SeverityOK
|
||||
r.Warnings = []string{}
|
||||
r.Warnings = []health.Message{}
|
||||
r.Dismissed = opts.Dismissed
|
||||
|
||||
r.Regions = map[int]*RegionReport{}
|
||||
@ -168,7 +168,7 @@ func (r *RegionReport) Run(ctx context.Context) {
|
||||
r.Healthy = true
|
||||
r.Severity = health.SeverityOK
|
||||
r.NodeReports = []*NodeReport{}
|
||||
r.Warnings = []string{}
|
||||
r.Warnings = []health.Message{}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
var unhealthyNodes int // atomic.Int64 is not mandatory as we depend on RegionReport mutex.
|
||||
@ -263,7 +263,7 @@ func (r *NodeReport) Run(ctx context.Context) {
|
||||
r.Severity = health.SeverityOK
|
||||
r.ClientLogs = [][]string{}
|
||||
r.ClientErrs = [][]string{}
|
||||
r.Warnings = []string{}
|
||||
r.Warnings = []health.Message{}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
|
@ -130,7 +130,7 @@ func TestDERP(t *testing.T) {
|
||||
assert.Equal(t, health.SeverityWarning, report.Severity)
|
||||
assert.True(t, report.Dismissed)
|
||||
if assert.NotEmpty(t, report.Warnings) {
|
||||
assert.Contains(t, report.Warnings[0], health.CodeDERPOneNodeUnhealthy)
|
||||
assert.Contains(t, report.Warnings[0].Code, health.CodeDERPOneNodeUnhealthy)
|
||||
}
|
||||
for _, region := range report.Regions {
|
||||
assert.True(t, region.Healthy)
|
||||
@ -236,7 +236,7 @@ func TestDERP(t *testing.T) {
|
||||
assert.True(t, report.Healthy)
|
||||
assert.Equal(t, health.SeverityWarning, report.Severity)
|
||||
if assert.NotEmpty(t, report.Warnings) {
|
||||
assert.Contains(t, report.Warnings[0], health.CodeDERPNodeUsesWebsocket)
|
||||
assert.Equal(t, report.Warnings[0].Code, health.CodeDERPNodeUsesWebsocket)
|
||||
}
|
||||
for _, region := range report.Regions {
|
||||
assert.True(t, region.Healthy)
|
||||
|
@ -3,6 +3,8 @@ package health
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -47,16 +49,34 @@ func (s Severity) Value() int {
|
||||
return severityRank[s]
|
||||
}
|
||||
|
||||
// @typescript-generate Message
|
||||
type Message struct {
|
||||
Code Code `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (m Message) String() string {
|
||||
var sb strings.Builder
|
||||
_, _ = sb.WriteString(string(m.Code))
|
||||
_, _ = sb.WriteRune(':')
|
||||
_, _ = sb.WriteRune(' ')
|
||||
_, _ = sb.WriteString(m.Message)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Code is a stable identifier used to link to documentation.
|
||||
// @typescript-generate Code
|
||||
type Code string
|
||||
|
||||
// Messagef is a convenience function for formatting a healthcheck error message.
|
||||
func Messagef(code Code, msg string, args ...any) string {
|
||||
var sb strings.Builder
|
||||
_, _ = sb.WriteString(string(code))
|
||||
_, _ = sb.WriteRune(':')
|
||||
_, _ = sb.WriteRune(' ')
|
||||
_, _ = sb.WriteString(fmt.Sprintf(msg, args...))
|
||||
return sb.String()
|
||||
// Messagef is a convenience function for returning a health.Message
|
||||
func Messagef(code Code, msg string, args ...any) Message {
|
||||
return Message{
|
||||
Code: code,
|
||||
Message: fmt.Sprintf(msg, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf is a convenience function for returning a stringly-typed version of a Message.
|
||||
func Errorf(code Code, msg string, args ...any) *string {
|
||||
return ptr.Ref(Messagef(code, msg, args...).String())
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.DERP.Error = ptr.Ref(health.Messagef(health.CodeUnknown, "derp report panic: %s", err))
|
||||
report.DERP.Error = health.Errorf(health.CodeUnknown, "derp report panic: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -115,7 +115,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.AccessURL.Error = ptr.Ref(health.Messagef(health.CodeUnknown, "access url report panic: %s", err))
|
||||
report.AccessURL.Error = health.Errorf(health.CodeUnknown, "access url report panic: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -127,7 +127,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.Websocket.Error = ptr.Ref(health.Messagef(health.CodeUnknown, "websocket report panic: %s", err))
|
||||
report.Websocket.Error = health.Errorf(health.CodeUnknown, "websocket report panic: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -139,7 +139,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.Database.Error = ptr.Ref(health.Messagef(health.CodeUnknown, "database report panic: %s", err))
|
||||
report.Database.Error = health.Errorf(health.CodeUnknown, "database report panic: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -151,7 +151,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.WorkspaceProxy.Error = ptr.Ref(health.Messagef(health.CodeUnknown, "proxy report panic: %s", err))
|
||||
report.WorkspaceProxy.Error = health.Errorf(health.CodeUnknown, "proxy report panic: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -107,7 +107,7 @@ func TestHealthcheck(t *testing.T) {
|
||||
checker: &testChecker{
|
||||
DERPReport: derphealth.Report{
|
||||
Healthy: true,
|
||||
Warnings: []string{"foobar"},
|
||||
Warnings: []health.Message{{Message: "foobar", Code: "EFOOBAR"}},
|
||||
Severity: health.SeverityWarning,
|
||||
},
|
||||
AccessURLReport: healthcheck.AccessURLReport{
|
||||
@ -259,7 +259,7 @@ func TestHealthcheck(t *testing.T) {
|
||||
},
|
||||
WorkspaceProxyReport: healthcheck.WorkspaceProxyReport{
|
||||
Healthy: true,
|
||||
Warnings: []string{"foobar"},
|
||||
Warnings: []health.Message{{Message: "foobar", Code: "EFOOBAR"}},
|
||||
Severity: health.SeverityWarning,
|
||||
},
|
||||
},
|
||||
|
@ -13,7 +13,6 @@ import (
|
||||
"nhooyr.io/websocket"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/healthcheck/health"
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
)
|
||||
|
||||
// @typescript-generate WebsocketReport
|
||||
@ -76,7 +75,7 @@ func (r *WebsocketReport) Run(ctx context.Context, opts *WebsocketReportOptions)
|
||||
}
|
||||
if err != nil {
|
||||
r.Error = convertError(xerrors.Errorf("websocket dial: %w", err))
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeWebsocketDial, "websocket dial: %s", err))
|
||||
r.Error = health.Errorf(health.CodeWebsocketDial, "websocket dial: %s", err)
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
@ -86,26 +85,26 @@ func (r *WebsocketReport) Run(ctx context.Context, opts *WebsocketReportOptions)
|
||||
msg := strconv.Itoa(i)
|
||||
err := c.Write(ctx, websocket.MessageText, []byte(msg))
|
||||
if err != nil {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeWebsocketEcho, "write message: %s", err))
|
||||
r.Error = health.Errorf(health.CodeWebsocketEcho, "write message: %s", err)
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
|
||||
ty, got, err := c.Read(ctx)
|
||||
if err != nil {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeWebsocketEcho, "read message: %s", err))
|
||||
r.Error = health.Errorf(health.CodeWebsocketEcho, "read message: %s", err)
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
|
||||
if ty != websocket.MessageText {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeWebsocketMsg, "received incorrect message type: %v", ty))
|
||||
r.Error = health.Errorf(health.CodeWebsocketMsg, "received incorrect message type: %v", ty)
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
|
||||
if string(got) != msg {
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeWebsocketMsg, "received incorrect message: wanted %q, got %q", msg, string(got)))
|
||||
r.Error = health.Errorf(health.CodeWebsocketMsg, "received incorrect message: wanted %q, got %q", msg, string(got))
|
||||
r.Severity = health.SeverityError
|
||||
return
|
||||
}
|
||||
|
@ -16,11 +16,11 @@ import (
|
||||
|
||||
// @typescript-generate WorkspaceProxyReport
|
||||
type WorkspaceProxyReport struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity"`
|
||||
Warnings []string `json:"warnings"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
Error *string `json:"error"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Severity health.Severity `json:"severity"`
|
||||
Warnings []health.Message `json:"warnings"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
Error *string `json:"error"`
|
||||
|
||||
WorkspaceProxies codersdk.RegionsResponse[codersdk.WorkspaceProxy] `json:"workspace_proxies"`
|
||||
}
|
||||
@ -54,7 +54,7 @@ func (*AGPLWorkspaceProxiesFetchUpdater) Update(context.Context) error {
|
||||
func (r *WorkspaceProxyReport) Run(ctx context.Context, opts *WorkspaceProxyReportOptions) {
|
||||
r.Healthy = true
|
||||
r.Severity = health.SeverityOK
|
||||
r.Warnings = []string{}
|
||||
r.Warnings = []health.Message{}
|
||||
r.Dismissed = opts.Dismissed
|
||||
|
||||
if opts.WorkspaceProxiesFetchUpdater == nil {
|
||||
@ -72,7 +72,7 @@ func (r *WorkspaceProxyReport) Run(ctx context.Context, opts *WorkspaceProxyRepo
|
||||
if err != nil {
|
||||
r.Healthy = false
|
||||
r.Severity = health.SeverityError
|
||||
r.Error = ptr.Ref(health.Messagef(health.CodeProxyFetch, "fetch workspace proxies: %s", err))
|
||||
r.Error = health.Errorf(health.CodeProxyFetch, "fetch workspace proxies: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ func (r *WorkspaceProxyReport) Run(ctx context.Context, opts *WorkspaceProxyRepo
|
||||
case health.SeverityWarning, health.SeverityOK:
|
||||
r.Warnings = append(r.Warnings, health.Messagef(health.CodeProxyUnhealthy, err))
|
||||
case health.SeverityError:
|
||||
r.appendError(health.Messagef(health.CodeProxyUnhealthy, err))
|
||||
r.appendError(*health.Errorf(health.CodeProxyUnhealthy, err))
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ func (r *WorkspaceProxyReport) Run(ctx context.Context, opts *WorkspaceProxyRepo
|
||||
if vErr := checkVersion(proxy, opts.CurrentVersion); vErr != nil {
|
||||
r.Healthy = false
|
||||
r.Severity = health.SeverityError
|
||||
r.appendError(health.Messagef(health.CodeProxyVersionMismatch, vErr.Error()))
|
||||
r.appendError(*health.Errorf(health.CodeProxyVersionMismatch, vErr.Error()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package healthcheck_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -27,7 +26,7 @@ func TestWorkspaceProxies(t *testing.T) {
|
||||
updateProxyHealth func(context.Context) error
|
||||
expectedHealthy bool
|
||||
expectedError string
|
||||
expectedWarning string
|
||||
expectedWarningCode health.Code
|
||||
expectedSeverity health.Severity
|
||||
}{
|
||||
{
|
||||
@ -103,10 +102,10 @@ func TestWorkspaceProxies(t *testing.T) {
|
||||
fakeWorkspaceProxy("alpha", false, currentVersion),
|
||||
fakeWorkspaceProxy("beta", true, currentVersion),
|
||||
),
|
||||
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
||||
expectedHealthy: true,
|
||||
expectedSeverity: health.SeverityWarning,
|
||||
expectedWarning: string(health.CodeProxyUnhealthy),
|
||||
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
||||
expectedHealthy: true,
|
||||
expectedSeverity: health.SeverityWarning,
|
||||
expectedWarningCode: health.CodeProxyUnhealthy,
|
||||
},
|
||||
{
|
||||
name: "Enabled/AllUnhealthy",
|
||||
@ -163,7 +162,7 @@ func TestWorkspaceProxies(t *testing.T) {
|
||||
updateProxyHealth: fakeUpdateProxyHealth(assert.AnError),
|
||||
expectedHealthy: true,
|
||||
expectedSeverity: health.SeverityWarning,
|
||||
expectedWarning: string(health.CodeProxyUpdate),
|
||||
expectedWarningCode: health.CodeProxyUpdate,
|
||||
},
|
||||
} {
|
||||
tt := tt
|
||||
@ -190,15 +189,15 @@ func TestWorkspaceProxies(t *testing.T) {
|
||||
} else {
|
||||
assert.Nil(t, rpt.Error)
|
||||
}
|
||||
if tt.expectedWarning != "" && assert.NotEmpty(t, rpt.Warnings) {
|
||||
if tt.expectedWarningCode != "" && assert.NotEmpty(t, rpt.Warnings) {
|
||||
var found bool
|
||||
for _, w := range rpt.Warnings {
|
||||
if strings.Contains(w, tt.expectedWarning) {
|
||||
if w.Code == tt.expectedWarningCode {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "expected warning %s not found in %v", tt.expectedWarning, rpt.Warnings)
|
||||
assert.True(t, found, "expected warning %s not found in %v", tt.expectedWarningCode, rpt.Warnings)
|
||||
} else {
|
||||
assert.Empty(t, rpt.Warnings)
|
||||
}
|
||||
|
Reference in New Issue
Block a user