mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
feat: do not fail DERP healthcheck if WebSocket is used (#10714)
This commit is contained in:
36
coderd/apidoc/docs.go
generated
36
coderd/apidoc/docs.go
generated
@ -12115,6 +12115,12 @@ const docTemplate = `{
|
||||
},
|
||||
"uses_websocket": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12135,6 +12141,12 @@ const docTemplate = `{
|
||||
},
|
||||
"region": {
|
||||
"$ref": "#/definitions/tailcfg.DERPRegion"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12164,6 +12176,12 @@ const docTemplate = `{
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/derphealth.RegionReport"
|
||||
}
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12201,6 +12219,12 @@ const docTemplate = `{
|
||||
},
|
||||
"status_code": {
|
||||
"type": "integer"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12224,6 +12248,12 @@ const docTemplate = `{
|
||||
},
|
||||
"threshold_ms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12277,6 +12307,12 @@ const docTemplate = `{
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
36
coderd/apidoc/swagger.json
generated
36
coderd/apidoc/swagger.json
generated
@ -11036,6 +11036,12 @@
|
||||
},
|
||||
"uses_websocket": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11056,6 +11062,12 @@
|
||||
},
|
||||
"region": {
|
||||
"$ref": "#/definitions/tailcfg.DERPRegion"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11085,6 +11097,12 @@
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/derphealth.RegionReport"
|
||||
}
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11122,6 +11140,12 @@
|
||||
},
|
||||
"status_code": {
|
||||
"type": "integer"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11145,6 +11169,12 @@
|
||||
},
|
||||
"threshold_ms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11198,6 +11228,12 @@
|
||||
},
|
||||
"healthy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -14,8 +14,10 @@ import (
|
||||
|
||||
// @typescript-generate AccessURLReport
|
||||
type AccessURLReport struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
AccessURL string `json:"access_url"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Reachable bool `json:"reachable"`
|
||||
StatusCode int `json:"status_code"`
|
||||
HealthzResponse string `json:"healthz_response"`
|
||||
|
@ -16,7 +16,9 @@ const (
|
||||
|
||||
// @typescript-generate DatabaseReport
|
||||
type DatabaseReport struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
Reachable bool `json:"reachable"`
|
||||
Latency string `json:"latency"`
|
||||
LatencyMS int64 `json:"latency_ms"`
|
||||
|
@ -24,9 +24,14 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
)
|
||||
|
||||
const (
|
||||
warningNodeUsesWebsocket = `Node uses WebSockets because the "Upgrade: DERP" header may be blocked on the load balancer.`
|
||||
)
|
||||
|
||||
// @typescript-generate Report
|
||||
type Report struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
Regions map[int]*RegionReport `json:"regions"`
|
||||
|
||||
@ -39,8 +44,9 @@ type Report struct {
|
||||
|
||||
// @typescript-generate RegionReport
|
||||
type RegionReport struct {
|
||||
mu sync.Mutex
|
||||
Healthy bool `json:"healthy"`
|
||||
mu sync.Mutex
|
||||
Healthy bool `json:"healthy"`
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
Region *tailcfg.DERPRegion `json:"region"`
|
||||
NodeReports []*NodeReport `json:"node_reports"`
|
||||
@ -52,8 +58,10 @@ type NodeReport struct {
|
||||
mu sync.Mutex
|
||||
clientCounter int
|
||||
|
||||
Healthy bool `json:"healthy"`
|
||||
Node *tailcfg.DERPNode `json:"node"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
Node *tailcfg.DERPNode `json:"node"`
|
||||
|
||||
ServerInfo derp.ServerInfoMessage `json:"node_info"`
|
||||
CanExchangeMessages bool `json:"can_exchange_messages"`
|
||||
@ -108,6 +116,10 @@ func (r *Report) Run(ctx context.Context, opts *ReportOptions) {
|
||||
if !regionReport.Healthy {
|
||||
r.Healthy = false
|
||||
}
|
||||
|
||||
for _, w := range regionReport.Warnings {
|
||||
r.Warnings = append(r.Warnings, fmt.Sprintf("[%s] %s", regionReport.Region.RegionName, w))
|
||||
}
|
||||
mu.Unlock()
|
||||
}()
|
||||
}
|
||||
@ -159,6 +171,10 @@ func (r *RegionReport) Run(ctx context.Context) {
|
||||
if !nodeReport.Healthy {
|
||||
r.Healthy = false
|
||||
}
|
||||
|
||||
for _, w := range nodeReport.Warnings {
|
||||
r.Warnings = append(r.Warnings, fmt.Sprintf("[%s] %s", nodeReport.Node.Name, w))
|
||||
}
|
||||
r.mu.Unlock()
|
||||
}()
|
||||
}
|
||||
@ -208,14 +224,14 @@ func (r *NodeReport) Run(ctx context.Context) {
|
||||
|
||||
// We can't exchange messages with the node,
|
||||
if (!r.CanExchangeMessages && !r.Node.STUNOnly) ||
|
||||
// A node may use websockets because `Upgrade: DERP` may be blocked on
|
||||
// the load balancer. This is unhealthy because websockets are slower
|
||||
// than the regular DERP protocol.
|
||||
r.UsesWebsocket ||
|
||||
// The node was marked as STUN compatible but the STUN test failed.
|
||||
r.STUN.Error != nil {
|
||||
r.Healthy = false
|
||||
}
|
||||
|
||||
if r.UsesWebsocket {
|
||||
r.Warnings = append(r.Warnings, warningNodeUsesWebsocket)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NodeReport) doExchangeMessage(ctx context.Context) {
|
||||
|
@ -170,11 +170,14 @@ func TestDERP(t *testing.T) {
|
||||
|
||||
report.Run(ctx, opts)
|
||||
|
||||
assert.False(t, report.Healthy)
|
||||
assert.True(t, report.Healthy)
|
||||
assert.NotEmpty(t, report.Warnings)
|
||||
for _, region := range report.Regions {
|
||||
assert.False(t, region.Healthy)
|
||||
assert.True(t, region.Healthy)
|
||||
assert.NotEmpty(t, region.Warnings)
|
||||
for _, node := range region.NodeReports {
|
||||
assert.False(t, node.Healthy)
|
||||
assert.True(t, node.Healthy)
|
||||
assert.NotEmpty(t, node.Warnings)
|
||||
assert.True(t, node.CanExchangeMessages)
|
||||
assert.NotEmpty(t, node.RoundTripPing)
|
||||
assert.Len(t, node.ClientLogs, 2)
|
||||
|
@ -77,6 +77,25 @@ func TestHealthcheck(t *testing.T) {
|
||||
},
|
||||
healthy: false,
|
||||
failingSections: []string{healthcheck.SectionDERP},
|
||||
}, {
|
||||
name: "DERPWarning",
|
||||
checker: &testChecker{
|
||||
DERPReport: derphealth.Report{
|
||||
Healthy: true,
|
||||
Warnings: []string{"foobar"},
|
||||
},
|
||||
AccessURLReport: healthcheck.AccessURLReport{
|
||||
Healthy: true,
|
||||
},
|
||||
WebsocketReport: healthcheck.WebsocketReport{
|
||||
Healthy: true,
|
||||
},
|
||||
DatabaseReport: healthcheck.DatabaseReport{
|
||||
Healthy: true,
|
||||
},
|
||||
},
|
||||
healthy: true,
|
||||
failingSections: nil,
|
||||
}, {
|
||||
name: "AccessURLFail",
|
||||
checker: &testChecker{
|
||||
@ -153,6 +172,7 @@ func TestHealthcheck(t *testing.T) {
|
||||
assert.Equal(t, c.healthy, report.Healthy)
|
||||
assert.Equal(t, c.failingSections, report.FailingSections)
|
||||
assert.Equal(t, c.checker.DERPReport.Healthy, report.DERP.Healthy)
|
||||
assert.Equal(t, c.checker.DERPReport.Warnings, report.DERP.Warnings)
|
||||
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)
|
||||
|
@ -21,10 +21,12 @@ type WebsocketReportOptions struct {
|
||||
|
||||
// @typescript-generate WebsocketReport
|
||||
type WebsocketReport struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Body string `json:"body"`
|
||||
Code int `json:"code"`
|
||||
Error *string `json:"error"`
|
||||
Healthy bool `json:"healthy"`
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
Body string `json:"body"`
|
||||
Code int `json:"code"`
|
||||
Error *string `json:"error"`
|
||||
}
|
||||
|
||||
func (r *WebsocketReport) Run(ctx context.Context, opts *WebsocketReportOptions) {
|
||||
|
Reference in New Issue
Block a user