fix: use `*string` instead of `error` in healthcheck response (#8234)

This commit is contained in:
Colin Adler 2023-06-27 14:13:54 -05:00 committed by GitHub
parent e2e07b01e9
commit b8a143566b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 195 additions and 114 deletions

40
coderd/apidoc/docs.go generated
View File

@ -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"
},

View File

@ -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"
},

View File

@ -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
}

View File

@ -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())
})
}

View File

@ -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)

View File

@ -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)
})
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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)
})
}
}

View File

@ -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
}
}

View File

@ -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)
})

View File

@ -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",

View File

@ -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 | | |