diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go
index bbf238df73e60..64ea030b1e0b7 100644
--- a/coderd/apidoc/docs.go
+++ b/coderd/apidoc/docs.go
@@ -430,6 +430,68 @@ const docTemplate = `{
}
}
},
+ "/debug/health/settings": {
+ "get": {
+ "security": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Debug"
+ ],
+ "summary": "Get health settings",
+ "operationId": "get-health-settings",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.HealthSettings"
+ }
+ }
+ }
+ },
+ "put": {
+ "security": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Debug"
+ ],
+ "summary": "Update health settings",
+ "operationId": "update-health-settings",
+ "parameters": [
+ {
+ "description": "Update health settings",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/codersdk.UpdateHealthSettings"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.UpdateHealthSettings"
+ }
+ }
+ }
+ }
+ },
"/debug/tailnet": {
"get": {
"security": [
@@ -8904,6 +8966,17 @@ const docTemplate = `{
"GroupSourceOIDC"
]
},
+ "codersdk.HealthSettings": {
+ "type": "object",
+ "properties": {
+ "dismissed_healthchecks": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
"codersdk.Healthcheck": {
"type": "object",
"properties": {
@@ -9851,6 +9924,7 @@ const docTemplate = `{
"group",
"license",
"convert_login",
+ "health_settings",
"workspace_proxy",
"organization"
],
@@ -9865,6 +9939,7 @@ const docTemplate = `{
"ResourceTypeGroup",
"ResourceTypeLicense",
"ResourceTypeConvertLogin",
+ "ResourceTypeHealthSettings",
"ResourceTypeWorkspaceProxy",
"ResourceTypeOrganization"
]
@@ -10771,6 +10846,17 @@ const docTemplate = `{
}
}
},
+ "codersdk.UpdateHealthSettings": {
+ "type": "object",
+ "properties": {
+ "dismissed_healthchecks": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
"codersdk.UpdateRoles": {
"type": "object",
"properties": {
diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json
index a692f7258e981..1f6a8f07f5670 100644
--- a/coderd/apidoc/swagger.json
+++ b/coderd/apidoc/swagger.json
@@ -366,6 +366,58 @@
}
}
},
+ "/debug/health/settings": {
+ "get": {
+ "security": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": ["application/json"],
+ "tags": ["Debug"],
+ "summary": "Get health settings",
+ "operationId": "get-health-settings",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.HealthSettings"
+ }
+ }
+ }
+ },
+ "put": {
+ "security": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "consumes": ["application/json"],
+ "produces": ["application/json"],
+ "tags": ["Debug"],
+ "summary": "Update health settings",
+ "operationId": "update-health-settings",
+ "parameters": [
+ {
+ "description": "Update health settings",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/codersdk.UpdateHealthSettings"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.UpdateHealthSettings"
+ }
+ }
+ }
+ }
+ },
"/debug/tailnet": {
"get": {
"security": [
@@ -8000,6 +8052,17 @@
"enum": ["user", "oidc"],
"x-enum-varnames": ["GroupSourceUser", "GroupSourceOIDC"]
},
+ "codersdk.HealthSettings": {
+ "type": "object",
+ "properties": {
+ "dismissed_healthchecks": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
"codersdk.Healthcheck": {
"type": "object",
"properties": {
@@ -8877,6 +8940,7 @@
"group",
"license",
"convert_login",
+ "health_settings",
"workspace_proxy",
"organization"
],
@@ -8891,6 +8955,7 @@
"ResourceTypeGroup",
"ResourceTypeLicense",
"ResourceTypeConvertLogin",
+ "ResourceTypeHealthSettings",
"ResourceTypeWorkspaceProxy",
"ResourceTypeOrganization"
]
@@ -9754,6 +9819,17 @@
}
}
},
+ "codersdk.UpdateHealthSettings": {
+ "type": "object",
+ "properties": {
+ "dismissed_healthchecks": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
"codersdk.UpdateRoles": {
"type": "object",
"properties": {
diff --git a/coderd/audit/diff.go b/coderd/audit/diff.go
index 8cf0a1d0ddaf3..bdaef00bb082b 100644
--- a/coderd/audit/diff.go
+++ b/coderd/audit/diff.go
@@ -18,7 +18,8 @@ type Auditable interface {
database.AuditableGroup |
database.License |
database.WorkspaceProxy |
- database.AuditOAuthConvertState
+ database.AuditOAuthConvertState |
+ database.HealthSettings
}
// Map is a map of changed fields in an audited resource. It maps field names to
diff --git a/coderd/audit/request.go b/coderd/audit/request.go
index cc1f60779a7dc..6e738f9929bbb 100644
--- a/coderd/audit/request.go
+++ b/coderd/audit/request.go
@@ -93,6 +93,8 @@ func ResourceTarget[T Auditable](tgt T) string {
return typed.Name
case database.AuditOAuthConvertState:
return string(typed.ToLoginType)
+ case database.HealthSettings:
+ return "" // no target?
default:
panic(fmt.Sprintf("unknown resource %T", tgt))
}
@@ -123,6 +125,9 @@ func ResourceID[T Auditable](tgt T) uuid.UUID {
case database.AuditOAuthConvertState:
// The merge state is for the given user
return typed.UserID
+ case database.HealthSettings:
+ // Artificial ID for auditing purposes
+ return typed.ID
default:
panic(fmt.Sprintf("unknown resource %T", tgt))
}
@@ -152,6 +157,8 @@ func ResourceType[T Auditable](tgt T) database.ResourceType {
return database.ResourceTypeWorkspaceProxy
case database.AuditOAuthConvertState:
return database.ResourceTypeConvertLogin
+ case database.HealthSettings:
+ return database.ResourceTypeHealthSettings
default:
panic(fmt.Sprintf("unknown resource %T", typed))
}
diff --git a/coderd/coderd.go b/coderd/coderd.go
index 2940eb64c3128..091ef987ff31e 100644
--- a/coderd/coderd.go
+++ b/coderd/coderd.go
@@ -970,7 +970,13 @@ func New(options *Options) *API {
r.Get("/coordinator", api.debugCoordinator)
r.Get("/tailnet", api.debugTailnet)
- r.Get("/health", api.debugDeploymentHealth)
+ r.Route("/health", func(r chi.Router) {
+ r.Get("/", api.debugDeploymentHealth)
+ r.Route("/settings", func(r chi.Router) {
+ r.Get("/", api.deploymentHealthSettings)
+ r.Put("/", api.putDeploymentHealthSettings)
+ })
+ })
r.Get("/ws", (&healthcheck.WebsocketEchoServer{}).ServeHTTP)
r.Route("/{user}", func(r chi.Router) {
r.Use(httpmw.ExtractUserParam(options.Database))
diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql
index d65da03ed3448..f4200f7ea3109 100644
--- a/coderd/database/dump.sql
+++ b/coderd/database/dump.sql
@@ -133,7 +133,8 @@ CREATE TYPE resource_type AS ENUM (
'workspace_build',
'license',
'workspace_proxy',
- 'convert_login'
+ 'convert_login',
+ 'health_settings'
);
CREATE TYPE startup_script_behavior AS ENUM (
diff --git a/coderd/database/migrations/000172_health_settings_audit.down.sql b/coderd/database/migrations/000172_health_settings_audit.down.sql
new file mode 100644
index 0000000000000..362f597df0911
--- /dev/null
+++ b/coderd/database/migrations/000172_health_settings_audit.down.sql
@@ -0,0 +1 @@
+-- Nothing to do
diff --git a/coderd/database/migrations/000172_health_settings_audit.up.sql b/coderd/database/migrations/000172_health_settings_audit.up.sql
new file mode 100644
index 0000000000000..09dd8e17bfe0c
--- /dev/null
+++ b/coderd/database/migrations/000172_health_settings_audit.up.sql
@@ -0,0 +1,2 @@
+-- This has to be outside a transaction
+ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'health_settings';
diff --git a/coderd/database/models.go b/coderd/database/models.go
index 3fe6c8fa12b87..bd7625657b7bd 100644
--- a/coderd/database/models.go
+++ b/coderd/database/models.go
@@ -1158,6 +1158,7 @@ const (
ResourceTypeLicense ResourceType = "license"
ResourceTypeWorkspaceProxy ResourceType = "workspace_proxy"
ResourceTypeConvertLogin ResourceType = "convert_login"
+ ResourceTypeHealthSettings ResourceType = "health_settings"
)
func (e *ResourceType) Scan(src interface{}) error {
@@ -1208,7 +1209,8 @@ func (e ResourceType) Valid() bool {
ResourceTypeWorkspaceBuild,
ResourceTypeLicense,
ResourceTypeWorkspaceProxy,
- ResourceTypeConvertLogin:
+ ResourceTypeConvertLogin,
+ ResourceTypeHealthSettings:
return true
}
return false
@@ -1228,6 +1230,7 @@ func AllResourceTypeValues() []ResourceType {
ResourceTypeLicense,
ResourceTypeWorkspaceProxy,
ResourceTypeConvertLogin,
+ ResourceTypeHealthSettings,
}
}
diff --git a/coderd/database/types.go b/coderd/database/types.go
index 5099733601f65..b1fb389d88794 100644
--- a/coderd/database/types.go
+++ b/coderd/database/types.go
@@ -23,6 +23,11 @@ type AuditOAuthConvertState struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
+type HealthSettings struct {
+ ID uuid.UUID `db:"id" json:"id"`
+ DismissedHealthchecks []string `db:"dismissed_healthchecks" json:"dismissed_healthchecks"`
+}
+
type Actions []rbac.Action
func (a *Actions) Scan(src interface{}) error {
diff --git a/coderd/debug.go b/coderd/debug.go
index ba6eaf9696d99..08916e1b4d37b 100644
--- a/coderd/debug.go
+++ b/coderd/debug.go
@@ -1,14 +1,24 @@
package coderd
import (
+ "bytes"
"context"
+ "encoding/json"
"fmt"
"net/http"
"time"
+ "golang.org/x/exp/slices"
+ "golang.org/x/xerrors"
+
+ "github.com/google/uuid"
+
+ "github.com/coder/coder/v2/coderd/audit"
+ "github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/healthcheck"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/coderd/httpmw"
+ "github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
)
@@ -107,6 +117,131 @@ func formatHealthcheck(ctx context.Context, rw http.ResponseWriter, r *http.Requ
}
}
+// @Summary Get health settings
+// @ID get-health-settings
+// @Security CoderSessionToken
+// @Produce json
+// @Tags Debug
+// @Success 200 {object} codersdk.HealthSettings
+// @Router /debug/health/settings [get]
+func (api *API) deploymentHealthSettings(rw http.ResponseWriter, r *http.Request) {
+ settingsJSON, err := api.Database.GetHealthSettings(r.Context())
+ if err != nil {
+ httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Failed to fetch health settings.",
+ Detail: err.Error(),
+ })
+ return
+ }
+
+ var settings codersdk.HealthSettings
+ err = json.Unmarshal([]byte(settingsJSON), &settings)
+ if err != nil {
+ httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Failed to unmarshal health settings.",
+ Detail: err.Error(),
+ })
+ return
+ }
+
+ if len(settings.DismissedHealthchecks) == 0 {
+ settings.DismissedHealthchecks = []string{}
+ }
+
+ httpapi.Write(r.Context(), rw, http.StatusOK, settings)
+}
+
+// @Summary Update health settings
+// @ID update-health-settings
+// @Security CoderSessionToken
+// @Accept json
+// @Produce json
+// @Tags Debug
+// @Param request body codersdk.UpdateHealthSettings true "Update health settings"
+// @Success 200 {object} codersdk.UpdateHealthSettings
+// @Router /debug/health/settings [put]
+func (api *API) putDeploymentHealthSettings(rw http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ if !api.Authorize(r, rbac.ActionUpdate, rbac.ResourceDeploymentValues) {
+ httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{
+ Message: "Insufficient permissions to update health settings.",
+ })
+ return
+ }
+
+ var settings codersdk.HealthSettings
+ if !httpapi.Read(ctx, rw, r, &settings) {
+ return
+ }
+
+ err := validateHealthSettings(settings)
+ if err != nil {
+ httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Failed to validate health settings.",
+ Detail: err.Error(),
+ })
+ return
+ }
+
+ settingsJSON, err := json.Marshal(&settings)
+ if err != nil {
+ httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Failed to marshal health settings.",
+ Detail: err.Error(),
+ })
+ return
+ }
+
+ currentSettingsJSON, err := api.Database.GetHealthSettings(r.Context())
+ if err != nil {
+ httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Failed to fetch current health settings.",
+ Detail: err.Error(),
+ })
+ return
+ }
+
+ if bytes.Equal(settingsJSON, []byte(currentSettingsJSON)) {
+ httpapi.Write(r.Context(), rw, http.StatusNotModified, nil)
+ return
+ }
+
+ auditor := api.Auditor.Load()
+ aReq, commitAudit := audit.InitRequest[database.HealthSettings](rw, &audit.RequestParams{
+ Audit: *auditor,
+ Log: api.Logger,
+ Request: r,
+ Action: database.AuditActionWrite,
+ })
+ defer commitAudit()
+ aReq.New = database.HealthSettings{
+ ID: uuid.New(),
+ DismissedHealthchecks: settings.DismissedHealthchecks,
+ }
+
+ err = api.Database.UpsertHealthSettings(ctx, string(settingsJSON))
+ if err != nil {
+ httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Failed to update health settings.",
+ Detail: err.Error(),
+ })
+ return
+ }
+
+ httpapi.Write(r.Context(), rw, http.StatusOK, settings)
+}
+
+func validateHealthSettings(settings codersdk.HealthSettings) error {
+ for _, dismissed := range settings.DismissedHealthchecks {
+ ok := slices.Contains(healthcheck.Sections, dismissed)
+ if !ok {
+ return xerrors.Errorf("unknown healthcheck section: %s", dismissed)
+ }
+ }
+ return nil
+}
+
// For some reason the swagger docs need to be attached to a function.
//
// @Summary Debug Info Websocket Test
diff --git a/coderd/debug_test.go b/coderd/debug_test.go
index 186539b82d90f..855827c34d0f4 100644
--- a/coderd/debug_test.go
+++ b/coderd/debug_test.go
@@ -14,6 +14,7 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/healthcheck"
"github.com/coder/coder/v2/coderd/healthcheck/derphealth"
+ "github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
)
@@ -232,6 +233,108 @@ func TestDebugHealth(t *testing.T) {
})
}
+func TestHealthSettings(t *testing.T) {
+ t.Parallel()
+
+ t.Run("InitialState", func(t *testing.T) {
+ t.Parallel()
+
+ ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
+ defer cancel()
+
+ // given
+ adminClient := coderdtest.New(t, nil)
+ _ = coderdtest.CreateFirstUser(t, adminClient)
+
+ // when
+ settings, err := adminClient.HealthSettings(ctx)
+ require.NoError(t, err)
+
+ // then
+ require.Equal(t, codersdk.HealthSettings{DismissedHealthchecks: []string{}}, settings)
+ })
+
+ t.Run("DismissSection", func(t *testing.T) {
+ t.Parallel()
+
+ ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
+ defer cancel()
+
+ // given
+ adminClient := coderdtest.New(t, nil)
+ _ = coderdtest.CreateFirstUser(t, adminClient)
+
+ expected := codersdk.HealthSettings{
+ DismissedHealthchecks: []string{healthcheck.SectionDERP, healthcheck.SectionWebsocket},
+ }
+
+ // when: dismiss "derp" and "websocket"
+ err := adminClient.PutHealthSettings(ctx, expected)
+ require.NoError(t, err)
+
+ // then
+ settings, err := adminClient.HealthSettings(ctx)
+ require.NoError(t, err)
+ require.Equal(t, expected, settings)
+ })
+
+ t.Run("UnDismissSection", func(t *testing.T) {
+ t.Parallel()
+
+ ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
+ defer cancel()
+
+ // given
+ adminClient := coderdtest.New(t, nil)
+ _ = coderdtest.CreateFirstUser(t, adminClient)
+
+ initial := codersdk.HealthSettings{
+ DismissedHealthchecks: []string{healthcheck.SectionDERP, healthcheck.SectionWebsocket},
+ }
+
+ err := adminClient.PutHealthSettings(ctx, initial)
+ require.NoError(t, err)
+
+ expected := codersdk.HealthSettings{
+ DismissedHealthchecks: []string{healthcheck.SectionDERP},
+ }
+
+ // when: undismiss "websocket"
+ err = adminClient.PutHealthSettings(ctx, expected)
+ require.NoError(t, err)
+
+ // then
+ settings, err := adminClient.HealthSettings(ctx)
+ require.NoError(t, err)
+ require.Equal(t, expected, settings)
+ })
+
+ t.Run("NotModified", func(t *testing.T) {
+ t.Parallel()
+
+ ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
+ defer cancel()
+
+ // given
+ adminClient := coderdtest.New(t, nil)
+ _ = coderdtest.CreateFirstUser(t, adminClient)
+
+ expected := codersdk.HealthSettings{
+ DismissedHealthchecks: []string{healthcheck.SectionDERP, healthcheck.SectionWebsocket},
+ }
+
+ err := adminClient.PutHealthSettings(ctx, expected)
+ require.NoError(t, err)
+
+ // when
+ err = adminClient.PutHealthSettings(ctx, expected)
+
+ // then
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "health settings not modified")
+ })
+}
+
func TestDebugWebsocket(t *testing.T) {
t.Parallel()
diff --git a/coderd/healthcheck/healthcheck.go b/coderd/healthcheck/healthcheck.go
index 9233626419634..9ecb9b9d13a44 100644
--- a/coderd/healthcheck/healthcheck.go
+++ b/coderd/healthcheck/healthcheck.go
@@ -20,6 +20,8 @@ const (
SectionWorkspaceProxy string = "WorkspaceProxy"
)
+var Sections = []string{SectionAccessURL, SectionDatabase, SectionDERP, SectionWebsocket, SectionWorkspaceProxy}
+
type Checker interface {
DERP(ctx context.Context, opts *derphealth.ReportOptions) derphealth.Report
AccessURL(ctx context.Context, opts *AccessURLReportOptions) AccessURLReport
diff --git a/codersdk/audit.go b/codersdk/audit.go
index 5ceae81a21c42..c1ea077ec0831 100644
--- a/codersdk/audit.go
+++ b/codersdk/audit.go
@@ -24,6 +24,7 @@ const (
ResourceTypeGroup ResourceType = "group"
ResourceTypeLicense ResourceType = "license"
ResourceTypeConvertLogin ResourceType = "convert_login"
+ ResourceTypeHealthSettings ResourceType = "health_settings"
ResourceTypeWorkspaceProxy ResourceType = "workspace_proxy"
ResourceTypeOrganization ResourceType = "organization"
)
@@ -56,6 +57,8 @@ func (r ResourceType) FriendlyString() string {
return "workspace proxy"
case ResourceTypeOrganization:
return "organization"
+ case ResourceTypeHealthSettings:
+ return "health_settings"
default:
return "unknown"
}
diff --git a/codersdk/health.go b/codersdk/health.go
new file mode 100644
index 0000000000000..ece12ba424771
--- /dev/null
+++ b/codersdk/health.go
@@ -0,0 +1,45 @@
+package codersdk
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+
+ "golang.org/x/xerrors"
+)
+
+type HealthSettings struct {
+ DismissedHealthchecks []string `json:"dismissed_healthchecks"`
+}
+
+type UpdateHealthSettings struct {
+ DismissedHealthchecks []string `json:"dismissed_healthchecks"`
+}
+
+func (c *Client) HealthSettings(ctx context.Context) (HealthSettings, error) {
+ res, err := c.Request(ctx, http.MethodGet, "/api/v2/debug/health/settings", nil)
+ if err != nil {
+ return HealthSettings{}, err
+ }
+ defer res.Body.Close()
+ if res.StatusCode != http.StatusOK {
+ return HealthSettings{}, ReadBodyAsError(res)
+ }
+ var settings HealthSettings
+ return settings, json.NewDecoder(res.Body).Decode(&settings)
+}
+
+func (c *Client) PutHealthSettings(ctx context.Context, settings HealthSettings) error {
+ res, err := c.Request(ctx, http.MethodPut, "/api/v2/debug/health/settings", settings)
+ if err != nil {
+ return err
+ }
+ defer res.Body.Close()
+ if res.StatusCode == http.StatusNotModified {
+ return xerrors.New("health settings not modified")
+ }
+ if res.StatusCode != http.StatusOK {
+ return ReadBodyAsError(res)
+ }
+ return nil
+}
diff --git a/docs/admin/audit-logs.md b/docs/admin/audit-logs.md
index 09ad0aae5bc80..527134f88befb 100644
--- a/docs/admin/audit-logs.md
+++ b/docs/admin/audit-logs.md
@@ -14,6 +14,7 @@ We track the following resources:
| AuditOAuthConvertState
|
Field | Tracked |
---|
created_at | true |
expires_at | true |
from_login_type | true |
to_login_type | true |
user_id | true |
|
| Group
create, write, delete | Field | Tracked |
---|
avatar_url | true |
display_name | true |
id | true |
members | true |
name | true |
organization_id | false |
quota_allowance | true |
source | false |
|
| GitSSHKey
create | Field | Tracked |
---|
created_at | false |
private_key | true |
public_key | true |
updated_at | false |
user_id | true |
|
+| HealthSettings
| Field | Tracked |
---|
dismissed_healthchecks | true |
id | false |
|
| License
create, delete | Field | Tracked |
---|
exp | true |
id | false |
jwt | false |
uploaded_at | true |
uuid | true |
|
| Template
write, delete | Field | Tracked |
---|
active_version_id | true |
allow_user_autostart | true |
allow_user_autostop | true |
allow_user_cancel_workspace_jobs | true |
autostart_block_days_of_week | true |
autostop_requirement_days_of_week | true |
autostop_requirement_weeks | true |
created_at | false |
created_by | true |
created_by_avatar_url | false |
created_by_username | false |
default_ttl | true |
deleted | false |
deprecated | true |
description | true |
display_name | true |
failure_ttl | true |
group_acl | true |
icon | true |
id | true |
max_ttl | true |
name | true |
organization_id | false |
provisioner | true |
require_active_version | true |
time_til_dormant | true |
time_til_dormant_autodelete | true |
updated_at | false |
user_acl | true |
|
| TemplateVersion
create, write | Field | Tracked |
---|
archived | true |
created_at | false |
created_by | true |
created_by_avatar_url | false |
created_by_username | false |
external_auth_providers | false |
id | true |
job_id | false |
message | false |
name | true |
organization_id | false |
readme | true |
template_id | true |
updated_at | false |
|
diff --git a/docs/api/debug.md b/docs/api/debug.md
index 60576c1c0ac62..de9348e1404e4 100644
--- a/docs/api/debug.md
+++ b/docs/api/debug.md
@@ -298,6 +298,83 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \
To perform this operation, you must be authenticated. [Learn more](authentication.md).
+## Get health settings
+
+### Code samples
+
+```shell
+# Example request using curl
+curl -X GET http://coder-server:8080/api/v2/debug/health/settings \
+ -H 'Accept: application/json' \
+ -H 'Coder-Session-Token: API_KEY'
+```
+
+`GET /debug/health/settings`
+
+### Example responses
+
+> 200 Response
+
+```json
+{
+ "dismissed_healthchecks": ["string"]
+}
+```
+
+### Responses
+
+| Status | Meaning | Description | Schema |
+| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------ |
+| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.HealthSettings](schemas.md#codersdkhealthsettings) |
+
+To perform this operation, you must be authenticated. [Learn more](authentication.md).
+
+## Update health settings
+
+### Code samples
+
+```shell
+# Example request using curl
+curl -X PUT http://coder-server:8080/api/v2/debug/health/settings \
+ -H 'Content-Type: application/json' \
+ -H 'Accept: application/json' \
+ -H 'Coder-Session-Token: API_KEY'
+```
+
+`PUT /debug/health/settings`
+
+> Body parameter
+
+```json
+{
+ "dismissed_healthchecks": ["string"]
+}
+```
+
+### Parameters
+
+| Name | In | Type | Required | Description |
+| ------ | ---- | ------------------------------------------------------------------------ | -------- | ---------------------- |
+| `body` | body | [codersdk.UpdateHealthSettings](schemas.md#codersdkupdatehealthsettings) | true | Update health settings |
+
+### Example responses
+
+> 200 Response
+
+```json
+{
+ "dismissed_healthchecks": ["string"]
+}
+```
+
+### Responses
+
+| Status | Meaning | Description | Schema |
+| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------ |
+| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UpdateHealthSettings](schemas.md#codersdkupdatehealthsettings) |
+
+To perform this operation, you must be authenticated. [Learn more](authentication.md).
+
## Debug Info Tailnet
### Code samples
diff --git a/docs/api/schemas.md b/docs/api/schemas.md
index 1d1834a621764..6de323c0968fe 100644
--- a/docs/api/schemas.md
+++ b/docs/api/schemas.md
@@ -3166,6 +3166,20 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `user` |
| `oidc` |
+## codersdk.HealthSettings
+
+```json
+{
+ "dismissed_healthchecks": ["string"]
+}
+```
+
+### Properties
+
+| Name | Type | Required | Restrictions | Description |
+| ------------------------ | --------------- | -------- | ------------ | ----------- |
+| `dismissed_healthchecks` | array of string | false | | |
+
## codersdk.Healthcheck
```json
@@ -4162,6 +4176,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `group` |
| `license` |
| `convert_login` |
+| `health_settings` |
| `workspace_proxy` |
| `organization` |
@@ -5158,6 +5173,20 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `url` | string | false | | URL to download the latest release of Coder. |
| `version` | string | false | | Version is the semantic version for the latest release of Coder. |
+## codersdk.UpdateHealthSettings
+
+```json
+{
+ "dismissed_healthchecks": ["string"]
+}
+```
+
+### Properties
+
+| Name | Type | Required | Restrictions | Description |
+| ------------------------ | --------------- | -------- | ------------ | ----------- |
+| `dismissed_healthchecks` | array of string | false | | |
+
## codersdk.UpdateRoles
```json
diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go
index 085f4ac581a9e..dcd775cf4c1f3 100644
--- a/enterprise/audit/table.go
+++ b/enterprise/audit/table.go
@@ -184,6 +184,10 @@ var auditableResourcesTypes = map[any]map[string]Action{
"to_login_type": ActionTrack,
"user_id": ActionTrack,
},
+ &database.HealthSettings{}: {
+ "id": ActionIgnore,
+ "dismissed_healthchecks": ActionTrack,
+ },
// TODO: track an ID here when the below ticket is completed:
// https://github.com/coder/coder/pull/6012
&database.License{}: {
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index 14f4993085215..901f0c213bb02 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -542,6 +542,11 @@ export interface Group {
readonly source: GroupSource;
}
+// From codersdk/health.go
+export interface HealthSettings {
+ readonly dismissed_healthchecks: string[];
+}
+
// From codersdk/workspaceapps.go
export interface Healthcheck {
readonly url: string;
@@ -1155,6 +1160,11 @@ export interface UpdateCheckResponse {
readonly url: string;
}
+// From codersdk/health.go
+export interface UpdateHealthSettings {
+ readonly dismissed_healthchecks: string[];
+}
+
// From codersdk/users.go
export interface UpdateRoles {
readonly roles: string[];
@@ -1920,6 +1930,7 @@ export type ResourceType =
| "convert_login"
| "git_ssh_key"
| "group"
+ | "health_settings"
| "license"
| "organization"
| "template"
@@ -1933,6 +1944,7 @@ export const ResourceTypes: ResourceType[] = [
"convert_login",
"git_ssh_key",
"group",
+ "health_settings",
"license",
"organization",
"template",