From 377ca047977f8481483ca96fcf4d603aba2b35c4 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 8 Jan 2024 11:49:16 +0000 Subject: [PATCH 1/2] fix(coderd/healthcheck): scope warnings in ProvisionerDaemonsReport - Sorts provisioner daemons by name ascending in output - Adds daemon-specific warnings to healthcheck output - Reword some messages --- coderd/apidoc/docs.go | 18 +- coderd/apidoc/swagger.json | 18 +- coderd/healthcheck/provisioner.go | 38 ++++- coderd/healthcheck/provisioner_test.go | 218 +++++++++++++++++++++++-- docs/api/debug.md | 30 ++-- docs/api/schemas.md | 107 ++++++++---- site/src/api/typesGenerated.ts | 8 +- site/src/testHelpers/entities.ts | 58 +++++-- 8 files changed, 412 insertions(+), 83 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 80da058186351..eefbeb628ba32 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -13109,10 +13109,10 @@ const docTemplate = `{ "error": { "type": "string" }, - "provisioner_daemons": { + "items": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.ProvisionerDaemon" + "$ref": "#/definitions/healthcheck.ProvisionerDaemonsReportItem" } }, "severity": { @@ -13126,6 +13126,20 @@ const docTemplate = `{ } } }, + "healthcheck.ProvisionerDaemonsReportItem": { + "type": "object", + "properties": { + "provisioner_daemon": { + "$ref": "#/definitions/codersdk.ProvisionerDaemon" + }, + "warnings": { + "type": "array", + "items": { + "$ref": "#/definitions/health.Message" + } + } + } + }, "healthcheck.Report": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index d0d440c15255b..595ad23b695ae 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -11933,10 +11933,10 @@ "error": { "type": "string" }, - "provisioner_daemons": { + "items": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.ProvisionerDaemon" + "$ref": "#/definitions/healthcheck.ProvisionerDaemonsReportItem" } }, "severity": { @@ -11950,6 +11950,20 @@ } } }, + "healthcheck.ProvisionerDaemonsReportItem": { + "type": "object", + "properties": { + "provisioner_daemon": { + "$ref": "#/definitions/codersdk.ProvisionerDaemon" + }, + "warnings": { + "type": "array", + "items": { + "$ref": "#/definitions/health.Message" + } + } + } + }, "healthcheck.Report": { "type": "object", "properties": { diff --git a/coderd/healthcheck/provisioner.go b/coderd/healthcheck/provisioner.go index bbbd9d2bedd35..9dc38ba99aa32 100644 --- a/coderd/healthcheck/provisioner.go +++ b/coderd/healthcheck/provisioner.go @@ -2,6 +2,7 @@ package healthcheck import ( "context" + "sort" "time" "golang.org/x/mod/semver" @@ -26,7 +27,13 @@ type ProvisionerDaemonsReport struct { Dismissed bool `json:"dismissed"` Error *string `json:"error"` - ProvisionerDaemons []codersdk.ProvisionerDaemon `json:"provisioner_daemons"` + Items []ProvisionerDaemonsReportItem `json:"items"` +} + +// @typescript-generate ProvisionerDaemonsReportItem +type ProvisionerDaemonsReportItem struct { + codersdk.ProvisionerDaemon `json:"provisioner_daemon"` + Warnings []health.Message `json:"warnings"` } type ProvisionerDaemonsReportDeps struct { @@ -47,7 +54,7 @@ type ProvisionerDaemonsStore interface { } func (r *ProvisionerDaemonsReport) Run(ctx context.Context, opts *ProvisionerDaemonsReportDeps) { - r.ProvisionerDaemons = make([]codersdk.ProvisionerDaemon, 0) + r.Items = make([]ProvisionerDaemonsReportItem, 0) r.Severity = health.SeverityOK r.Warnings = make([]health.Message, 0) r.Dismissed = opts.Dismissed @@ -86,6 +93,12 @@ func (r *ProvisionerDaemonsReport) Run(ctx context.Context, opts *ProvisionerDae r.Error = ptr.Ref("error fetching provisioner daemons: " + err.Error()) return } + + // Ensure stable order for display and for tests + sort.Slice(daemons, func(i, j int) bool { + return daemons[i].Name < daemons[j].Name + }) + for _, daemon := range daemons { // Daemon never connected, skip. if !daemon.LastSeenAt.Valid { @@ -96,19 +109,24 @@ func (r *ProvisionerDaemonsReport) Run(ctx context.Context, opts *ProvisionerDae continue } - r.ProvisionerDaemons = append(r.ProvisionerDaemons, db2sdk.ProvisionerDaemon(daemon)) + it := ProvisionerDaemonsReportItem{ + ProvisionerDaemon: db2sdk.ProvisionerDaemon(daemon), + Warnings: make([]health.Message, 0), + } // For release versions, just check MAJOR.MINOR and ignore patch. if !semver.IsValid(daemon.Version) { if r.Severity.Value() < health.SeverityError.Value() { r.Severity = health.SeverityError } - r.Warnings = append(r.Warnings, health.Messagef(health.CodeUnknown, "Provisioner daemon %q reports invalid version %q", opts.CurrentVersion, daemon.Version)) + r.Warnings = append(r.Warnings, health.Messagef(health.CodeUnknown, "Some provisioner daemons report invalid version information.")) + it.Warnings = append(it.Warnings, health.Messagef(health.CodeUnknown, "Invalid version %q", daemon.Version)) } else if !buildinfo.VersionsMatch(opts.CurrentVersion, daemon.Version) { if r.Severity.Value() < health.SeverityWarning.Value() { r.Severity = health.SeverityWarning } - r.Warnings = append(r.Warnings, health.Messagef(health.CodeProvisionerDaemonVersionMismatch, "Provisioner daemon %q has outdated version %q", daemon.Name, daemon.Version)) + r.Warnings = append(r.Warnings, health.Messagef(health.CodeProvisionerDaemonVersionMismatch, "Some provisioner daemons report mismatched versions.")) + it.Warnings = append(it.Warnings, health.Messagef(health.CodeProvisionerDaemonVersionMismatch, "Mismatched version %q", daemon.Version)) } // Provisioner daemon API version follows different rules; we just want to check the major API version and @@ -119,16 +137,20 @@ func (r *ProvisionerDaemonsReport) Run(ctx context.Context, opts *ProvisionerDae if r.Severity.Value() < health.SeverityError.Value() { r.Severity = health.SeverityError } - r.Warnings = append(r.Warnings, health.Messagef(health.CodeUnknown, "Provisioner daemon %q reports invalid API version: %s", daemon.Name, err.Error())) + r.Warnings = append(r.Warnings, health.Messagef(health.CodeUnknown, "Some provisioner daemons reports invalid API version information")) + it.Warnings = append(it.Warnings, health.Messagef(health.CodeUnknown, "Invalid API version: %s", err.Error())) // contains version string } else if maj != opts.CurrentAPIMajorVersion { if r.Severity.Value() < health.SeverityWarning.Value() { r.Severity = health.SeverityWarning } - r.Warnings = append(r.Warnings, health.Messagef(health.CodeProvisionerDaemonAPIMajorVersionDeprecated, "Provisioner daemon %q reports deprecated major API version %d. Consider upgrading!", daemon.Name, provisionersdk.CurrentMajor)) + r.Warnings = append(r.Warnings, health.Messagef(health.CodeProvisionerDaemonAPIMajorVersionDeprecated, "Some provisioner daemons report deprecated major API versions. Consider upgrading!")) + it.Warnings = append(it.Warnings, health.Messagef(health.CodeProvisionerDaemonAPIMajorVersionDeprecated, "Deprecated major API version %d.", provisionersdk.CurrentMajor)) } + + r.Items = append(r.Items, it) } - if len(r.ProvisionerDaemons) == 0 { + if len(r.Items) == 0 { r.Severity = health.SeverityError r.Error = ptr.Ref("No active provisioner daemons found!") return diff --git a/coderd/healthcheck/provisioner_test.go b/coderd/healthcheck/provisioner_test.go index 27c5293d70309..884f8e2cc30ba 100644 --- a/coderd/healthcheck/provisioner_test.go +++ b/coderd/healthcheck/provisioner_test.go @@ -14,6 +14,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/healthcheck" "github.com/coder/coder/v2/coderd/healthcheck/health" + "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/provisionersdk" gomock "go.uber.org/mock/gomock" @@ -22,6 +23,8 @@ import ( func TestProvisionerDaemonReport(t *testing.T) { t.Parallel() + now := dbtime.Now() + for _, tt := range []struct { name string currentVersion string @@ -31,12 +34,14 @@ func TestProvisionerDaemonReport(t *testing.T) { expectedSeverity health.Severity expectedWarningCode health.Code expectedError string + expectedItems []healthcheck.ProvisionerDaemonsReportItem }{ { name: "current version empty", currentVersion: "", expectedSeverity: health.SeverityError, expectedError: "Developer error: CurrentVersion is empty", + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{}, }, { name: "no daemons", @@ -44,6 +49,7 @@ func TestProvisionerDaemonReport(t *testing.T) { currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityError, expectedError: "No active provisioner daemons found!", + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{}, }, { name: "error fetching daemons", @@ -52,13 +58,29 @@ func TestProvisionerDaemonReport(t *testing.T) { provisionerDaemonsErr: assert.AnError, expectedSeverity: health.SeverityError, expectedError: assert.AnError.Error(), + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{}, }, { name: "one daemon up to date", currentVersion: "v1.2.3", currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityOK, - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0")}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0", now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{ + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-ok", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v1.2.3", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{}, + }, + }, }, { name: "one daemon out of date", @@ -66,7 +88,27 @@ func TestProvisionerDaemonReport(t *testing.T) { currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityWarning, expectedWarningCode: health.CodeProvisionerDaemonVersionMismatch, - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-old", "v1.1.2", "1.0")}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-old", "v1.1.2", "1.0", now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{ + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-old", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v1.1.2", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{ + { + Code: health.CodeProvisionerDaemonVersionMismatch, + Message: `Mismatched version "v1.1.2"`, + }, + }, + }, + }, }, { name: "invalid daemon version", @@ -74,7 +116,27 @@ func TestProvisionerDaemonReport(t *testing.T) { currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityError, expectedWarningCode: health.CodeUnknown, - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-invalid-version", "invalid", "1.0")}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-invalid-version", "invalid", "1.0", now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{ + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-invalid-version", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "invalid", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{ + { + Code: health.CodeUnknown, + Message: `Invalid version "invalid"`, + }, + }, + }, + }, }, { name: "invalid daemon api version", @@ -82,7 +144,27 @@ func TestProvisionerDaemonReport(t *testing.T) { currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityError, expectedWarningCode: health.CodeUnknown, - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-new-minor", "v1.2.3", "invalid")}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-invalid-api", "v1.2.3", "invalid", now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{ + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-invalid-api", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v1.2.3", + APIVersion: "invalid", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{ + { + Code: health.CodeUnknown, + Message: `Invalid API version: invalid version string: invalid`, + }, + }, + }, + }, }, { name: "api version backward compat", @@ -90,7 +172,27 @@ func TestProvisionerDaemonReport(t *testing.T) { currentAPIMajorVersion: 2, expectedSeverity: health.SeverityWarning, expectedWarningCode: health.CodeProvisionerDaemonAPIMajorVersionDeprecated, - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-old-api", "v2.3.4", "1.0")}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-old-api", "v2.3.4", "1.0", now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{ + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-old-api", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v2.3.4", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{ + { + Code: health.CodeProvisionerDaemonAPIMajorVersionDeprecated, + Message: "Deprecated major API version 1.", + }, + }, + }, + }, }, { name: "one up to date, one out of date", @@ -98,7 +200,40 @@ func TestProvisionerDaemonReport(t *testing.T) { currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityWarning, expectedWarningCode: health.CodeProvisionerDaemonVersionMismatch, - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0"), fakeProvisionerDaemon(t, "pd-old", "v1.1.2", "1.0")}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0", now), fakeProvisionerDaemon(t, "pd-old", "v1.1.2", "1.0", now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{ + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-ok", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v1.2.3", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{}, + }, + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-old", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v1.1.2", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{ + { + Code: health.CodeProvisionerDaemonVersionMismatch, + Message: `Mismatched version "v1.1.2"`, + }, + }, + }, + }, }, { name: "one up to date, one newer", @@ -106,14 +241,62 @@ func TestProvisionerDaemonReport(t *testing.T) { currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityWarning, expectedWarningCode: health.CodeProvisionerDaemonVersionMismatch, - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0"), fakeProvisionerDaemon(t, "pd-new", "v2.3.4", "1.0")}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0", now), fakeProvisionerDaemon(t, "pd-new", "v2.3.4", "1.0", now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{ + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-new", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v2.3.4", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{ + { + Code: health.CodeProvisionerDaemonVersionMismatch, + Message: `Mismatched version "v2.3.4"`, + }, + }, + }, + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-ok", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v1.2.3", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{}, + }, + }, }, { name: "one up to date, one stale older", currentVersion: "v2.3.4", currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityOK, - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemonStale(t, "pd-ok", "v1.2.3", "0.9", dbtime.Now().Add(-5*time.Minute)), fakeProvisionerDaemon(t, "pd-new", "v2.3.4", "1.0")}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemonStale(t, "pd-stale", "v1.2.3", "0.9", now.Add(-5*time.Minute), now), fakeProvisionerDaemon(t, "pd-ok", "v2.3.4", "1.0", now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{ + { + ProvisionerDaemon: codersdk.ProvisionerDaemon{ + ID: uuid.Nil, + Name: "pd-ok", + CreatedAt: now, + LastSeenAt: codersdk.NewNullTime(now, true), + Version: "v2.3.4", + APIVersion: "1.0", + Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho, codersdk.ProvisionerTypeTerraform}, + Tags: map[string]string{}, + }, + Warnings: []health.Message{}, + }, + }, }, { name: "one stale", @@ -121,7 +304,8 @@ func TestProvisionerDaemonReport(t *testing.T) { currentAPIMajorVersion: provisionersdk.CurrentMajor, expectedSeverity: health.SeverityError, expectedError: "No active provisioner daemons found!", - provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemonStale(t, "pd-ok", "v1.2.3", "0.9", dbtime.Now().Add(-5*time.Minute))}, + provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemonStale(t, "pd-ok", "v1.2.3", "0.9", now.Add(-5*time.Minute), now)}, + expectedItems: []healthcheck.ProvisionerDaemonsReportItem{}, }, } { tt := tt @@ -135,7 +319,6 @@ func TestProvisionerDaemonReport(t *testing.T) { if tt.currentAPIMajorVersion == 0 { deps.CurrentAPIMajorVersion = provisionersdk.CurrentMajor } - now := dbtime.Now() deps.TimeNow = func() time.Time { return now } @@ -163,17 +346,20 @@ func TestProvisionerDaemonReport(t *testing.T) { if tt.expectedError != "" && assert.NotNil(t, rpt.Error) { assert.Contains(t, *rpt.Error, tt.expectedError) } + if tt.expectedItems != nil { + assert.Equal(t, tt.expectedItems, rpt.Items) + } }) } } -func fakeProvisionerDaemon(t *testing.T, name, version, apiVersion string) database.ProvisionerDaemon { +func fakeProvisionerDaemon(t *testing.T, name, version, apiVersion string, now time.Time) database.ProvisionerDaemon { t.Helper() return database.ProvisionerDaemon{ - ID: uuid.New(), + ID: uuid.Nil, Name: name, - CreatedAt: dbtime.Now(), - LastSeenAt: sql.NullTime{Time: dbtime.Now(), Valid: true}, + CreatedAt: now, + LastSeenAt: sql.NullTime{Time: now, Valid: true}, Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho, database.ProvisionerTypeTerraform}, ReplicaID: uuid.NullUUID{}, Tags: map[string]string{}, @@ -182,9 +368,9 @@ func fakeProvisionerDaemon(t *testing.T, name, version, apiVersion string) datab } } -func fakeProvisionerDaemonStale(t *testing.T, name, version, apiVersion string, lastSeenAt time.Time) database.ProvisionerDaemon { +func fakeProvisionerDaemonStale(t *testing.T, name, version, apiVersion string, lastSeenAt, now time.Time) database.ProvisionerDaemon { t.Helper() - d := fakeProvisionerDaemon(t, name, version, apiVersion) + d := fakeProvisionerDaemon(t, name, version, apiVersion, now) d.LastSeenAt.Valid = true d.LastSeenAt.Time = lastSeenAt return d diff --git a/docs/api/debug.md b/docs/api/debug.md index 3668a886c3a0d..7b84457ad292d 100644 --- a/docs/api/debug.md +++ b/docs/api/debug.md @@ -285,19 +285,27 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "provisioner_daemons": { "dismissed": true, "error": "string", - "provisioner_daemons": [ + "items": [ { - "api_version": "string", - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "last_seen_at": "2019-08-24T14:15:22Z", - "name": "string", - "provisioners": ["string"], - "tags": { - "property1": "string", - "property2": "string" + "provisioner_daemon": { + "api_version": "string", + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "name": "string", + "provisioners": ["string"], + "tags": { + "property1": "string", + "property2": "string" + }, + "version": "string" }, - "version": "string" + "warnings": [ + { + "code": "EUNKNOWN", + "message": "string" + } + ] } ], "severity": "ok", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 8b653c7286d28..e63452bd15134 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -7900,19 +7900,27 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "dismissed": true, "error": "string", - "provisioner_daemons": [ + "items": [ { - "api_version": "string", - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "last_seen_at": "2019-08-24T14:15:22Z", - "name": "string", - "provisioners": ["string"], - "tags": { - "property1": "string", - "property2": "string" + "provisioner_daemon": { + "api_version": "string", + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "name": "string", + "provisioners": ["string"], + "tags": { + "property1": "string", + "property2": "string" + }, + "version": "string" }, - "version": "string" + "warnings": [ + { + "code": "EUNKNOWN", + "message": "string" + } + ] } ], "severity": "ok", @@ -7927,13 +7935,46 @@ If the schedule is empty, the user will be updated to use the default schedule.| ### Properties -| Name | Type | Required | Restrictions | Description | -| --------------------- | ----------------------------------------------------------------- | -------- | ------------ | ----------- | -| `dismissed` | boolean | false | | | -| `error` | string | false | | | -| `provisioner_daemons` | array of [codersdk.ProvisionerDaemon](#codersdkprovisionerdaemon) | false | | | -| `severity` | [health.Severity](#healthseverity) | false | | | -| `warnings` | array of [health.Message](#healthmessage) | false | | | +| Name | Type | Required | Restrictions | Description | +| ----------- | --------------------------------------------------------------------------------------------- | -------- | ------------ | ----------- | +| `dismissed` | boolean | false | | | +| `error` | string | false | | | +| `items` | array of [healthcheck.ProvisionerDaemonsReportItem](#healthcheckprovisionerdaemonsreportitem) | false | | | +| `severity` | [health.Severity](#healthseverity) | false | | | +| `warnings` | array of [health.Message](#healthmessage) | false | | | + +## healthcheck.ProvisionerDaemonsReportItem + +```json +{ + "provisioner_daemon": { + "api_version": "string", + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "name": "string", + "provisioners": ["string"], + "tags": { + "property1": "string", + "property2": "string" + }, + "version": "string" + }, + "warnings": [ + { + "code": "EUNKNOWN", + "message": "string" + } + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------------------- | -------------------------------------------------------- | -------- | ------------ | ----------- | +| `provisioner_daemon` | [codersdk.ProvisionerDaemon](#codersdkprovisionerdaemon) | false | | | +| `warnings` | array of [health.Message](#healthmessage) | false | | | ## healthcheck.Report @@ -8179,19 +8220,27 @@ If the schedule is empty, the user will be updated to use the default schedule.| "provisioner_daemons": { "dismissed": true, "error": "string", - "provisioner_daemons": [ + "items": [ { - "api_version": "string", - "created_at": "2019-08-24T14:15:22Z", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "last_seen_at": "2019-08-24T14:15:22Z", - "name": "string", - "provisioners": ["string"], - "tags": { - "property1": "string", - "property2": "string" + "provisioner_daemon": { + "api_version": "string", + "created_at": "2019-08-24T14:15:22Z", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "name": "string", + "provisioners": ["string"], + "tags": { + "property1": "string", + "property2": "string" + }, + "version": "string" }, - "version": "string" + "warnings": [ + { + "code": "EUNKNOWN", + "message": "string" + } + ] } ], "severity": "ok", diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 37c671cbfa2ab..da9e0c733ee73 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2211,7 +2211,13 @@ export interface HealthcheckProvisionerDaemonsReport { readonly warnings: HealthMessage[]; readonly dismissed: boolean; readonly error?: string; - readonly provisioner_daemons: ProvisionerDaemon[]; + readonly items: HealthcheckProvisionerDaemonsReportItem[]; +} + +// From healthcheck/provisioner.go +export interface HealthcheckProvisionerDaemonsReportItem { + readonly provisioner_daemon: ProvisionerDaemon; + readonly warnings: HealthMessage[]; } // From healthcheck/healthcheck.go diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 5ef64845cb430..dc2d107802a2d 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -3105,19 +3105,22 @@ export const MockHealth: TypesGen.HealthcheckReport = { severity: "ok", warnings: [], dismissed: false, - provisioner_daemons: [ + items: [ { - id: "e455b582-ac04-4323-9ad6-ab71301fa006", - created_at: "2024-01-04T15:53:03.21563Z", - last_seen_at: "2024-01-04T16:05:03.967551Z", - name: "vvuurrkk-2", - version: "v2.6.0-devel+965ad5e96", - api_version: "1.0", - provisioners: ["echo", "terraform"], - tags: { - owner: "", - scope: "organization", + provisioner_daemon: { + id: "e455b582-ac04-4323-9ad6-ab71301fa006", + created_at: "2024-01-04T15:53:03.21563Z", + last_seen_at: "2024-01-04T16:05:03.967551Z", + name: "vvuurrkk-2", + version: "v2.6.0-devel+965ad5e96", + api_version: "1.0", + provisioners: ["echo", "terraform"], + tags: { + owner: "", + scope: "organization", + }, }, + warnings: [], }, ], }, @@ -3211,10 +3214,37 @@ export const DeploymentHealthUnhealthy: TypesGen.HealthcheckReport = { }, provisioner_daemons: { severity: "error", - error: "something went wrong lol", - warnings: [], + error: "something went wrong", + warnings: [ + { + message: "this is a message", + code: "EUNKNOWN", + }, + ], dismissed: false, - provisioner_daemons: [], + items: [ + { + provisioner_daemon: { + id: "e455b582-ac04-4323-9ad6-ab71301fa006", + created_at: "2024-01-04T15:53:03.21563Z", + last_seen_at: "2024-01-04T16:05:03.967551Z", + name: "vvuurrkk-2", + version: "v2.6.0-devel+965ad5e96", + api_version: "1.0", + provisioners: ["echo", "terraform"], + tags: { + owner: "", + scope: "organization", + }, + }, + warnings: [ + { + message: "this is a specific message for this thing", + code: "EUNKNOWN", + }, + ], + }, + ], }, }; From b0977b84bf0e89e6ec8a73ab5a13f131d7efa4f9 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 8 Jan 2024 13:42:49 +0000 Subject: [PATCH 2/2] fix msg --- coderd/healthcheck/provisioner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/healthcheck/provisioner.go b/coderd/healthcheck/provisioner.go index 9dc38ba99aa32..4e467be0d5015 100644 --- a/coderd/healthcheck/provisioner.go +++ b/coderd/healthcheck/provisioner.go @@ -137,7 +137,7 @@ func (r *ProvisionerDaemonsReport) Run(ctx context.Context, opts *ProvisionerDae if r.Severity.Value() < health.SeverityError.Value() { r.Severity = health.SeverityError } - r.Warnings = append(r.Warnings, health.Messagef(health.CodeUnknown, "Some provisioner daemons reports invalid API version information")) + r.Warnings = append(r.Warnings, health.Messagef(health.CodeUnknown, "Some provisioner daemons report invalid API version information.")) it.Warnings = append(it.Warnings, health.Messagef(health.CodeUnknown, "Invalid API version: %s", err.Error())) // contains version string } else if maj != opts.CurrentAPIMajorVersion { if r.Severity.Value() < health.SeverityWarning.Value() {