mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
Currently, importing `codersdk` just to interact with the API requires importing tailscale, which causes builds to fail unless manually using our fork.
212 lines
6.4 KiB
Go
212 lines
6.4 KiB
Go
package healthcheck
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/coder/coder/v2/buildinfo"
|
|
"github.com/coder/coder/v2/coderd/healthcheck/derphealth"
|
|
"github.com/coder/coder/v2/coderd/healthcheck/health"
|
|
"github.com/coder/coder/v2/coderd/util/ptr"
|
|
"github.com/coder/coder/v2/codersdk/healthsdk"
|
|
)
|
|
|
|
type Checker interface {
|
|
DERP(ctx context.Context, opts *derphealth.ReportOptions) healthsdk.DERPHealthReport
|
|
AccessURL(ctx context.Context, opts *AccessURLReportOptions) healthsdk.AccessURLReport
|
|
Websocket(ctx context.Context, opts *WebsocketReportOptions) healthsdk.WebsocketReport
|
|
Database(ctx context.Context, opts *DatabaseReportOptions) healthsdk.DatabaseReport
|
|
WorkspaceProxy(ctx context.Context, opts *WorkspaceProxyReportOptions) healthsdk.WorkspaceProxyReport
|
|
ProvisionerDaemons(ctx context.Context, opts *ProvisionerDaemonsReportDeps) healthsdk.ProvisionerDaemonsReport
|
|
}
|
|
|
|
type ReportOptions struct {
|
|
AccessURL AccessURLReportOptions
|
|
Database DatabaseReportOptions
|
|
DerpHealth derphealth.ReportOptions
|
|
Websocket WebsocketReportOptions
|
|
WorkspaceProxy WorkspaceProxyReportOptions
|
|
ProvisionerDaemons ProvisionerDaemonsReportDeps
|
|
|
|
Checker Checker
|
|
}
|
|
|
|
type defaultChecker struct{}
|
|
|
|
func (defaultChecker) DERP(ctx context.Context, opts *derphealth.ReportOptions) healthsdk.DERPHealthReport {
|
|
var report derphealth.Report
|
|
report.Run(ctx, opts)
|
|
return healthsdk.DERPHealthReport(report)
|
|
}
|
|
|
|
func (defaultChecker) AccessURL(ctx context.Context, opts *AccessURLReportOptions) healthsdk.AccessURLReport {
|
|
var report AccessURLReport
|
|
report.Run(ctx, opts)
|
|
return healthsdk.AccessURLReport(report)
|
|
}
|
|
|
|
func (defaultChecker) Websocket(ctx context.Context, opts *WebsocketReportOptions) healthsdk.WebsocketReport {
|
|
var report WebsocketReport
|
|
report.Run(ctx, opts)
|
|
return healthsdk.WebsocketReport(report)
|
|
}
|
|
|
|
func (defaultChecker) Database(ctx context.Context, opts *DatabaseReportOptions) healthsdk.DatabaseReport {
|
|
var report DatabaseReport
|
|
report.Run(ctx, opts)
|
|
return healthsdk.DatabaseReport(report)
|
|
}
|
|
|
|
func (defaultChecker) WorkspaceProxy(ctx context.Context, opts *WorkspaceProxyReportOptions) healthsdk.WorkspaceProxyReport {
|
|
var report WorkspaceProxyReport
|
|
report.Run(ctx, opts)
|
|
return healthsdk.WorkspaceProxyReport(report)
|
|
}
|
|
|
|
func (defaultChecker) ProvisionerDaemons(ctx context.Context, opts *ProvisionerDaemonsReportDeps) healthsdk.ProvisionerDaemonsReport {
|
|
var report ProvisionerDaemonsReport
|
|
report.Run(ctx, opts)
|
|
return healthsdk.ProvisionerDaemonsReport(report)
|
|
}
|
|
|
|
func Run(ctx context.Context, opts *ReportOptions) *healthsdk.HealthcheckReport {
|
|
var (
|
|
wg sync.WaitGroup
|
|
report healthsdk.HealthcheckReport
|
|
)
|
|
|
|
if opts.Checker == nil {
|
|
opts.Checker = defaultChecker{}
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
report.DERP.Error = health.Errorf(health.CodeUnknown, "derp report panic: %s", err)
|
|
}
|
|
}()
|
|
|
|
report.DERP = opts.Checker.DERP(ctx, &opts.DerpHealth)
|
|
}()
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
report.AccessURL.Error = health.Errorf(health.CodeUnknown, "access url report panic: %s", err)
|
|
}
|
|
}()
|
|
|
|
report.AccessURL = opts.Checker.AccessURL(ctx, &opts.AccessURL)
|
|
}()
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
report.Websocket.Error = health.Errorf(health.CodeUnknown, "websocket report panic: %s", err)
|
|
}
|
|
}()
|
|
|
|
report.Websocket = opts.Checker.Websocket(ctx, &opts.Websocket)
|
|
}()
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
report.Database.Error = health.Errorf(health.CodeUnknown, "database report panic: %s", err)
|
|
}
|
|
}()
|
|
|
|
report.Database = opts.Checker.Database(ctx, &opts.Database)
|
|
}()
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
report.WorkspaceProxy.Error = health.Errorf(health.CodeUnknown, "proxy report panic: %s", err)
|
|
}
|
|
}()
|
|
|
|
report.WorkspaceProxy = opts.Checker.WorkspaceProxy(ctx, &opts.WorkspaceProxy)
|
|
}()
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
report.ProvisionerDaemons.Error = health.Errorf(health.CodeUnknown, "provisioner daemon report panic: %s", err)
|
|
}
|
|
}()
|
|
|
|
report.ProvisionerDaemons = opts.Checker.ProvisionerDaemons(ctx, &opts.ProvisionerDaemons)
|
|
}()
|
|
|
|
report.CoderVersion = buildinfo.Version()
|
|
wg.Wait()
|
|
|
|
report.Time = time.Now()
|
|
report.FailingSections = []healthsdk.HealthSection{}
|
|
if report.DERP.Severity.Value() > health.SeverityWarning.Value() {
|
|
report.FailingSections = append(report.FailingSections, healthsdk.HealthSectionDERP)
|
|
}
|
|
if report.AccessURL.Severity.Value() > health.SeverityOK.Value() {
|
|
report.FailingSections = append(report.FailingSections, healthsdk.HealthSectionAccessURL)
|
|
}
|
|
if report.Websocket.Severity.Value() > health.SeverityWarning.Value() {
|
|
report.FailingSections = append(report.FailingSections, healthsdk.HealthSectionWebsocket)
|
|
}
|
|
if report.Database.Severity.Value() > health.SeverityWarning.Value() {
|
|
report.FailingSections = append(report.FailingSections, healthsdk.HealthSectionDatabase)
|
|
}
|
|
if report.WorkspaceProxy.Severity.Value() > health.SeverityWarning.Value() {
|
|
report.FailingSections = append(report.FailingSections, healthsdk.HealthSectionWorkspaceProxy)
|
|
}
|
|
if report.ProvisionerDaemons.Severity.Value() > health.SeverityWarning.Value() {
|
|
report.FailingSections = append(report.FailingSections, healthsdk.HealthSectionProvisionerDaemons)
|
|
}
|
|
|
|
report.Healthy = len(report.FailingSections) == 0
|
|
|
|
// Review healthcheck sub-reports.
|
|
report.Severity = health.SeverityOK
|
|
|
|
if report.DERP.Severity.Value() > report.Severity.Value() {
|
|
report.Severity = report.DERP.Severity
|
|
}
|
|
if report.AccessURL.Severity.Value() > report.Severity.Value() {
|
|
report.Severity = report.AccessURL.Severity
|
|
}
|
|
if report.Websocket.Severity.Value() > report.Severity.Value() {
|
|
report.Severity = report.Websocket.Severity
|
|
}
|
|
if report.Database.Severity.Value() > report.Severity.Value() {
|
|
report.Severity = report.Database.Severity
|
|
}
|
|
if report.WorkspaceProxy.Severity.Value() > report.Severity.Value() {
|
|
report.Severity = report.WorkspaceProxy.Severity
|
|
}
|
|
if report.ProvisionerDaemons.Severity.Value() > report.Severity.Value() {
|
|
report.Severity = report.ProvisionerDaemons.Severity
|
|
}
|
|
return &report
|
|
}
|
|
|
|
func convertError(err error) *string {
|
|
if err != nil {
|
|
return ptr.Ref(err.Error())
|
|
}
|
|
|
|
return nil
|
|
}
|