feat: manage health settings using Coder API (#10861)

This commit is contained in:
Marcin Tojek 2023-11-28 18:15:17 +01:00 committed by GitHub
parent 452668c893
commit 19b6d194fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 603 additions and 4 deletions

86
coderd/apidoc/docs.go generated
View File

@ -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": {

View File

@ -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": {

View File

@ -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

View File

@ -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))
}

View File

@ -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))

View File

@ -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 (

View File

@ -0,0 +1 @@
-- Nothing to do

View File

@ -0,0 +1,2 @@
-- This has to be outside a transaction
ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'health_settings';

View File

@ -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,
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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"
}

45
codersdk/health.go Normal file
View File

@ -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
}

View File

@ -14,6 +14,7 @@ We track the following resources:
| AuditOAuthConvertState<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>from_login_type</td><td>true</td></tr><tr><td>to_login_type</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| Group<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>members</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>quota_allowance</td><td>true</td></tr><tr><td>source</td><td>false</td></tr></tbody></table> |
| GitSSHKey<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| HealthSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>dismissed_healthchecks</td><td>true</td></tr><tr><td>id</td><td>false</td></tr></tbody></table> |
| License<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>exp</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwt</td><td>false</td></tr><tr><td>uploaded_at</td><td>true</td></tr><tr><td>uuid</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>active_version_id</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>autostart_block_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deprecated</td><td>true</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_ttl</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>require_active_version</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>archived</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>external_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>message</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |

77
docs/api/debug.md generated
View File

@ -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

29
docs/api/schemas.md generated
View File

@ -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

View File

@ -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{}: {

View File

@ -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",