fix(site): add workspace proxy section to health page (#10862)

* Adds workspace proxy section to health page
* Conditionally places workspace proxy warnings in errors or warnings based on calculated severity
* Adds some more stories we were missing for HealthPage
This commit is contained in:
Cian Johnston 2023-11-27 09:26:02 +00:00 committed by GitHub
parent 6c67add2d9
commit b73397e08c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 170 additions and 14 deletions

View File

@ -238,6 +238,34 @@ func TestHealthcheck(t *testing.T) {
severity: health.SeverityError,
healthy: false,
failingSections: []string{healthcheck.SectionWorkspaceProxy},
}, {
name: "ProxyWarn",
checker: &testChecker{
DERPReport: derphealth.Report{
Healthy: true,
Severity: health.SeverityOK,
},
AccessURLReport: healthcheck.AccessURLReport{
Healthy: true,
Severity: health.SeverityOK,
},
WebsocketReport: healthcheck.WebsocketReport{
Healthy: true,
Severity: health.SeverityOK,
},
DatabaseReport: healthcheck.DatabaseReport{
Healthy: true,
Severity: health.SeverityOK,
},
WorkspaceProxyReport: healthcheck.WorkspaceProxyReport{
Healthy: true,
Warnings: []string{"foobar"},
Severity: health.SeverityWarning,
},
},
severity: health.SeverityWarning,
healthy: true,
failingSections: []string{},
}, {
name: "AllFail",
healthy: false,

View File

@ -2,8 +2,9 @@ package healthcheck
import (
"context"
"errors"
"fmt"
"sort"
"strings"
"golang.org/x/xerrors"
@ -78,6 +79,7 @@ func (r *WorkspaceProxyReport) Run(ctx context.Context, opts *WorkspaceProxyRepo
})
var total, healthy int
var errs []string
for _, proxy := range r.WorkspaceProxies.Regions {
total++
if proxy.Healthy {
@ -86,34 +88,40 @@ func (r *WorkspaceProxyReport) Run(ctx context.Context, opts *WorkspaceProxyRepo
if len(proxy.Status.Report.Errors) > 0 {
for _, err := range proxy.Status.Report.Errors {
r.appendError(xerrors.New(err))
errs = append(errs, fmt.Sprintf("%s: %s", proxy.Name, err))
}
}
}
r.Severity = calculateSeverity(total, healthy)
r.Healthy = r.Severity.Value() < health.SeverityError.Value()
switch r.Severity {
case health.SeverityWarning, health.SeverityOK:
r.Warnings = append(r.Warnings, errs...)
case health.SeverityError:
r.appendError(errs...)
}
// Versions _must_ match. Perform this check last. This will clobber any other severity.
for _, proxy := range r.WorkspaceProxies.Regions {
if vErr := checkVersion(proxy, opts.CurrentVersion); vErr != nil {
r.Healthy = false
r.Severity = health.SeverityError
r.appendError(vErr)
r.appendError(fmt.Sprintf("%s: %s", proxy.Name, vErr.Error()))
}
}
}
// appendError appends errs onto r.Error.
// We only have one error, so multiple errors need to be squashed in there.
func (r *WorkspaceProxyReport) appendError(errs ...error) {
if len(errs) == 0 {
func (r *WorkspaceProxyReport) appendError(es ...string) {
if len(es) == 0 {
return
}
if r.Error != nil {
errs = append([]error{xerrors.New(*r.Error)}, errs...)
es = append([]string{*r.Error}, es...)
}
r.Error = ptr.Ref(errors.Join(errs...).Error())
r.Error = ptr.Ref(strings.Join(es, "\n"))
}
func checkVersion(proxy codersdk.WorkspaceProxy, currentVersion string) error {

View File

@ -9,8 +9,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
)
func Test_WorkspaceProxyReport_appendErrors(t *testing.T) {
@ -20,7 +18,7 @@ func Test_WorkspaceProxyReport_appendErrors(t *testing.T) {
name string
expected string
prevErr string
errs []error
errs []string
}{
{
name: "nil",
@ -29,24 +27,24 @@ func Test_WorkspaceProxyReport_appendErrors(t *testing.T) {
{
name: "one error",
expected: assert.AnError.Error(),
errs: []error{assert.AnError},
errs: []string{assert.AnError.Error()},
},
{
name: "one error, one prev",
prevErr: "previous error",
expected: "previous error\n" + assert.AnError.Error(),
errs: []error{assert.AnError},
errs: []string{assert.AnError.Error()},
},
{
name: "two errors",
expected: assert.AnError.Error() + "\nanother error",
errs: []error{assert.AnError, xerrors.Errorf("another error")},
errs: []string{assert.AnError.Error(), "another error"},
},
{
name: "two errors, one prev",
prevErr: "previous error",
expected: "previous error\n" + assert.AnError.Error() + "\nanother error",
errs: []error{assert.AnError, xerrors.Errorf("another error")},
errs: []string{assert.AnError.Error(), "another error"},
},
} {
tt := tt

View File

@ -19,14 +19,106 @@ type Story = StoryObj<typeof HealthPageView>;
export const Example: Story = {};
export const AccessURLUnhealthy: Story = {
args: {
healthStatus: {
...MockHealth,
healthy: false,
severity: "error",
access_url: {
...MockHealth.access_url,
healthy: false,
error: "ouch",
},
},
},
};
export const AccessURLWarning: Story = {
args: {
healthStatus: {
...MockHealth,
healthy: true,
severity: "warning",
access_url: {
...MockHealth.access_url,
healthy: true,
warnings: ["foobar"],
},
},
},
};
export const DatabaseUnhealthy: Story = {
args: {
healthStatus: {
...MockHealth,
healthy: false,
severity: "error",
database: {
...MockHealth.database,
healthy: false,
error: "ouch",
},
},
},
};
export const DatabaseWarning: Story = {
args: {
healthStatus: {
...MockHealth,
healthy: true,
severity: "warning",
database: {
...MockHealth.database,
healthy: true,
warnings: ["foobar"],
},
},
},
};
export const WebsocketUnhealthy: Story = {
args: {
healthStatus: {
...MockHealth,
healthy: false,
severity: "error",
websocket: {
...MockHealth.websocket,
healthy: false,
error: "ouch",
},
},
},
};
export const WebsocketWarning: Story = {
args: {
healthStatus: {
...MockHealth,
healthy: true,
severity: "warning",
websocket: {
...MockHealth.websocket,
healthy: true,
warnings: ["foobar"],
},
},
},
};
export const UnhealthyDERP: Story = {
args: {
healthStatus: {
...MockHealth,
healthy: false,
severity: "error",
derp: {
...MockHealth.derp,
healthy: false,
error: "ouch",
},
},
},
@ -36,6 +128,7 @@ export const DERPWarnings: Story = {
args: {
healthStatus: {
...MockHealth,
severity: "warning",
derp: {
...MockHealth.derp,
warnings: ["foobar"],
@ -43,3 +136,31 @@ export const DERPWarnings: Story = {
},
},
};
export const ProxyUnhealthy: Story = {
args: {
healthStatus: {
...MockHealth,
severity: "error",
healthy: false,
workspace_proxy: {
...MockHealth.workspace_proxy,
healthy: false,
error: "ouch",
},
},
},
};
export const ProxyWarning: Story = {
args: {
healthStatus: {
...MockHealth,
severity: "warning",
workspace_proxy: {
...MockHealth.workspace_proxy,
warnings: ["foobar"],
},
},
},
};

View File

@ -29,6 +29,7 @@ const sections = {
access_url: "Access URL",
websocket: "Websocket",
database: "Database",
workspace_proxy: "Workspace Proxy",
} as const;
export default function HealthPage() {