mirror of https://github.com/coder/coder.git
294 lines
9.2 KiB
Go
294 lines
9.2 KiB
Go
package healthcheck_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/coder/coder/v2/coderd/healthcheck"
|
|
"github.com/coder/coder/v2/coderd/healthcheck/health"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
)
|
|
|
|
func TestWorkspaceProxies(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, tt := range []struct {
|
|
name string
|
|
fetchWorkspaceProxies func(context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error)
|
|
updateProxyHealth func(context.Context) error
|
|
expectedHealthy bool
|
|
expectedError string
|
|
expectedWarningCode health.Code
|
|
expectedSeverity health.Severity
|
|
}{
|
|
{
|
|
name: "NotEnabled",
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityOK,
|
|
},
|
|
{
|
|
name: "Enabled/NoProxies",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityOK,
|
|
},
|
|
{
|
|
name: "Enabled/OneHealthy",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(fakeWorkspaceProxy("alpha", true)),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityOK,
|
|
},
|
|
{
|
|
name: "Enabled/OneUnhealthy",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(fakeWorkspaceProxy("alpha", false)),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: false,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedError: string(health.CodeProxyUnhealthy),
|
|
},
|
|
{
|
|
name: "Enabled/OneUnreachable",
|
|
fetchWorkspaceProxies: func(ctx context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error) {
|
|
return codersdk.RegionsResponse[codersdk.WorkspaceProxy]{
|
|
Regions: []codersdk.WorkspaceProxy{
|
|
{
|
|
Region: codersdk.Region{
|
|
Name: "gone",
|
|
Healthy: false,
|
|
},
|
|
Status: codersdk.WorkspaceProxyStatus{
|
|
Status: codersdk.ProxyUnreachable,
|
|
Report: codersdk.ProxyHealthReport{
|
|
Errors: []string{
|
|
"request to proxy failed: Get \"http://127.0.0.1:3001/healthz-report\": dial tcp 127.0.0.1:3001: connect: connection refused",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, nil
|
|
},
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: false,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedError: string(health.CodeProxyUnhealthy),
|
|
},
|
|
{
|
|
name: "Enabled/AllHealthy",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(
|
|
fakeWorkspaceProxy("alpha", true),
|
|
fakeWorkspaceProxy("beta", true),
|
|
),
|
|
updateProxyHealth: func(ctx context.Context) error {
|
|
return nil
|
|
},
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityOK,
|
|
},
|
|
{
|
|
name: "Enabled/OneHealthyOneUnhealthy",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(
|
|
fakeWorkspaceProxy("alpha", false),
|
|
fakeWorkspaceProxy("beta", true),
|
|
),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityWarning,
|
|
expectedWarningCode: health.CodeProxyUnhealthy,
|
|
},
|
|
{
|
|
name: "Enabled/AllUnhealthy",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(
|
|
fakeWorkspaceProxy("alpha", false),
|
|
fakeWorkspaceProxy("beta", false),
|
|
),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: false,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedError: string(health.CodeProxyUnhealthy),
|
|
},
|
|
{
|
|
name: "Enabled/NotConnectedYet",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(
|
|
fakeWorkspaceProxy("slowpoke", true),
|
|
),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityOK,
|
|
},
|
|
{
|
|
name: "Enabled/ErrFetchWorkspaceProxy",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxiesErr(assert.AnError),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: false,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedError: string(health.CodeProxyFetch),
|
|
},
|
|
{
|
|
name: "Enabled/ErrUpdateProxyHealth",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(fakeWorkspaceProxy("alpha", true)),
|
|
updateProxyHealth: fakeUpdateProxyHealth(assert.AnError),
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityWarning,
|
|
expectedWarningCode: health.CodeProxyUpdate,
|
|
},
|
|
{
|
|
name: "Enabled/OneUnhealthyAndDeleted",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(fakeWorkspaceProxy("alpha", false, func(wp *codersdk.WorkspaceProxy) {
|
|
wp.Deleted = true
|
|
})),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityOK,
|
|
},
|
|
{
|
|
name: "Enabled/ProxyWarnings",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(
|
|
fakeWorkspaceProxy("alpha", true, func(wp *codersdk.WorkspaceProxy) {
|
|
wp.Status.Report.Warnings = []string{"warning"}
|
|
}),
|
|
fakeWorkspaceProxy("beta", false),
|
|
),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: true,
|
|
expectedSeverity: health.SeverityWarning,
|
|
expectedWarningCode: health.CodeProxyUnhealthy,
|
|
},
|
|
{
|
|
name: "Enabled/ProxyWarningsButAllErrored",
|
|
fetchWorkspaceProxies: fakeFetchWorkspaceProxies(
|
|
fakeWorkspaceProxy("alpha", false),
|
|
fakeWorkspaceProxy("beta", false, func(wp *codersdk.WorkspaceProxy) {
|
|
wp.Status.Report.Warnings = []string{"warning"}
|
|
}),
|
|
),
|
|
updateProxyHealth: fakeUpdateProxyHealth(nil),
|
|
expectedHealthy: false,
|
|
expectedError: string(health.CodeProxyUnhealthy),
|
|
expectedSeverity: health.SeverityError,
|
|
},
|
|
} {
|
|
tt := tt
|
|
if tt.name != "Enabled/ProxyWarnings" {
|
|
continue
|
|
}
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
var rpt healthcheck.WorkspaceProxyReport
|
|
var opts healthcheck.WorkspaceProxyReportOptions
|
|
if tt.fetchWorkspaceProxies != nil && tt.updateProxyHealth != nil {
|
|
opts.WorkspaceProxiesFetchUpdater = &fakeWorkspaceProxyFetchUpdater{
|
|
fetchFunc: tt.fetchWorkspaceProxies,
|
|
updateFunc: tt.updateProxyHealth,
|
|
}
|
|
} else {
|
|
opts.WorkspaceProxiesFetchUpdater = &healthcheck.AGPLWorkspaceProxiesFetchUpdater{}
|
|
}
|
|
|
|
rpt.Run(context.Background(), &opts)
|
|
|
|
assert.Equal(t, tt.expectedHealthy, rpt.Healthy)
|
|
assert.Equal(t, tt.expectedSeverity, rpt.Severity)
|
|
if tt.expectedError != "" && assert.NotNil(t, rpt.Error) {
|
|
assert.Contains(t, *rpt.Error, tt.expectedError)
|
|
} else {
|
|
if !assert.Nil(t, rpt.Error) {
|
|
t.Logf("error: %v", *rpt.Error)
|
|
}
|
|
}
|
|
if tt.expectedWarningCode != "" && assert.NotEmpty(t, rpt.Warnings) {
|
|
var found bool
|
|
for _, w := range rpt.Warnings {
|
|
if w.Code == tt.expectedWarningCode {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "expected warning %s not found in %v", tt.expectedWarningCode, rpt.Warnings)
|
|
} else {
|
|
assert.Empty(t, rpt.Warnings)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkspaceProxy_ErrorDismissed(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var report healthcheck.WorkspaceProxyReport
|
|
report.Run(context.Background(), &healthcheck.WorkspaceProxyReportOptions{
|
|
WorkspaceProxiesFetchUpdater: &fakeWorkspaceProxyFetchUpdater{
|
|
fetchFunc: fakeFetchWorkspaceProxiesErr(assert.AnError),
|
|
updateFunc: fakeUpdateProxyHealth(assert.AnError),
|
|
},
|
|
Dismissed: true,
|
|
})
|
|
|
|
assert.True(t, report.Dismissed)
|
|
assert.Equal(t, health.SeverityWarning, report.Severity)
|
|
}
|
|
|
|
// yet another implementation of the thing
|
|
type fakeWorkspaceProxyFetchUpdater struct {
|
|
fetchFunc func(context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error)
|
|
updateFunc func(context.Context) error
|
|
}
|
|
|
|
func (u *fakeWorkspaceProxyFetchUpdater) Fetch(ctx context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error) {
|
|
return u.fetchFunc(ctx)
|
|
}
|
|
|
|
func (u *fakeWorkspaceProxyFetchUpdater) Update(ctx context.Context) error {
|
|
return u.updateFunc(ctx)
|
|
}
|
|
|
|
//nolint:revive // yes, this is a control flag, and that is OK in a unit test.
|
|
func fakeWorkspaceProxy(name string, healthy bool, mutators ...func(*codersdk.WorkspaceProxy)) codersdk.WorkspaceProxy {
|
|
var status codersdk.WorkspaceProxyStatus
|
|
if !healthy {
|
|
status = codersdk.WorkspaceProxyStatus{
|
|
Status: codersdk.ProxyUnreachable,
|
|
Report: codersdk.ProxyHealthReport{
|
|
Errors: []string{assert.AnError.Error()},
|
|
},
|
|
}
|
|
}
|
|
wsp := codersdk.WorkspaceProxy{
|
|
Region: codersdk.Region{
|
|
Name: name,
|
|
Healthy: healthy,
|
|
},
|
|
Status: status,
|
|
}
|
|
for _, f := range mutators {
|
|
f(&wsp)
|
|
}
|
|
return wsp
|
|
}
|
|
|
|
func fakeFetchWorkspaceProxies(ps ...codersdk.WorkspaceProxy) func(context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error) {
|
|
return func(context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error) {
|
|
return codersdk.RegionsResponse[codersdk.WorkspaceProxy]{
|
|
Regions: ps,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
func fakeFetchWorkspaceProxiesErr(err error) func(context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error) {
|
|
return func(context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error) {
|
|
return codersdk.RegionsResponse[codersdk.WorkspaceProxy]{
|
|
Regions: []codersdk.WorkspaceProxy{},
|
|
}, err
|
|
}
|
|
}
|
|
|
|
func fakeUpdateProxyHealth(err error) func(context.Context) error {
|
|
return func(context.Context) error {
|
|
return err
|
|
}
|
|
}
|