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