mirror of https://github.com/coder/coder.git
fix: use `*string` instead of `error` in healthcheck response (#8234)
This commit is contained in:
parent
e2e07b01e9
commit
b8a143566b
|
@ -9953,7 +9953,12 @@ const docTemplate = `{
|
|||
"healthcheck.AccessURLReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"access_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -9978,7 +9983,9 @@ const docTemplate = `{
|
|||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {}
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"client_logs": {
|
||||
|
@ -9990,7 +9997,9 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -10014,7 +10023,9 @@ const docTemplate = `{
|
|||
"healthcheck.DERPRegionReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -10032,14 +10043,18 @@ const docTemplate = `{
|
|||
"healthcheck.DERPReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"netcheck": {
|
||||
"$ref": "#/definitions/netcheck.Report"
|
||||
},
|
||||
"netcheck_err": {},
|
||||
"netcheck_err": {
|
||||
"type": "string"
|
||||
},
|
||||
"netcheck_logs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -10069,7 +10084,9 @@ const docTemplate = `{
|
|||
"healthcheck.DatabaseReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -10087,6 +10104,10 @@ const docTemplate = `{
|
|||
"access_url": {
|
||||
"$ref": "#/definitions/healthcheck.AccessURLReport"
|
||||
},
|
||||
"coder_version": {
|
||||
"description": "The Coder version of the server that the report was generated on.",
|
||||
"type": "string"
|
||||
},
|
||||
"database": {
|
||||
"$ref": "#/definitions/healthcheck.DatabaseReport"
|
||||
},
|
||||
|
@ -10094,6 +10115,7 @@ const docTemplate = `{
|
|||
"$ref": "#/definitions/healthcheck.DERPReport"
|
||||
},
|
||||
"failing_sections": {
|
||||
"description": "FailingSections is a list of sections that have failed their healthcheck.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
|
@ -10115,7 +10137,9 @@ const docTemplate = `{
|
|||
"healthcheck.WebsocketReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
@ -9008,7 +9008,12 @@
|
|||
"healthcheck.AccessURLReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"access_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -9033,7 +9038,9 @@
|
|||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {}
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"client_logs": {
|
||||
|
@ -9045,7 +9052,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -9069,7 +9078,9 @@
|
|||
"healthcheck.DERPRegionReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -9087,14 +9098,18 @@
|
|||
"healthcheck.DERPReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"netcheck": {
|
||||
"$ref": "#/definitions/netcheck.Report"
|
||||
},
|
||||
"netcheck_err": {},
|
||||
"netcheck_err": {
|
||||
"type": "string"
|
||||
},
|
||||
"netcheck_logs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -9124,7 +9139,9 @@
|
|||
"healthcheck.DatabaseReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -9142,6 +9159,10 @@
|
|||
"access_url": {
|
||||
"$ref": "#/definitions/healthcheck.AccessURLReport"
|
||||
},
|
||||
"coder_version": {
|
||||
"description": "The Coder version of the server that the report was generated on.",
|
||||
"type": "string"
|
||||
},
|
||||
"database": {
|
||||
"$ref": "#/definitions/healthcheck.DatabaseReport"
|
||||
},
|
||||
|
@ -9149,6 +9170,7 @@
|
|||
"$ref": "#/definitions/healthcheck.DERPReport"
|
||||
},
|
||||
"failing_sections": {
|
||||
"description": "FailingSections is a list of sections that have failed their healthcheck.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
|
@ -9170,7 +9192,9 @@
|
|||
"healthcheck.WebsocketReport": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
@ -8,14 +8,17 @@ import (
|
|||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/coderd/util/ptr"
|
||||
)
|
||||
|
||||
type AccessURLReport struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Reachable bool `json:"reachable"`
|
||||
StatusCode int `json:"status_code"`
|
||||
HealthzResponse string `json:"healthz_response"`
|
||||
Error error `json:"error"`
|
||||
AccessURL string `json:"access_url"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Reachable bool `json:"reachable"`
|
||||
StatusCode int `json:"status_code"`
|
||||
HealthzResponse string `json:"healthz_response"`
|
||||
Error *string `json:"error"`
|
||||
}
|
||||
|
||||
type AccessURLReportOptions struct {
|
||||
|
@ -28,9 +31,10 @@ func (r *AccessURLReport) Run(ctx context.Context, opts *AccessURLReportOptions)
|
|||
defer cancel()
|
||||
|
||||
if opts.AccessURL == nil {
|
||||
r.Error = xerrors.New("access URL is nil")
|
||||
r.Error = ptr.Ref("access URL is nil")
|
||||
return
|
||||
}
|
||||
r.AccessURL = opts.AccessURL.String()
|
||||
|
||||
if opts.Client == nil {
|
||||
opts.Client = http.DefaultClient
|
||||
|
@ -38,26 +42,26 @@ func (r *AccessURLReport) Run(ctx context.Context, opts *AccessURLReportOptions)
|
|||
|
||||
accessURL, err := opts.AccessURL.Parse("/healthz")
|
||||
if err != nil {
|
||||
r.Error = xerrors.Errorf("parse healthz endpoint: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("parse healthz endpoint: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", accessURL.String(), nil)
|
||||
if err != nil {
|
||||
r.Error = xerrors.Errorf("create healthz request: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("create healthz request: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
res, err := opts.Client.Do(req)
|
||||
if err != nil {
|
||||
r.Error = xerrors.Errorf("get healthz endpoint: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("get healthz endpoint: %w", err))
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
r.Error = xerrors.Errorf("read healthz response: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("read healthz response: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ func TestAccessURL(t *testing.T) {
|
|||
assert.True(t, report.Reachable)
|
||||
assert.Equal(t, http.StatusOK, report.StatusCode)
|
||||
assert.Equal(t, "OK", report.HealthzResponse)
|
||||
assert.NoError(t, report.Error)
|
||||
assert.Nil(t, report.Error)
|
||||
})
|
||||
|
||||
t.Run("404", func(t *testing.T) {
|
||||
|
@ -66,7 +66,7 @@ func TestAccessURL(t *testing.T) {
|
|||
assert.True(t, report.Reachable)
|
||||
assert.Equal(t, http.StatusNotFound, report.StatusCode)
|
||||
assert.Equal(t, string(resp), report.HealthzResponse)
|
||||
assert.NoError(t, report.Error)
|
||||
assert.Nil(t, report.Error)
|
||||
})
|
||||
|
||||
t.Run("ClientErr", func(t *testing.T) {
|
||||
|
@ -102,7 +102,8 @@ func TestAccessURL(t *testing.T) {
|
|||
assert.False(t, report.Reachable)
|
||||
assert.Equal(t, 0, report.StatusCode)
|
||||
assert.Equal(t, "", report.HealthzResponse)
|
||||
assert.ErrorIs(t, report.Error, expErr)
|
||||
require.NotNil(t, report.Error)
|
||||
assert.Contains(t, *report.Error, expErr.Error())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ type DatabaseReport struct {
|
|||
Healthy bool `json:"healthy"`
|
||||
Reachable bool `json:"reachable"`
|
||||
Latency time.Duration `json:"latency"`
|
||||
Error error `json:"error"`
|
||||
Error *string `json:"error"`
|
||||
}
|
||||
|
||||
type DatabaseReportOptions struct {
|
||||
|
@ -31,7 +31,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 = xerrors.Errorf("ping: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("ping: %w", err))
|
||||
return
|
||||
}
|
||||
pings = append(pings, pong)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/coderd/database/dbmock"
|
||||
|
@ -35,7 +36,7 @@ func TestDatabase(t *testing.T) {
|
|||
assert.True(t, report.Healthy)
|
||||
assert.True(t, report.Reachable)
|
||||
assert.Equal(t, ping, report.Latency)
|
||||
assert.NoError(t, report.Error)
|
||||
assert.Nil(t, report.Error)
|
||||
})
|
||||
|
||||
t.Run("Error", func(t *testing.T) {
|
||||
|
@ -56,7 +57,8 @@ func TestDatabase(t *testing.T) {
|
|||
assert.False(t, report.Healthy)
|
||||
assert.False(t, report.Reachable)
|
||||
assert.Zero(t, report.Latency)
|
||||
assert.ErrorIs(t, report.Error, err)
|
||||
require.NotNil(t, report.Error)
|
||||
assert.Contains(t, *report.Error, err.Error())
|
||||
})
|
||||
|
||||
t.Run("Median", func(t *testing.T) {
|
||||
|
@ -80,6 +82,6 @@ func TestDatabase(t *testing.T) {
|
|||
assert.True(t, report.Healthy)
|
||||
assert.True(t, report.Reachable)
|
||||
assert.Equal(t, time.Millisecond, report.Latency)
|
||||
assert.NoError(t, report.Error)
|
||||
assert.Nil(t, report.Error)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -30,10 +30,10 @@ type DERPReport struct {
|
|||
Regions map[int]*DERPRegionReport `json:"regions"`
|
||||
|
||||
Netcheck *netcheck.Report `json:"netcheck"`
|
||||
NetcheckErr error `json:"netcheck_err"`
|
||||
NetcheckErr *string `json:"netcheck_err"`
|
||||
NetcheckLogs []string `json:"netcheck_logs"`
|
||||
|
||||
Error error `json:"error"`
|
||||
Error *string `json:"error"`
|
||||
}
|
||||
|
||||
type DERPRegionReport struct {
|
||||
|
@ -42,7 +42,7 @@ type DERPRegionReport struct {
|
|||
|
||||
Region *tailcfg.DERPRegion `json:"region"`
|
||||
NodeReports []*DERPNodeReport `json:"node_reports"`
|
||||
Error error `json:"error"`
|
||||
Error *string `json:"error"`
|
||||
}
|
||||
type DERPNodeReport struct {
|
||||
mu sync.Mutex
|
||||
|
@ -56,8 +56,8 @@ type DERPNodeReport struct {
|
|||
RoundTripPing time.Duration `json:"round_trip_ping"`
|
||||
UsesWebsocket bool `json:"uses_websocket"`
|
||||
ClientLogs [][]string `json:"client_logs"`
|
||||
ClientErrs [][]error `json:"client_errs"`
|
||||
Error error `json:"error"`
|
||||
ClientErrs [][]string `json:"client_errs"`
|
||||
Error *string `json:"error"`
|
||||
|
||||
STUN DERPStunReport `json:"stun"`
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ func (r *DERPReport) Run(ctx context.Context, opts *DERPReportOptions) {
|
|||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
regionReport.Error = xerrors.Errorf("%v", err)
|
||||
regionReport.Error = ptr.Ref(fmt.Sprint(err))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -115,7 +115,9 @@ func (r *DERPReport) Run(ctx context.Context, opts *DERPReportOptions) {
|
|||
PortMapper: portmapper.NewClient(tslogger.WithPrefix(ncLogf, "portmap: "), nil),
|
||||
Logf: tslogger.WithPrefix(ncLogf, "netcheck: "),
|
||||
}
|
||||
r.Netcheck, r.NetcheckErr = nc.GetReport(ctx, opts.DERPMap)
|
||||
ncReport, netcheckErr := nc.GetReport(ctx, opts.DERPMap)
|
||||
r.Netcheck = ncReport
|
||||
r.NetcheckErr = convertError(netcheckErr)
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
@ -140,7 +142,7 @@ func (r *DERPRegionReport) Run(ctx context.Context) {
|
|||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
nodeReport.Error = xerrors.Errorf("%v", err)
|
||||
nodeReport.Error = ptr.Ref(fmt.Sprint(err))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -179,7 +181,7 @@ func (r *DERPNodeReport) Run(ctx context.Context) {
|
|||
defer cancel()
|
||||
|
||||
r.ClientLogs = [][]string{}
|
||||
r.ClientErrs = [][]error{}
|
||||
r.ClientErrs = [][]string{}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
|
@ -376,7 +378,7 @@ func (r *DERPNodeReport) stunAddr(ctx context.Context) (string, int, error) {
|
|||
|
||||
func (r *DERPNodeReport) writeClientErr(clientID int, err error) {
|
||||
r.mu.Lock()
|
||||
r.ClientErrs[clientID] = append(r.ClientErrs[clientID], err)
|
||||
r.ClientErrs[clientID] = append(r.ClientErrs[clientID], err.Error())
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
|
@ -385,7 +387,7 @@ func (r *DERPNodeReport) derpClient(ctx context.Context, derpURL *url.URL) (*der
|
|||
id := r.clientCounter
|
||||
r.clientCounter++
|
||||
r.ClientLogs = append(r.ClientLogs, []string{})
|
||||
r.ClientErrs = append(r.ClientErrs, []error{})
|
||||
r.ClientErrs = append(r.ClientErrs, []string{})
|
||||
r.mu.Unlock()
|
||||
|
||||
client, err := derphttp.NewClient(key.NewNode(), derpURL.String(), func(format string, args ...any) {
|
||||
|
|
|
@ -2,15 +2,17 @@ package healthcheck
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
"tailscale.com/tailcfg"
|
||||
|
||||
"github.com/coder/coder/buildinfo"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/util/ptr"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -31,13 +33,17 @@ type Report struct {
|
|||
// Time is the time the report was generated at.
|
||||
Time time.Time `json:"time"`
|
||||
// Healthy is true if the report returns no errors.
|
||||
Healthy bool `json:"healthy"`
|
||||
Healthy bool `json:"healthy"`
|
||||
// FailingSections is a list of sections that have failed their healthcheck.
|
||||
FailingSections []string `json:"failing_sections"`
|
||||
|
||||
DERP DERPReport `json:"derp"`
|
||||
AccessURL AccessURLReport `json:"access_url"`
|
||||
Websocket WebsocketReport `json:"websocket"`
|
||||
Database DatabaseReport `json:"database"`
|
||||
|
||||
// The Coder version of the server that the report was generated on.
|
||||
CoderVersion string `json:"coder_version"`
|
||||
}
|
||||
|
||||
type ReportOptions struct {
|
||||
|
@ -88,7 +94,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
|||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.DERP.Error = xerrors.Errorf("%v", err)
|
||||
report.DERP.Error = ptr.Ref(fmt.Sprint(err))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -102,7 +108,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
|||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.AccessURL.Error = xerrors.Errorf("%v", err)
|
||||
report.AccessURL.Error = ptr.Ref(fmt.Sprint(err))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -117,7 +123,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
|||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.Websocket.Error = xerrors.Errorf("%v", err)
|
||||
report.Websocket.Error = ptr.Ref(fmt.Sprint(err))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -132,7 +138,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
|||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
report.Database.Error = xerrors.Errorf("%v", err)
|
||||
report.Database.Error = ptr.Ref(fmt.Sprint(err))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -141,7 +147,9 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
|||
})
|
||||
}()
|
||||
|
||||
report.CoderVersion = buildinfo.Version()
|
||||
wg.Wait()
|
||||
|
||||
report.Time = time.Now()
|
||||
if !report.DERP.Healthy {
|
||||
report.FailingSections = append(report.FailingSections, SectionDERP)
|
||||
|
@ -159,3 +167,11 @@ func Run(ctx context.Context, opts *ReportOptions) *Report {
|
|||
report.Healthy = len(report.FailingSections) == 0
|
||||
return &report
|
||||
}
|
||||
|
||||
func convertError(err error) *string {
|
||||
if err != nil {
|
||||
return ptr.Ref(err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -155,6 +155,7 @@ func TestHealthcheck(t *testing.T) {
|
|||
assert.Equal(t, c.checker.AccessURLReport.Healthy, report.AccessURL.Healthy)
|
||||
assert.Equal(t, c.checker.WebsocketReport.Healthy, report.Websocket.Healthy)
|
||||
assert.NotZero(t, report.Time)
|
||||
assert.NotZero(t, report.CoderVersion)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ type WebsocketReportOptions struct {
|
|||
type WebsocketReport struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Response WebsocketResponse `json:"response"`
|
||||
Error error `json:"error"`
|
||||
Error *string `json:"error"`
|
||||
}
|
||||
|
||||
type WebsocketResponse struct {
|
||||
|
@ -37,7 +37,7 @@ func (r *WebsocketReport) Run(ctx context.Context, opts *WebsocketReportOptions)
|
|||
|
||||
u, err := opts.AccessURL.Parse("/api/v2/debug/ws")
|
||||
if err != nil {
|
||||
r.Error = xerrors.Errorf("parse access url: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("parse access url: %w", err))
|
||||
return
|
||||
}
|
||||
if u.Scheme == "https" {
|
||||
|
@ -66,7 +66,7 @@ func (r *WebsocketReport) Run(ctx context.Context, opts *WebsocketReportOptions)
|
|||
}
|
||||
}
|
||||
if err != nil {
|
||||
r.Error = xerrors.Errorf("websocket dial: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("websocket dial: %w", err))
|
||||
return
|
||||
}
|
||||
defer c.Close(websocket.StatusGoingAway, "goodbye")
|
||||
|
@ -75,23 +75,23 @@ 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 = xerrors.Errorf("write message: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("write message: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
ty, got, err := c.Read(ctx)
|
||||
if err != nil {
|
||||
r.Error = xerrors.Errorf("read message: %w", err)
|
||||
r.Error = convertError(xerrors.Errorf("read message: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
if ty != websocket.MessageText {
|
||||
r.Error = xerrors.Errorf("received incorrect message type: %v", ty)
|
||||
r.Error = convertError(xerrors.Errorf("received incorrect message type: %v", ty))
|
||||
return
|
||||
}
|
||||
|
||||
if string(got) != msg {
|
||||
r.Error = xerrors.Errorf("received incorrect message: wanted %q, got %q", msg, string(got))
|
||||
r.Error = convertError(xerrors.Errorf("received incorrect message: wanted %q, got %q", msg, string(got)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func TestWebsocket(t *testing.T) {
|
|||
APIKey: "test",
|
||||
})
|
||||
|
||||
require.NoError(t, wsReport.Error)
|
||||
require.Nil(t, wsReport.Error)
|
||||
})
|
||||
|
||||
t.Run("Error", func(t *testing.T) {
|
||||
|
@ -62,7 +62,7 @@ func TestWebsocket(t *testing.T) {
|
|||
APIKey: "test",
|
||||
})
|
||||
|
||||
require.Error(t, wsReport.Error)
|
||||
require.NotNil(t, wsReport.Error)
|
||||
assert.Equal(t, wsReport.Response.Body, "test error")
|
||||
assert.Equal(t, wsReport.Response.Code, http.StatusBadRequest)
|
||||
})
|
||||
|
|
|
@ -40,20 +40,22 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \
|
|||
```json
|
||||
{
|
||||
"access_url": {
|
||||
"error": null,
|
||||
"access_url": "string",
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"healthz_response": "string",
|
||||
"reachable": true,
|
||||
"status_code": 0
|
||||
},
|
||||
"coder_version": "string",
|
||||
"database": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"latency": 0,
|
||||
"reachable": true
|
||||
},
|
||||
"derp": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"netcheck": {
|
||||
"captivePortal": "string",
|
||||
|
@ -85,18 +87,18 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \
|
|||
"udp": true,
|
||||
"upnP": "string"
|
||||
},
|
||||
"netcheck_err": null,
|
||||
"netcheck_err": "string",
|
||||
"netcheck_logs": ["string"],
|
||||
"regions": {
|
||||
"property1": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node_reports": [
|
||||
{
|
||||
"can_exchange_messages": true,
|
||||
"client_errs": [[null]],
|
||||
"client_errs": [["string"]],
|
||||
"client_logs": [["string"]],
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node": {
|
||||
"certName": "string",
|
||||
|
@ -150,14 +152,14 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \
|
|||
}
|
||||
},
|
||||
"property2": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node_reports": [
|
||||
{
|
||||
"can_exchange_messages": true,
|
||||
"client_errs": [[null]],
|
||||
"client_errs": [["string"]],
|
||||
"client_logs": [["string"]],
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node": {
|
||||
"certName": "string",
|
||||
|
@ -216,7 +218,7 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \
|
|||
"healthy": true,
|
||||
"time": "string",
|
||||
"websocket": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"response": {
|
||||
"body": "string",
|
||||
|
|
|
@ -5665,7 +5665,8 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
```json
|
||||
{
|
||||
"error": null,
|
||||
"access_url": "string",
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"healthz_response": "string",
|
||||
"reachable": true,
|
||||
|
@ -5677,7 +5678,8 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------------ | ------- | -------- | ------------ | ----------- |
|
||||
| `error` | any | false | | |
|
||||
| `access_url` | string | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `healthy` | boolean | false | | |
|
||||
| `healthz_response` | string | false | | |
|
||||
| `reachable` | boolean | false | | |
|
||||
|
@ -5688,9 +5690,9 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
```json
|
||||
{
|
||||
"can_exchange_messages": true,
|
||||
"client_errs": [[null]],
|
||||
"client_errs": [["string"]],
|
||||
"client_logs": [["string"]],
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node": {
|
||||
"certName": "string",
|
||||
|
@ -5727,7 +5729,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
| `can_exchange_messages` | boolean | false | | |
|
||||
| `client_errs` | array of array | false | | |
|
||||
| `client_logs` | array of array | false | | |
|
||||
| `error` | any | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `healthy` | boolean | false | | |
|
||||
| `node` | [tailcfg.DERPNode](#tailcfgderpnode) | false | | |
|
||||
| `node_info` | [derp.ServerInfoMessage](#derpserverinfomessage) | false | | |
|
||||
|
@ -5739,14 +5741,14 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
```json
|
||||
{
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node_reports": [
|
||||
{
|
||||
"can_exchange_messages": true,
|
||||
"client_errs": [[null]],
|
||||
"client_errs": [["string"]],
|
||||
"client_logs": [["string"]],
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node": {
|
||||
"certName": "string",
|
||||
|
@ -5805,7 +5807,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ----------------------------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `error` | any | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `healthy` | boolean | false | | |
|
||||
| `node_reports` | array of [healthcheck.DERPNodeReport](#healthcheckderpnodereport) | false | | |
|
||||
| `region` | [tailcfg.DERPRegion](#tailcfgderpregion) | false | | |
|
||||
|
@ -5814,7 +5816,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
```json
|
||||
{
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"netcheck": {
|
||||
"captivePortal": "string",
|
||||
|
@ -5846,18 +5848,18 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
"udp": true,
|
||||
"upnP": "string"
|
||||
},
|
||||
"netcheck_err": null,
|
||||
"netcheck_err": "string",
|
||||
"netcheck_logs": ["string"],
|
||||
"regions": {
|
||||
"property1": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node_reports": [
|
||||
{
|
||||
"can_exchange_messages": true,
|
||||
"client_errs": [[null]],
|
||||
"client_errs": [["string"]],
|
||||
"client_logs": [["string"]],
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node": {
|
||||
"certName": "string",
|
||||
|
@ -5911,14 +5913,14 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
}
|
||||
},
|
||||
"property2": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node_reports": [
|
||||
{
|
||||
"can_exchange_messages": true,
|
||||
"client_errs": [[null]],
|
||||
"client_errs": [["string"]],
|
||||
"client_logs": [["string"]],
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node": {
|
||||
"certName": "string",
|
||||
|
@ -5979,10 +5981,10 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------------ | ------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `error` | any | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `healthy` | boolean | false | | |
|
||||
| `netcheck` | [netcheck.Report](#netcheckreport) | false | | |
|
||||
| `netcheck_err` | any | false | | |
|
||||
| `netcheck_err` | string | false | | |
|
||||
| `netcheck_logs` | array of string | false | | |
|
||||
| `regions` | object | false | | |
|
||||
| » `[any property]` | [healthcheck.DERPRegionReport](#healthcheckderpregionreport) | false | | |
|
||||
|
@ -6009,7 +6011,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
```json
|
||||
{
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"latency": 0,
|
||||
"reachable": true
|
||||
|
@ -6020,7 +6022,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ----------- | ------- | -------- | ------------ | ----------- |
|
||||
| `error` | any | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `healthy` | boolean | false | | |
|
||||
| `latency` | integer | false | | |
|
||||
| `reachable` | boolean | false | | |
|
||||
|
@ -6030,20 +6032,22 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
```json
|
||||
{
|
||||
"access_url": {
|
||||
"error": null,
|
||||
"access_url": "string",
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"healthz_response": "string",
|
||||
"reachable": true,
|
||||
"status_code": 0
|
||||
},
|
||||
"coder_version": "string",
|
||||
"database": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"latency": 0,
|
||||
"reachable": true
|
||||
},
|
||||
"derp": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"netcheck": {
|
||||
"captivePortal": "string",
|
||||
|
@ -6075,18 +6079,18 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
"udp": true,
|
||||
"upnP": "string"
|
||||
},
|
||||
"netcheck_err": null,
|
||||
"netcheck_err": "string",
|
||||
"netcheck_logs": ["string"],
|
||||
"regions": {
|
||||
"property1": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node_reports": [
|
||||
{
|
||||
"can_exchange_messages": true,
|
||||
"client_errs": [[null]],
|
||||
"client_errs": [["string"]],
|
||||
"client_logs": [["string"]],
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node": {
|
||||
"certName": "string",
|
||||
|
@ -6140,14 +6144,14 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
}
|
||||
},
|
||||
"property2": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node_reports": [
|
||||
{
|
||||
"can_exchange_messages": true,
|
||||
"client_errs": [[null]],
|
||||
"client_errs": [["string"]],
|
||||
"client_logs": [["string"]],
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"node": {
|
||||
"certName": "string",
|
||||
|
@ -6206,7 +6210,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
"healthy": true,
|
||||
"time": "string",
|
||||
"websocket": {
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"response": {
|
||||
"body": "string",
|
||||
|
@ -6218,21 +6222,22 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------------ | ---------------------------------------------------------- | -------- | ------------ | ------------------------------------------------ |
|
||||
| `access_url` | [healthcheck.AccessURLReport](#healthcheckaccessurlreport) | false | | |
|
||||
| `database` | [healthcheck.DatabaseReport](#healthcheckdatabasereport) | false | | |
|
||||
| `derp` | [healthcheck.DERPReport](#healthcheckderpreport) | false | | |
|
||||
| `failing_sections` | array of string | false | | |
|
||||
| `healthy` | boolean | false | | Healthy is true if the report returns no errors. |
|
||||
| `time` | string | false | | Time is the time the report was generated at. |
|
||||
| `websocket` | [healthcheck.WebsocketReport](#healthcheckwebsocketreport) | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------------ | ---------------------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------- |
|
||||
| `access_url` | [healthcheck.AccessURLReport](#healthcheckaccessurlreport) | false | | |
|
||||
| `coder_version` | string | false | | The Coder version of the server that the report was generated on. |
|
||||
| `database` | [healthcheck.DatabaseReport](#healthcheckdatabasereport) | false | | |
|
||||
| `derp` | [healthcheck.DERPReport](#healthcheckderpreport) | false | | |
|
||||
| `failing_sections` | array of string | false | | Failing sections is a list of sections that have failed their healthcheck. |
|
||||
| `healthy` | boolean | false | | Healthy is true if the report returns no errors. |
|
||||
| `time` | string | false | | Time is the time the report was generated at. |
|
||||
| `websocket` | [healthcheck.WebsocketReport](#healthcheckwebsocketreport) | false | | |
|
||||
|
||||
## healthcheck.WebsocketReport
|
||||
|
||||
```json
|
||||
{
|
||||
"error": null,
|
||||
"error": "string",
|
||||
"healthy": true,
|
||||
"response": {
|
||||
"body": "string",
|
||||
|
@ -6245,7 +6250,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ---------- | -------------------------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `error` | any | false | | |
|
||||
| `error` | string | false | | |
|
||||
| `healthy` | boolean | false | | |
|
||||
| `response` | [healthcheck.WebsocketResponse](#healthcheckwebsocketresponse) | false | | |
|
||||
|
||||
|
|
Loading…
Reference in New Issue