diff --git a/cli/testdata/coder_server_--help.golden b/cli/testdata/coder_server_--help.golden index 8981ea5f30d8f..4d1b9609aa6f4 100644 --- a/cli/testdata/coder_server_--help.golden +++ b/cli/testdata/coder_server_--help.golden @@ -80,6 +80,15 @@ Use a YAML configuration file when your server launch become unwieldy. Write out the current server config as YAML to stdout. +INTROSPECTION / HEALTH CHECK OPTIONS: + --health-check-refresh duration, $CODER_HEALTH_CHECK_REFRESH (default: 10m0s) + Refresh interval for healthchecks. + + --health-check-threshold-database duration, $CODER_HEALTH_CHECK_THRESHOLD_DATABASE (default: 15ms) + The threshold for the database health check. If the median latency of + the database exceeds this threshold over 5 attempts, the database is + considered unhealthy. The default value is 15ms. + INTROSPECTION / LOGGING OPTIONS: --enable-terraform-debug-mode bool, $CODER_ENABLE_TERRAFORM_DEBUG_MODE (default: false) Allow administrators to enable Terraform debug output. diff --git a/cli/testdata/server-config.yaml.golden b/cli/testdata/server-config.yaml.golden index a52ec496de6c1..3163a6e2cab33 100644 --- a/cli/testdata/server-config.yaml.golden +++ b/cli/testdata/server-config.yaml.golden @@ -232,6 +232,15 @@ introspection: # Allow administrators to enable Terraform debug output. # (default: false, type: bool) enableTerraformDebugMode: false + healthcheck: + # Refresh interval for healthchecks. + # (default: 10m0s, type: duration) + refresh: 10m0s + # The threshold for the database health check. If the median latency of the + # database exceeds this threshold over 5 attempts, the database is considered + # unhealthy. The default value is 15ms. + # (default: 15ms, type: duration) + thresholdDatabase: 15ms oauth2: github: # Client ID for Login with GitHub. diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index b55e9d6d23225..e8689850eb081 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8380,6 +8380,9 @@ const docTemplate = `{ "type": "string" } }, + "healthcheck": { + "$ref": "#/definitions/codersdk.HealthcheckConfig" + }, "http_address": { "description": "HTTPAddress is a string because it may be set to zero to disable.", "type": "string" @@ -8859,6 +8862,17 @@ const docTemplate = `{ } } }, + "codersdk.HealthcheckConfig": { + "type": "object", + "properties": { + "refresh": { + "type": "integer" + }, + "threshold_database": { + "type": "integer" + } + } + }, "codersdk.InsightsReportInterval": { "type": "string", "enum": [ @@ -12177,6 +12191,9 @@ const docTemplate = `{ }, "reachable": { "type": "boolean" + }, + "threshold_ms": { + "type": "integer" } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 8b474ff8ecbee..10a54546fde8f 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7492,6 +7492,9 @@ "type": "string" } }, + "healthcheck": { + "$ref": "#/definitions/codersdk.HealthcheckConfig" + }, "http_address": { "description": "HTTPAddress is a string because it may be set to zero to disable.", "type": "string" @@ -7961,6 +7964,17 @@ } } }, + "codersdk.HealthcheckConfig": { + "type": "object", + "properties": { + "refresh": { + "type": "integer" + }, + "threshold_database": { + "type": "integer" + } + } + }, "codersdk.InsightsReportInterval": { "type": "string", "enum": ["day", "week"], @@ -11102,6 +11116,9 @@ }, "reachable": { "type": "boolean" + }, + "threshold_ms": { + "type": "integer" } } }, diff --git a/coderd/coderd.go b/coderd/coderd.go index 600e66404f327..8f138dcc2c6f2 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -38,6 +38,7 @@ import ( // Used for swagger docs. _ "github.com/coder/coder/v2/coderd/apidoc" "github.com/coder/coder/v2/coderd/externalauth" + "github.com/coder/coder/v2/coderd/healthcheck/derphealth" "cdr.dev/slog" "github.com/coder/coder/v2/buildinfo" @@ -398,10 +399,20 @@ func New(options *Options) *API { if options.HealthcheckFunc == nil { options.HealthcheckFunc = func(ctx context.Context, apiKey string) *healthcheck.Report { return healthcheck.Run(ctx, &healthcheck.ReportOptions{ - DB: options.Database, - AccessURL: options.AccessURL, - DERPMap: api.DERPMap(), - APIKey: apiKey, + Database: healthcheck.DatabaseReportOptions{ + DB: options.Database, + Threshold: options.DeploymentValues.Healthcheck.ThresholdDatabase.Value(), + }, + Websocket: healthcheck.WebsocketReportOptions{ + AccessURL: options.AccessURL, + APIKey: apiKey, + }, + AccessURL: healthcheck.AccessURLReportOptions{ + AccessURL: options.AccessURL, + }, + DerpHealth: derphealth.ReportOptions{ + DERPMap: api.DERPMap(), + }, }) } } @@ -409,7 +420,7 @@ func New(options *Options) *API { options.HealthcheckTimeout = 30 * time.Second } if options.HealthcheckRefresh == 0 { - options.HealthcheckRefresh = 10 * time.Minute + options.HealthcheckRefresh = options.DeploymentValues.Healthcheck.Refresh.Value() } var oidcAuthURLParams map[string]string diff --git a/coderd/debug.go b/coderd/debug.go index 1e50b91ba69d3..6b53198046c7c 100644 --- a/coderd/debug.go +++ b/coderd/debug.go @@ -32,12 +32,12 @@ func (api *API) debugCoordinator(rw http.ResponseWriter, r *http.Request) { // @Router /debug/health [get] func (api *API) debugDeploymentHealth(rw http.ResponseWriter, r *http.Request) { apiKey := httpmw.APITokenFromRequest(r) - ctx, cancel := context.WithTimeout(r.Context(), api.HealthcheckTimeout) + ctx, cancel := context.WithTimeout(r.Context(), api.Options.HealthcheckTimeout) defer cancel() // Get cached report if it exists. if report := api.healthCheckCache.Load(); report != nil { - if time.Since(report.Time) < api.HealthcheckRefresh { + if time.Since(report.Time) < api.Options.HealthcheckRefresh { formatHealthcheck(ctx, rw, r, report) return } @@ -45,7 +45,7 @@ func (api *API) debugDeploymentHealth(rw http.ResponseWriter, r *http.Request) { resChan := api.healthCheckGroup.DoChan("", func() (*healthcheck.Report, error) { // Create a new context not tied to the request. - ctx, cancel := context.WithTimeout(context.Background(), api.HealthcheckTimeout) + ctx, cancel := context.WithTimeout(context.Background(), api.Options.HealthcheckTimeout) defer cancel() report := api.HealthcheckFunc(ctx, apiKey) diff --git a/coderd/debug_test.go b/coderd/debug_test.go index f9241a303bcd4..2136ca2d9d6ac 100644 --- a/coderd/debug_test.go +++ b/coderd/debug_test.go @@ -72,6 +72,51 @@ func TestDebugHealth(t *testing.T) { require.Equal(t, http.StatusNotFound, res.StatusCode) }) + t.Run("Refresh", func(t *testing.T) { + t.Parallel() + + var ( + calls = make(chan struct{}) + callsDone = make(chan struct{}) + ctx, cancel = context.WithTimeout(context.Background(), testutil.WaitShort) + client = coderdtest.New(t, &coderdtest.Options{ + HealthcheckRefresh: time.Microsecond, + HealthcheckFunc: func(context.Context, string) *healthcheck.Report { + calls <- struct{}{} + return &healthcheck.Report{} + }, + }) + _ = coderdtest.CreateFirstUser(t, client) + ) + + defer cancel() + + go func() { + defer close(callsDone) + <-calls + <-time.After(testutil.IntervalFast) + <-calls + }() + + res, err := client.Request(ctx, "GET", "/api/v2/debug/health", nil) + require.NoError(t, err) + defer res.Body.Close() + _, _ = io.ReadAll(res.Body) + require.Equal(t, http.StatusOK, res.StatusCode) + + res, err = client.Request(ctx, "GET", "/api/v2/debug/health", nil) + require.NoError(t, err) + defer res.Body.Close() + _, _ = io.ReadAll(res.Body) + require.Equal(t, http.StatusOK, res.StatusCode) + + select { + case <-callsDone: + case <-ctx.Done(): + t.Fatal("timed out waiting for calls to finish") + } + }) + t.Run("Deduplicated", func(t *testing.T) { t.Parallel() diff --git a/coderd/healthcheck/database.go b/coderd/healthcheck/database.go index 70005dc5b3d9f..9ee92d7a71a9f 100644 --- a/coderd/healthcheck/database.go +++ b/coderd/healthcheck/database.go @@ -10,20 +10,30 @@ import ( "github.com/coder/coder/v2/coderd/database" ) +const ( + DatabaseDefaultThreshold = 15 * time.Millisecond +) + // @typescript-generate DatabaseReport type DatabaseReport struct { - Healthy bool `json:"healthy"` - Reachable bool `json:"reachable"` - Latency string `json:"latency"` - LatencyMs int `json:"latency_ms"` - Error *string `json:"error"` + Healthy bool `json:"healthy"` + Reachable bool `json:"reachable"` + Latency string `json:"latency"` + LatencyMS int64 `json:"latency_ms"` + ThresholdMS int64 `json:"threshold_ms"` + Error *string `json:"error"` } type DatabaseReportOptions struct { - DB database.Store + DB database.Store + Threshold time.Duration } func (r *DatabaseReport) Run(ctx context.Context, opts *DatabaseReportOptions) { + r.ThresholdMS = opts.Threshold.Milliseconds() + if r.ThresholdMS == 0 { + r.ThresholdMS = DatabaseDefaultThreshold.Milliseconds() + } ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() @@ -43,10 +53,8 @@ func (r *DatabaseReport) Run(ctx context.Context, opts *DatabaseReportOptions) { // Take the median ping. latency := pings[pingCount/2] r.Latency = latency.String() - r.LatencyMs = int(latency.Milliseconds()) - // Somewhat arbitrary, but if the latency is over 15ms, we consider it - // unhealthy. - if latency < 15*time.Millisecond { + r.LatencyMS = latency.Milliseconds() + if r.LatencyMS < r.ThresholdMS { r.Healthy = true } r.Reachable = true diff --git a/coderd/healthcheck/database_test.go b/coderd/healthcheck/database_test.go index f6c2782aacacd..be97c6424a47a 100644 --- a/coderd/healthcheck/database_test.go +++ b/coderd/healthcheck/database_test.go @@ -36,7 +36,8 @@ func TestDatabase(t *testing.T) { assert.True(t, report.Healthy) assert.True(t, report.Reachable) assert.Equal(t, ping.String(), report.Latency) - assert.Equal(t, int(ping.Milliseconds()), report.LatencyMs) + assert.Equal(t, ping.Milliseconds(), report.LatencyMS) + assert.Equal(t, healthcheck.DatabaseDefaultThreshold.Milliseconds(), report.ThresholdMS) assert.Nil(t, report.Error) }) @@ -59,6 +60,7 @@ func TestDatabase(t *testing.T) { assert.False(t, report.Reachable) assert.Zero(t, report.Latency) require.NotNil(t, report.Error) + assert.Equal(t, healthcheck.DatabaseDefaultThreshold.Milliseconds(), report.ThresholdMS) assert.Contains(t, *report.Error, err.Error()) }) @@ -83,7 +85,34 @@ func TestDatabase(t *testing.T) { assert.True(t, report.Healthy) assert.True(t, report.Reachable) assert.Equal(t, time.Millisecond.String(), report.Latency) - assert.Equal(t, 1, report.LatencyMs) + assert.EqualValues(t, 1, report.LatencyMS) + assert.Equal(t, healthcheck.DatabaseDefaultThreshold.Milliseconds(), report.ThresholdMS) + assert.Nil(t, report.Error) + }) + + t.Run("Threshold", func(t *testing.T) { + t.Parallel() + + var ( + ctx, cancel = context.WithTimeout(context.Background(), testutil.WaitShort) + report = healthcheck.DatabaseReport{} + db = dbmock.NewMockStore(gomock.NewController(t)) + ) + defer cancel() + + db.EXPECT().Ping(gomock.Any()).Return(time.Second, nil) + db.EXPECT().Ping(gomock.Any()).Return(time.Millisecond, nil) + db.EXPECT().Ping(gomock.Any()).Return(time.Second, nil) + db.EXPECT().Ping(gomock.Any()).Return(time.Millisecond, nil) + db.EXPECT().Ping(gomock.Any()).Return(time.Second, nil) + + report.Run(ctx, &healthcheck.DatabaseReportOptions{DB: db, Threshold: time.Second}) + + assert.False(t, report.Healthy) + assert.True(t, report.Reachable) + assert.Equal(t, time.Second.String(), report.Latency) + assert.EqualValues(t, 1000, report.LatencyMS) + assert.Equal(t, time.Second.Milliseconds(), report.ThresholdMS) assert.Nil(t, report.Error) }) } diff --git a/coderd/healthcheck/healthcheck.go b/coderd/healthcheck/healthcheck.go index 61c6e40c1e1be..d59de08592203 100644 --- a/coderd/healthcheck/healthcheck.go +++ b/coderd/healthcheck/healthcheck.go @@ -3,15 +3,10 @@ package healthcheck import ( "context" "fmt" - "net/http" - "net/url" "sync" "time" - "tailscale.com/tailcfg" - "github.com/coder/coder/v2/buildinfo" - "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/healthcheck/derphealth" "github.com/coder/coder/v2/coderd/util/ptr" ) @@ -49,12 +44,10 @@ type Report struct { } type ReportOptions struct { - DB database.Store - // TODO: support getting this over HTTP? - DERPMap *tailcfg.DERPMap - AccessURL *url.URL - Client *http.Client - APIKey string + AccessURL AccessURLReportOptions + Database DatabaseReportOptions + DerpHealth derphealth.ReportOptions + Websocket WebsocketReportOptions Checker Checker } @@ -100,9 +93,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report { } }() - report.DERP = opts.Checker.DERP(ctx, &derphealth.ReportOptions{ - DERPMap: opts.DERPMap, - }) + report.DERP = opts.Checker.DERP(ctx, &opts.DerpHealth) }() wg.Add(1) @@ -114,10 +105,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report { } }() - report.AccessURL = opts.Checker.AccessURL(ctx, &AccessURLReportOptions{ - AccessURL: opts.AccessURL, - Client: opts.Client, - }) + report.AccessURL = opts.Checker.AccessURL(ctx, &opts.AccessURL) }() wg.Add(1) @@ -129,10 +117,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report { } }() - report.Websocket = opts.Checker.Websocket(ctx, &WebsocketReportOptions{ - APIKey: opts.APIKey, - AccessURL: opts.AccessURL, - }) + report.Websocket = opts.Checker.Websocket(ctx, &opts.Websocket) }() wg.Add(1) @@ -144,9 +129,7 @@ func Run(ctx context.Context, opts *ReportOptions) *Report { } }() - report.Database = opts.Checker.Database(ctx, &DatabaseReportOptions{ - DB: opts.DB, - }) + report.Database = opts.Checker.Database(ctx, &opts.Database) }() report.CoderVersion = buildinfo.Version() diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 586de0774849f..613e3b17045fa 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -183,6 +183,7 @@ type DeploymentValues struct { EnableTerraformDebugMode clibase.Bool `json:"enable_terraform_debug_mode,omitempty" typescript:",notnull"` UserQuietHoursSchedule UserQuietHoursScheduleConfig `json:"user_quiet_hours_schedule,omitempty" typescript:",notnull"` WebTerminalRenderer clibase.String `json:"web_terminal_renderer,omitempty" typescript:",notnull"` + Healthcheck HealthcheckConfig `json:"healthcheck,omitempty" typescript:",notnull"` Config clibase.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"` WriteConfig clibase.Bool `json:"write_config,omitempty" typescript:",notnull"` @@ -395,6 +396,12 @@ type UserQuietHoursScheduleConfig struct { // WindowDuration clibase.Duration `json:"window_duration" typescript:",notnull"` } +// HealthcheckConfig contains configuration for healthchecks. +type HealthcheckConfig struct { + Refresh clibase.Duration `json:"refresh" typescript:",notnull"` + ThresholdDatabase clibase.Duration `json:"threshold_database" typescript:",notnull"` +} + const ( annotationEnterpriseKey = "enterprise" annotationSecretKey = "secret" @@ -489,6 +496,11 @@ func (c *DeploymentValues) Options() clibase.OptionSet { Name: "Logging", YAML: "logging", } + deploymentGroupIntrospectionHealthcheck = clibase.Group{ + Parent: &deploymentGroupIntrospection, + Name: "Health Check", + YAML: "healthcheck", + } deploymentGroupOAuth2 = clibase.Group{ Name: "OAuth2", Description: `Configure login and user-provisioning with GitHub via oAuth2.`, @@ -1799,6 +1811,27 @@ Write out the current server config as YAML to stdout.`, Group: &deploymentGroupClient, YAML: "webTerminalRenderer", }, + // Healthcheck Options + { + Name: "Health Check Refresh", + Description: "Refresh interval for healthchecks.", + Flag: "health-check-refresh", + Env: "CODER_HEALTH_CHECK_REFRESH", + Default: (10 * time.Minute).String(), + Value: &c.Healthcheck.Refresh, + Group: &deploymentGroupIntrospectionHealthcheck, + YAML: "refresh", + }, + { + Name: "Health Check Threshold: Database", + Description: "The threshold for the database health check. If the median latency of the database exceeds this threshold over 5 attempts, the database is considered unhealthy. The default value is 15ms.", + Flag: "health-check-threshold-database", + Env: "CODER_HEALTH_CHECK_THRESHOLD_DATABASE", + Default: (15 * time.Millisecond).String(), + Value: &c.Healthcheck.ThresholdDatabase, + Group: &deploymentGroupIntrospectionHealthcheck, + YAML: "thresholdDatabase", + }, } return opts diff --git a/docs/api/debug.md b/docs/api/debug.md index 5016f6a87b256..24ccf253398cb 100644 --- a/docs/api/debug.md +++ b/docs/api/debug.md @@ -53,7 +53,8 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "healthy": true, "latency": "string", "latency_ms": 0, - "reachable": true + "reachable": true, + "threshold_ms": 0 }, "derp": { "error": "string", diff --git a/docs/api/general.md b/docs/api/general.md index 6d000836670ea..de89e07e558c5 100644 --- a/docs/api/general.md +++ b/docs/api/general.md @@ -235,6 +235,10 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ ] }, "external_token_encryption_keys": ["string"], + "healthcheck": { + "refresh": 0, + "threshold_database": 0 + }, "http_address": "string", "in_memory_database": true, "job_hang_detector_interval": 0, diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 517e76981c567..d3a61585d096c 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2156,6 +2156,10 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ] }, "external_token_encryption_keys": ["string"], + "healthcheck": { + "refresh": 0, + "threshold_database": 0 + }, "http_address": "string", "in_memory_database": true, "job_hang_detector_interval": 0, @@ -2527,6 +2531,10 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ] }, "external_token_encryption_keys": ["string"], + "healthcheck": { + "refresh": 0, + "threshold_database": 0 + }, "http_address": "string", "in_memory_database": true, "job_hang_detector_interval": 0, @@ -2726,6 +2734,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `experiments` | array of string | false | | | | `external_auth` | [clibase.Struct-array_codersdk_ExternalAuthConfig](#clibasestruct-array_codersdk_externalauthconfig) | false | | | | `external_token_encryption_keys` | array of string | false | | | +| `healthcheck` | [codersdk.HealthcheckConfig](#codersdkhealthcheckconfig) | false | | | | `http_address` | string | false | | Http address is a string because it may be set to zero to disable. | | `in_memory_database` | boolean | false | | | | `job_hang_detector_interval` | integer | false | | | @@ -3176,6 +3185,22 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | | `url` | string | false | | URL specifies the endpoint to check for the app health. | +## codersdk.HealthcheckConfig + +```json +{ + "refresh": 0, + "threshold_database": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------------- | ------- | -------- | ------------ | ----------- | +| `refresh` | integer | false | | | +| `threshold_database` | integer | false | | | + ## codersdk.InsightsReportInterval ```json @@ -7444,19 +7469,21 @@ If the schedule is empty, the user will be updated to use the default schedule.| "healthy": true, "latency": "string", "latency_ms": 0, - "reachable": true + "reachable": true, + "threshold_ms": 0 } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------ | ------- | -------- | ------------ | ----------- | -| `error` | string | false | | | -| `healthy` | boolean | false | | | -| `latency` | string | false | | | -| `latency_ms` | integer | false | | | -| `reachable` | boolean | 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 | | | ## healthcheck.Report @@ -7476,7 +7503,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "healthy": true, "latency": "string", "latency_ms": 0, - "reachable": true + "reachable": true, + "threshold_ms": 0 }, "derp": { "error": "string", diff --git a/docs/cli/server.md b/docs/cli/server.md index 93a07b72f98a8..3fb0c57b3ee48 100644 --- a/docs/cli/server.md +++ b/docs/cli/server.md @@ -305,6 +305,28 @@ Time to force cancel provisioning tasks that are stuck. HTTP bind address of the server. Unset to disable the HTTP endpoint. +### --health-check-refresh + +| | | +| ----------- | ---------------------------------------------- | +| Type | duration | +| Environment | $CODER_HEALTH_CHECK_REFRESH | +| YAML | introspection.healthcheck.refresh | +| Default | 10m0s | + +Refresh interval for healthchecks. + +### --health-check-threshold-database + +| | | +| ----------- | -------------------------------------------------------- | +| Type | duration | +| Environment | $CODER_HEALTH_CHECK_THRESHOLD_DATABASE | +| YAML | introspection.healthcheck.thresholdDatabase | +| Default | 15ms | + +The threshold for the database health check. If the median latency of the database exceeds this threshold over 5 attempts, the database is considered unhealthy. The default value is 15ms. + ### --log-human | | | diff --git a/enterprise/cli/testdata/coder_server_--help.golden b/enterprise/cli/testdata/coder_server_--help.golden index 6e3b4a502ed27..85c924474c206 100644 --- a/enterprise/cli/testdata/coder_server_--help.golden +++ b/enterprise/cli/testdata/coder_server_--help.golden @@ -81,6 +81,15 @@ Use a YAML configuration file when your server launch become unwieldy. Write out the current server config as YAML to stdout. +INTROSPECTION / HEALTH CHECK OPTIONS: + --health-check-refresh duration, $CODER_HEALTH_CHECK_REFRESH (default: 10m0s) + Refresh interval for healthchecks. + + --health-check-threshold-database duration, $CODER_HEALTH_CHECK_THRESHOLD_DATABASE (default: 15ms) + The threshold for the database health check. If the median latency of + the database exceeds this threshold over 5 attempts, the database is + considered unhealthy. The default value is 15ms. + INTROSPECTION / LOGGING OPTIONS: --enable-terraform-debug-mode bool, $CODER_ENABLE_TERRAFORM_DEBUG_MODE (default: false) Allow administrators to enable Terraform debug output. diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 920237160dfd1..dcfb60584d7c8 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -423,6 +423,7 @@ export interface DeploymentValues { readonly enable_terraform_debug_mode?: boolean; readonly user_quiet_hours_schedule?: UserQuietHoursScheduleConfig; readonly web_terminal_renderer?: string; + readonly healthcheck?: HealthcheckConfig; readonly config?: string; readonly write_config?: boolean; readonly address?: string; @@ -548,6 +549,12 @@ export interface Healthcheck { readonly threshold: number; } +// From codersdk/deployment.go +export interface HealthcheckConfig { + readonly refresh: number; + readonly threshold_database: number; +} + // From codersdk/workspaceagents.go export interface IssueReconnectingPTYSignedTokenRequest { readonly url: string; @@ -2088,6 +2095,7 @@ export interface HealthcheckDatabaseReport { readonly reachable: boolean; readonly latency: string; readonly latency_ms: number; + readonly threshold_ms: number; readonly error?: string; }