From 01cbf0690591bf8ee077e7c6b36318970b530818 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 15 Nov 2023 15:00:38 +0100 Subject: [PATCH 01/11] Add warnings property --- coderd/healthcheck/accessurl.go | 4 +++- coderd/healthcheck/database.go | 4 +++- coderd/healthcheck/derphealth/derp.go | 34 ++++++++++++++++++++------- coderd/healthcheck/websocket.go | 10 ++++---- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/coderd/healthcheck/accessurl.go b/coderd/healthcheck/accessurl.go index 6f86944b7ca4e..10d2d936fb712 100644 --- a/coderd/healthcheck/accessurl.go +++ b/coderd/healthcheck/accessurl.go @@ -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"` diff --git a/coderd/healthcheck/database.go b/coderd/healthcheck/database.go index 9ee92d7a71a9f..9a8726a484361 100644 --- a/coderd/healthcheck/database.go +++ b/coderd/healthcheck/database.go @@ -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"` diff --git a/coderd/healthcheck/derphealth/derp.go b/coderd/healthcheck/derphealth/derp.go index 2570b9fcb10f0..94cc89307d565 100644 --- a/coderd/healthcheck/derphealth/derp.go +++ b/coderd/healthcheck/derphealth/derp.go @@ -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) { diff --git a/coderd/healthcheck/websocket.go b/coderd/healthcheck/websocket.go index 0b4a56e2d5ca9..5723cf48bc9a9 100644 --- a/coderd/healthcheck/websocket.go +++ b/coderd/healthcheck/websocket.go @@ -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) { From 6c84dc83c78e129969ce66e6de3d71b02d971de4 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 15 Nov 2023 15:02:29 +0100 Subject: [PATCH 02/11] make gen --- coderd/apidoc/docs.go | 36 +++++++++++ coderd/apidoc/swagger.json | 36 +++++++++++ docs/api/debug.md | 24 +++++--- docs/api/schemas.md | 107 ++++++++++++++++++++------------- site/src/api/typesGenerated.ts | 8 ++- 5 files changed, 161 insertions(+), 50 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 34e8f928e5daf..73aaefb04fe69 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -12107,6 +12107,12 @@ const docTemplate = `{ }, "uses_websocket": { "type": "boolean" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -12127,6 +12133,12 @@ const docTemplate = `{ }, "region": { "$ref": "#/definitions/tailcfg.DERPRegion" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -12156,6 +12168,12 @@ const docTemplate = `{ "additionalProperties": { "$ref": "#/definitions/derphealth.RegionReport" } + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -12193,6 +12211,12 @@ const docTemplate = `{ }, "status_code": { "type": "integer" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -12216,6 +12240,12 @@ const docTemplate = `{ }, "threshold_ms": { "type": "integer" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -12269,6 +12299,12 @@ const docTemplate = `{ }, "healthy": { "type": "boolean" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 7b82ad33bc58d..b06c2b9aa5bea 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -11028,6 +11028,12 @@ }, "uses_websocket": { "type": "boolean" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -11048,6 +11054,12 @@ }, "region": { "$ref": "#/definitions/tailcfg.DERPRegion" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -11077,6 +11089,12 @@ "additionalProperties": { "$ref": "#/definitions/derphealth.RegionReport" } + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -11114,6 +11132,12 @@ }, "status_code": { "type": "integer" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -11137,6 +11161,12 @@ }, "threshold_ms": { "type": "integer" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -11190,6 +11220,12 @@ }, "healthy": { "type": "boolean" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/docs/api/debug.md b/docs/api/debug.md index e5d0c0cd505de..7f87a99fcc5e9 100644 --- a/docs/api/debug.md +++ b/docs/api/debug.md @@ -45,7 +45,8 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "healthy": true, "healthz_response": "string", "reachable": true, - "status_code": 0 + "status_code": 0, + "warnings": ["string"] }, "coder_version": "string", "database": { @@ -54,7 +55,8 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "latency": "string", "latency_ms": 0, "reachable": true, - "threshold_ms": 0 + "threshold_ms": 0, + "warnings": ["string"] }, "derp": { "error": "string", @@ -128,7 +130,8 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "enabled": true, "error": "string" }, - "uses_websocket": true + "uses_websocket": true, + "warnings": ["string"] } ], "region": { @@ -154,7 +157,8 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "regionCode": "string", "regionID": 0, "regionName": "string" - } + }, + "warnings": ["string"] }, "property2": { "error": "string", @@ -192,7 +196,8 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "enabled": true, "error": "string" }, - "uses_websocket": true + "uses_websocket": true, + "warnings": ["string"] } ], "region": { @@ -218,9 +223,11 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "regionCode": "string", "regionID": 0, "regionName": "string" - } + }, + "warnings": ["string"] } - } + }, + "warnings": ["string"] }, "failing_sections": ["string"], "healthy": true, @@ -229,7 +236,8 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "body": "string", "code": 0, "error": "string", - "healthy": true + "healthy": true, + "warnings": ["string"] } } ``` diff --git a/docs/api/schemas.md b/docs/api/schemas.md index d3a61585d096c..f195d7f57a330 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -7138,7 +7138,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "enabled": true, "error": "string" }, - "uses_websocket": true + "uses_websocket": true, + "warnings": ["string"] } ``` @@ -7157,6 +7158,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `round_trip_ping_ms` | integer | false | | | | `stun` | [derphealth.StunReport](#derphealthstunreport) | false | | | | `uses_websocket` | boolean | false | | | +| `warnings` | array of string | false | | | ## derphealth.RegionReport @@ -7197,7 +7199,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "enabled": true, "error": "string" }, - "uses_websocket": true + "uses_websocket": true, + "warnings": ["string"] } ], "region": { @@ -7223,7 +7226,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "regionCode": "string", "regionID": 0, "regionName": "string" - } + }, + "warnings": ["string"] } ``` @@ -7235,6 +7239,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `healthy` | boolean | false | | | | `node_reports` | array of [derphealth.NodeReport](#derphealthnodereport) | false | | | | `region` | [tailcfg.DERPRegion](#tailcfgderpregion) | false | | | +| `warnings` | array of string | false | | | ## derphealth.Report @@ -7311,7 +7316,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "enabled": true, "error": "string" }, - "uses_websocket": true + "uses_websocket": true, + "warnings": ["string"] } ], "region": { @@ -7337,7 +7343,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "regionCode": "string", "regionID": 0, "regionName": "string" - } + }, + "warnings": ["string"] }, "property2": { "error": "string", @@ -7375,7 +7382,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "enabled": true, "error": "string" }, - "uses_websocket": true + "uses_websocket": true, + "warnings": ["string"] } ], "region": { @@ -7401,9 +7409,11 @@ If the schedule is empty, the user will be updated to use the default schedule.| "regionCode": "string", "regionID": 0, "regionName": "string" - } + }, + "warnings": ["string"] } - } + }, + "warnings": ["string"] } ``` @@ -7418,6 +7428,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `netcheck_logs` | array of string | false | | | | `regions` | object | false | | | | ยป `[any property]` | [derphealth.RegionReport](#derphealthregionreport) | false | | | +| `warnings` | array of string | false | | | ## derphealth.StunReport @@ -7446,20 +7457,22 @@ If the schedule is empty, the user will be updated to use the default schedule.| "healthy": true, "healthz_response": "string", "reachable": true, - "status_code": 0 + "status_code": 0, + "warnings": ["string"] } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------------ | ------- | -------- | ------------ | ----------- | -| `access_url` | string | false | | | -| `error` | string | false | | | -| `healthy` | boolean | false | | | -| `healthz_response` | string | false | | | -| `reachable` | boolean | false | | | -| `status_code` | integer | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------ | --------------- | -------- | ------------ | ----------- | +| `access_url` | string | false | | | +| `error` | string | false | | | +| `healthy` | boolean | false | | | +| `healthz_response` | string | false | | | +| `reachable` | boolean | false | | | +| `status_code` | integer | false | | | +| `warnings` | array of string | false | | | ## healthcheck.DatabaseReport @@ -7470,20 +7483,22 @@ If the schedule is empty, the user will be updated to use the default schedule.| "latency": "string", "latency_ms": 0, "reachable": true, - "threshold_ms": 0 + "threshold_ms": 0, + "warnings": ["string"] } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| -------------- | ------- | -------- | ------------ | ----------- | -| `error` | string | false | | | -| `healthy` | boolean | false | | | -| `latency` | string | false | | | -| `latency_ms` | integer | false | | | -| `reachable` | boolean | false | | | -| `threshold_ms` | integer | false | | | +| Name | Type | Required | Restrictions | Description | +| -------------- | --------------- | -------- | ------------ | ----------- | +| `error` | string | false | | | +| `healthy` | boolean | false | | | +| `latency` | string | false | | | +| `latency_ms` | integer | false | | | +| `reachable` | boolean | false | | | +| `threshold_ms` | integer | false | | | +| `warnings` | array of string | false | | | ## healthcheck.Report @@ -7495,7 +7510,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "healthy": true, "healthz_response": "string", "reachable": true, - "status_code": 0 + "status_code": 0, + "warnings": ["string"] }, "coder_version": "string", "database": { @@ -7504,7 +7520,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "latency": "string", "latency_ms": 0, "reachable": true, - "threshold_ms": 0 + "threshold_ms": 0, + "warnings": ["string"] }, "derp": { "error": "string", @@ -7578,7 +7595,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "enabled": true, "error": "string" }, - "uses_websocket": true + "uses_websocket": true, + "warnings": ["string"] } ], "region": { @@ -7604,7 +7622,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "regionCode": "string", "regionID": 0, "regionName": "string" - } + }, + "warnings": ["string"] }, "property2": { "error": "string", @@ -7642,7 +7661,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "enabled": true, "error": "string" }, - "uses_websocket": true + "uses_websocket": true, + "warnings": ["string"] } ], "region": { @@ -7668,9 +7688,11 @@ If the schedule is empty, the user will be updated to use the default schedule.| "regionCode": "string", "regionID": 0, "regionName": "string" - } + }, + "warnings": ["string"] } - } + }, + "warnings": ["string"] }, "failing_sections": ["string"], "healthy": true, @@ -7679,7 +7701,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "body": "string", "code": 0, "error": "string", - "healthy": true + "healthy": true, + "warnings": ["string"] } } ``` @@ -7704,18 +7727,20 @@ If the schedule is empty, the user will be updated to use the default schedule.| "body": "string", "code": 0, "error": "string", - "healthy": true + "healthy": true, + "warnings": ["string"] } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| --------- | ------- | -------- | ------------ | ----------- | -| `body` | string | false | | | -| `code` | integer | false | | | -| `error` | string | false | | | -| `healthy` | boolean | false | | | +| Name | Type | Required | Restrictions | Description | +| ---------- | --------------- | -------- | ------------ | ----------- | +| `body` | string | false | | | +| `code` | integer | false | | | +| `error` | string | false | | | +| `healthy` | boolean | false | | | +| `warnings` | array of string | false | | | ## netcheck.Report diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 032c3854138dd..93c3dbdddab57 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2081,8 +2081,9 @@ export type RegionTypes = Region | WorkspaceProxy; // From healthcheck/accessurl.go export interface HealthcheckAccessURLReport { - readonly access_url: string; readonly healthy: boolean; + readonly warnings: string[]; + readonly access_url: string; readonly reachable: boolean; readonly status_code: number; readonly healthz_response: string; @@ -2092,6 +2093,7 @@ export interface HealthcheckAccessURLReport { // From healthcheck/database.go export interface HealthcheckDatabaseReport { readonly healthy: boolean; + readonly warnings: string[]; readonly reachable: boolean; readonly latency: string; readonly latency_ms: number; @@ -2114,6 +2116,7 @@ export interface HealthcheckReport { // From healthcheck/websocket.go export interface HealthcheckWebsocketReport { readonly healthy: boolean; + readonly warnings: string[]; readonly body: string; readonly code: number; readonly error?: string; @@ -2169,6 +2172,7 @@ export const ClibaseValueSources: ClibaseValueSource[] = [ // From derphealth/derp.go export interface DerphealthNodeReport { readonly healthy: boolean; + readonly warnings: string[]; // Named type "tailscale.com/tailcfg.DERPNode" unknown, using "any" // eslint-disable-next-line @typescript-eslint/no-explicit-any -- External type readonly node?: any; @@ -2188,6 +2192,7 @@ export interface DerphealthNodeReport { // From derphealth/derp.go export interface DerphealthRegionReport { readonly healthy: boolean; + readonly warnings: string[]; // Named type "tailscale.com/tailcfg.DERPRegion" unknown, using "any" // eslint-disable-next-line @typescript-eslint/no-explicit-any -- External type readonly region?: any; @@ -2198,6 +2203,7 @@ export interface DerphealthRegionReport { // From derphealth/derp.go export interface DerphealthReport { readonly healthy: boolean; + readonly warnings: string[]; readonly regions: Record; // Named type "tailscale.com/net/netcheck.Report" unknown, using "any" // eslint-disable-next-line @typescript-eslint/no-explicit-any -- External type From b66e38e4de311cffa509826b78c7190cc342897b Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 15 Nov 2023 15:04:01 +0100 Subject: [PATCH 03/11] test derp --- coderd/healthcheck/derphealth/derp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/healthcheck/derphealth/derp_test.go b/coderd/healthcheck/derphealth/derp_test.go index 5b72007150ba4..80c7e8f72b321 100644 --- a/coderd/healthcheck/derphealth/derp_test.go +++ b/coderd/healthcheck/derphealth/derp_test.go @@ -174,7 +174,7 @@ func TestDERP(t *testing.T) { for _, region := range report.Regions { assert.False(t, region.Healthy) for _, node := range region.NodeReports { - assert.False(t, node.Healthy) + assert.True(t, node.Healthy) assert.True(t, node.CanExchangeMessages) assert.NotEmpty(t, node.RoundTripPing) assert.Len(t, node.ClientLogs, 2) From cec2e6d6e1509db44b9c231a967bd5bf69a24c37 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 15 Nov 2023 15:08:05 +0100 Subject: [PATCH 04/11] another test --- coderd/healthcheck/healthcheck_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/coderd/healthcheck/healthcheck_test.go b/coderd/healthcheck/healthcheck_test.go index f89f12116dc88..1a1840a37d581 100644 --- a/coderd/healthcheck/healthcheck_test.go +++ b/coderd/healthcheck/healthcheck_test.go @@ -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) From f1d7643be05c8973d23391ae39ab58b507676158 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 15 Nov 2023 15:22:23 +0100 Subject: [PATCH 05/11] Warnings --- site/src/testHelpers/entities.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index ad822e76e273d..42e211e268c90 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2404,6 +2404,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { node_reports: [ { healthy: true, + warnings: [], node: { Name: "999stun0", RegionID: 999, @@ -2428,6 +2429,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { }, { healthy: true, + warnings: [], node: { Name: "999b", RegionID: 999, @@ -2461,6 +2463,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { }, "10007": { healthy: true, + warnings: [], region: { EmbeddedRelay: false, RegionID: 10007, @@ -2486,6 +2489,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { node_reports: [ { healthy: true, + warnings: [], node: { Name: "10007stun0", RegionID: 10007, @@ -2510,6 +2514,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { }, { healthy: true, + warnings: [], node: { Name: "10007a", RegionID: 10007, @@ -2568,6 +2573,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { node_reports: [ { healthy: true, + warnings: [], node: { Name: "10008stun0", RegionID: 10008, @@ -2592,6 +2598,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { }, { healthy: true, + warnings: [], node: { Name: "10008a", RegionID: 10008, @@ -2625,6 +2632,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { }, "10009": { healthy: true, + warnings: [], region: { EmbeddedRelay: false, RegionID: 10009, @@ -2650,6 +2658,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { node_reports: [ { healthy: true, + warnings: [], node: { Name: "10009stun0", RegionID: 10009, @@ -2674,6 +2683,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { }, { healthy: true, + warnings: [], node: { Name: "10009a", RegionID: 10009, @@ -2751,19 +2761,22 @@ export const MockHealth: TypesGen.HealthcheckReport = { ], }, access_url: { - access_url: "https://dev.coder.com", healthy: true, + warnings: [], + access_url: "https://dev.coder.com", reachable: true, status_code: 200, healthz_response: "OK", }, websocket: { healthy: true, + warnings: [], body: "", code: 101, }, database: { healthy: true, + warnings: [], reachable: true, latency: "92570", latency_ms: 92570, @@ -2788,6 +2801,7 @@ export const DeploymentHealthUnhealthy: TypesGen.HealthcheckReport = { coder_version: "v2.3.0-devel+8cca4915a", access_url: { healthy: true, + warnings: [], access_url: "", healthz_response: "", reachable: true, @@ -2795,6 +2809,7 @@ export const DeploymentHealthUnhealthy: TypesGen.HealthcheckReport = { }, database: { healthy: false, + warnings: [], latency: "", latency_ms: 0, reachable: true, @@ -2802,11 +2817,13 @@ export const DeploymentHealthUnhealthy: TypesGen.HealthcheckReport = { }, derp: { healthy: false, + warnings: [], regions: [], netcheck_logs: [], }, websocket: { healthy: false, + warnings: [], body: "", code: 0, }, From 98bd84c7ebaeecea51c99ec4b2304d6d1b20d8cf Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 17 Nov 2023 14:24:21 +0100 Subject: [PATCH 06/11] make fmt --- site/src/testHelpers/entities.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 42e211e268c90..19afb23e8a20b 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2379,6 +2379,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { regions: { "999": { healthy: true, + warnings: [], region: { EmbeddedRelay: true, RegionID: 999, @@ -2548,6 +2549,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { }, "10008": { healthy: true, + warnings: [], region: { EmbeddedRelay: false, RegionID: 10008, From 5bc136693c851edd2350ea1c4d40d48832dee53e Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 17 Nov 2023 14:29:29 +0100 Subject: [PATCH 07/11] one more --- site/src/testHelpers/entities.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 19afb23e8a20b..8b8b475dd017c 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2376,6 +2376,7 @@ export const MockHealth: TypesGen.HealthcheckReport = { failing_sections: [], derp: { healthy: true, + warnings: [], regions: { "999": { healthy: true, From 1fb7d5636049b2abf5a60de7ec659e536c2342f3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 17 Nov 2023 14:43:51 +0100 Subject: [PATCH 08/11] TS --- .../pages/HealthPage/HealthPage.stories.tsx | 12 ++++++ site/src/pages/HealthPage/HealthPage.tsx | 39 +++++++++++++------ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/site/src/pages/HealthPage/HealthPage.stories.tsx b/site/src/pages/HealthPage/HealthPage.stories.tsx index f0a88cc5b4518..92887fb9e3a94 100644 --- a/site/src/pages/HealthPage/HealthPage.stories.tsx +++ b/site/src/pages/HealthPage/HealthPage.stories.tsx @@ -31,3 +31,15 @@ export const UnhealthyDERP: Story = { }, }, }; + +export const DERPWarnings: Story = { + args: { + healthStatus: { + ...MockHealth, + derp: { + ...MockHealth.derp, + warnings: ["foobar"], + }, + }, + }, +}; diff --git a/site/src/pages/HealthPage/HealthPage.tsx b/site/src/pages/HealthPage/HealthPage.tsx index 70ddd62185a72..c1692f7dd572f 100644 --- a/site/src/pages/HealthPage/HealthPage.tsx +++ b/site/src/pages/HealthPage/HealthPage.tsx @@ -85,7 +85,13 @@ export function HealthPageView({ {healthStatus.healthy - ? "All systems operational" + ? Object.keys(sections).some( + (key) => + healthStatus[key as keyof typeof sections]?.warnings + .length > 0, + ) + ? "All systems operational, but performance might be degraded" + : "All systems operational" : "Some issues have been detected"} @@ -137,9 +143,10 @@ export function HealthPageView({ .map((key) => { const label = sections[key as keyof typeof sections]; const isActive = tab.value === key; - const isHealthy = - healthStatus[key as keyof typeof sections].healthy; - + const healthSection = + healthStatus[key as keyof typeof sections]; + const isHealthy = healthSection.healthy; + const isWarning = healthSection.warnings.length > 0; return ( {isHealthy ? ( - theme.palette.success.light, - }} - /> + isWarning ? ( + theme.palette.warning.main, + }} + /> + ) : ( + theme.palette.success.light, + }} + /> + ) ) : ( Date: Fri, 17 Nov 2023 14:48:45 +0100 Subject: [PATCH 09/11] test fix --- coderd/healthcheck/derphealth/derp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/healthcheck/derphealth/derp_test.go b/coderd/healthcheck/derphealth/derp_test.go index 80c7e8f72b321..41a6f68c3e1f4 100644 --- a/coderd/healthcheck/derphealth/derp_test.go +++ b/coderd/healthcheck/derphealth/derp_test.go @@ -172,7 +172,7 @@ func TestDERP(t *testing.T) { assert.False(t, report.Healthy) for _, region := range report.Regions { - assert.False(t, region.Healthy) + assert.True(t, region.Healthy) for _, node := range region.NodeReports { assert.True(t, node.Healthy) assert.True(t, node.CanExchangeMessages) From e98b9fcd6b443b744cad4227e03d412ec743ba98 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 17 Nov 2023 14:54:31 +0100 Subject: [PATCH 10/11] one more --- coderd/healthcheck/derphealth/derp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/healthcheck/derphealth/derp_test.go b/coderd/healthcheck/derphealth/derp_test.go index 41a6f68c3e1f4..9694d8248c50b 100644 --- a/coderd/healthcheck/derphealth/derp_test.go +++ b/coderd/healthcheck/derphealth/derp_test.go @@ -170,7 +170,7 @@ func TestDERP(t *testing.T) { report.Run(ctx, opts) - assert.False(t, report.Healthy) + assert.True(t, report.Healthy) for _, region := range report.Regions { assert.True(t, region.Healthy) for _, node := range region.NodeReports { From 1671f5eed685298222d56578559a395a4f33597a Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 17 Nov 2023 15:04:45 +0100 Subject: [PATCH 11/11] Better unit --- coderd/healthcheck/derphealth/derp_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coderd/healthcheck/derphealth/derp_test.go b/coderd/healthcheck/derphealth/derp_test.go index 9694d8248c50b..c9ed3d591557e 100644 --- a/coderd/healthcheck/derphealth/derp_test.go +++ b/coderd/healthcheck/derphealth/derp_test.go @@ -171,10 +171,13 @@ func TestDERP(t *testing.T) { report.Run(ctx, opts) assert.True(t, report.Healthy) + assert.NotEmpty(t, report.Warnings) for _, region := range report.Regions { assert.True(t, region.Healthy) + assert.NotEmpty(t, region.Warnings) for _, node := range region.NodeReports { 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)