Skip to content

Commit b63ce10

Browse files
committed
WIP
1 parent e311e9e commit b63ce10

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed

coderd/healthcheck/workspaceproxy.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package healthcheck
2+
3+
import (
4+
"context"
5+
6+
"cdr.dev/slog"
7+
"github.com/coder/coder/v2/buildinfo"
8+
"github.com/coder/coder/v2/codersdk"
9+
)
10+
11+
type WorkspaceProxyReportOptions struct {
12+
// UpdateProxyHealth is a function called when healthcheck is run.
13+
// This would normally be ProxyHealth.ForceUpdate().
14+
// We do this because if someone mashes the healthcheck refresh button
15+
// they would expect up-to-date data.
16+
UpdateProxyHealth func(context.Context) error
17+
// FetchWorkspaceProxies is a function that returns the available workspace proxies.
18+
FetchWorkspaceProxies func(context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error)
19+
// CurrentVersion is the current server version.
20+
// We pass this in to make it easier to test.
21+
CurrentVersion string
22+
Logger slog.Logger
23+
}
24+
25+
// @typescript-generate Report
26+
type WorkspaceProxyReport struct {
27+
Healthy bool `json:"healthy"`
28+
Warnings []string `json:"warnings"`
29+
30+
WorkspaceProxies codersdk.RegionsResponse[codersdk.WorkspaceProxy]
31+
}
32+
33+
func (r *WorkspaceProxyReport) Run(ctx context.Context, opts *WorkspaceProxyReportOptions) {
34+
r.Healthy = true
35+
r.Warnings = []string{}
36+
37+
if opts.FetchWorkspaceProxies == nil {
38+
opts.Logger.Info(ctx, "no workspace proxies configured")
39+
return
40+
}
41+
42+
if opts.UpdateProxyHealth == nil {
43+
opts.Logger.Error(ctx, "developer error: opts.UpdateProxyHealth must not be nil if opts.FetchWorkspaceProxies is not nil")
44+
}
45+
46+
if err := opts.UpdateProxyHealth; err != nil {
47+
opts.Logger.Error(ctx, "failed to update proxy health: %w", err)
48+
}
49+
50+
proxies, err := opts.FetchWorkspaceProxies(ctx)
51+
if err != nil {
52+
opts.Logger.Error(ctx, "failed to fetch workspace proxies", slog.Error(err))
53+
r.Healthy = false
54+
}
55+
56+
r.WorkspaceProxies = proxies
57+
58+
var numProxies int
59+
var healthyProxies int
60+
for _, proxy := range r.WorkspaceProxies.Regions {
61+
numProxies++
62+
if proxy.Healthy {
63+
healthyProxies++
64+
}
65+
66+
// check versions
67+
if !buildinfo.VersionsMatch(proxy.Version, opts.CurrentVersion) {
68+
opts.Logger.Warn(ctx, "proxy version mismatch",
69+
slog.F("version", opts.CurrentVersion),
70+
slog.F("proxy_version", proxy.Version),
71+
slog.F("proxy_name", proxy.Name),
72+
)
73+
r.Warnings = append(r.Warnings, "Proxy %q version %q does not match primary server version %q", proxy.Name, proxy.Version, opts.CurrentVersion)
74+
}
75+
}
76+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package healthcheck_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"cdr.dev/slog/sloggers/slogtest"
10+
11+
"github.com/coder/coder/v2/coderd/healthcheck"
12+
"github.com/coder/coder/v2/codersdk"
13+
"github.com/coder/coder/v2/testutil"
14+
)
15+
16+
func TestWorkspaceProxies(t *testing.T) {
17+
t.Parallel()
18+
19+
t.Run("NotEnabled", func(t *testing.T) {
20+
t.Parallel()
21+
22+
ctx := testutil.Context(t, testutil.WaitShort)
23+
log := slogtest.Make(t, nil)
24+
rpt := healthcheck.WorkspaceProxyReport{}
25+
rpt.Run(ctx, &healthcheck.WorkspaceProxyReportOptions{
26+
Logger: log,
27+
})
28+
29+
require.True(t, rpt.Healthy, "expected report to be healthy")
30+
require.Empty(t, rpt.Warnings, "expected no warnings")
31+
require.Empty(t, rpt.WorkspaceProxies, "expected no proxies")
32+
})
33+
34+
t.Run("VersionMajorMinor", func(t *testing.T) {
35+
t.Parallel()
36+
37+
ctx := testutil.Context(t, testutil.WaitShort)
38+
log := slogtest.Make(t, nil)
39+
rpt := healthcheck.WorkspaceProxyReport{}
40+
rpt.Run(ctx, &healthcheck.WorkspaceProxyReportOptions{
41+
CurrentVersion: "v2.34.5",
42+
FetchWorkspaceProxies: func(_ context.Context) (codersdk.RegionsResponse[codersdk.WorkspaceProxy], error) {
43+
return codersdk.RegionsResponse[codersdk.WorkspaceProxy]{
44+
Regions: []codersdk.WorkspaceProxy{
45+
{
46+
Region: codersdk.Region{
47+
Healthy: true,
48+
},
49+
Version: "v2.34.4",
50+
},
51+
},
52+
}, nil
53+
},
54+
UpdateProxyHealth: func(context.Context) error { return nil },
55+
Logger: log,
56+
})
57+
58+
require.True(t, rpt.Healthy, "expected report to be healthy")
59+
require.Empty(t, rpt.Warnings, "expected no warnings")
60+
require.NotEmpty(t, rpt.WorkspaceProxies, "expected at least one proxy")
61+
})
62+
63+
t.Run("VersionMismatch", func(t *testing.T) {
64+
t.Parallel()
65+
66+
ctx := testutil.Context(t, testutil.WaitShort)
67+
log := slogtest.Make(t, nil)
68+
rpt := healthcheck.WorkspaceProxyReport{}
69+
rpt.Run(ctx, &healthcheck.WorkspaceProxyReportOptions{
70+
Logger: log,
71+
})
72+
73+
require.False(t, rpt.Healthy, "expected report not to be healthy")
74+
require.Len(t, rpt.Warnings, 1)
75+
require.Contains(t, rpt.Warnings[0], "does not match server version")
76+
require.NotEmpty(t, rpt.WorkspaceProxies)
77+
})
78+
}

0 commit comments

Comments
 (0)