mirror of https://github.com/coder/coder.git
378 lines
13 KiB
Go
378 lines
13 KiB
Go
package healthcheck_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
gomock "go.uber.org/mock/gomock"
|
|
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/database/dbmock"
|
|
"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/codersdk/healthsdk"
|
|
"github.com/coder/coder/v2/provisionerd/proto"
|
|
)
|
|
|
|
func TestProvisionerDaemonReport(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := dbtime.Now()
|
|
|
|
for _, tt := range []struct {
|
|
name string
|
|
currentVersion string
|
|
currentAPIMajorVersion int
|
|
provisionerDaemons []database.ProvisionerDaemon
|
|
provisionerDaemonsErr error
|
|
expectedSeverity health.Severity
|
|
expectedWarningCode health.Code
|
|
expectedError string
|
|
expectedItems []healthsdk.ProvisionerDaemonsReportItem
|
|
}{
|
|
{
|
|
name: "current version empty",
|
|
currentVersion: "",
|
|
expectedSeverity: health.SeverityError,
|
|
expectedError: "Developer error: CurrentVersion is empty",
|
|
expectedItems: []healthsdk.ProvisionerDaemonsReportItem{},
|
|
},
|
|
{
|
|
name: "no daemons",
|
|
currentVersion: "v1.2.3",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedItems: []healthsdk.ProvisionerDaemonsReportItem{},
|
|
expectedWarningCode: health.CodeProvisionerDaemonsNoProvisionerDaemons,
|
|
},
|
|
{
|
|
name: "error fetching daemons",
|
|
currentVersion: "v1.2.3",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
provisionerDaemonsErr: assert.AnError,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedError: assert.AnError.Error(),
|
|
expectedItems: []healthsdk.ProvisionerDaemonsReportItem{},
|
|
},
|
|
{
|
|
name: "one daemon up to date",
|
|
currentVersion: "v1.2.3",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityOK,
|
|
provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0", now)},
|
|
expectedItems: []healthsdk.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",
|
|
currentVersion: "v1.2.3",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityWarning,
|
|
expectedWarningCode: health.CodeProvisionerDaemonVersionMismatch,
|
|
provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-old", "v1.1.2", "1.0", now)},
|
|
expectedItems: []healthsdk.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",
|
|
currentVersion: "v1.2.3",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedWarningCode: health.CodeUnknown,
|
|
provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-invalid-version", "invalid", "1.0", now)},
|
|
expectedItems: []healthsdk.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",
|
|
currentVersion: "v1.2.3",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedWarningCode: health.CodeUnknown,
|
|
provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-invalid-api", "v1.2.3", "invalid", now)},
|
|
expectedItems: []healthsdk.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",
|
|
currentVersion: "v2.3.4",
|
|
currentAPIMajorVersion: 2,
|
|
expectedSeverity: health.SeverityWarning,
|
|
expectedWarningCode: health.CodeProvisionerDaemonAPIMajorVersionDeprecated,
|
|
provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-old-api", "v2.3.4", "1.0", now)},
|
|
expectedItems: []healthsdk.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",
|
|
currentVersion: "v1.2.3",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityWarning,
|
|
expectedWarningCode: health.CodeProvisionerDaemonVersionMismatch,
|
|
provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0", now), fakeProvisionerDaemon(t, "pd-old", "v1.1.2", "1.0", now)},
|
|
expectedItems: []healthsdk.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",
|
|
currentVersion: "v1.2.3",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityWarning,
|
|
expectedWarningCode: health.CodeProvisionerDaemonVersionMismatch,
|
|
provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemon(t, "pd-ok", "v1.2.3", "1.0", now), fakeProvisionerDaemon(t, "pd-new", "v2.3.4", "1.0", now)},
|
|
expectedItems: []healthsdk.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: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityOK,
|
|
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: []healthsdk.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",
|
|
currentVersion: "v2.3.4",
|
|
currentAPIMajorVersion: proto.CurrentMajor,
|
|
expectedSeverity: health.SeverityError,
|
|
expectedWarningCode: health.CodeProvisionerDaemonsNoProvisionerDaemons,
|
|
provisionerDaemons: []database.ProvisionerDaemon{fakeProvisionerDaemonStale(t, "pd-ok", "v1.2.3", "0.9", now.Add(-5*time.Minute), now)},
|
|
expectedItems: []healthsdk.ProvisionerDaemonsReportItem{},
|
|
},
|
|
} {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var rpt healthcheck.ProvisionerDaemonsReport
|
|
var deps healthcheck.ProvisionerDaemonsReportDeps
|
|
deps.CurrentVersion = tt.currentVersion
|
|
deps.CurrentAPIMajorVersion = tt.currentAPIMajorVersion
|
|
if tt.currentAPIMajorVersion == 0 {
|
|
deps.CurrentAPIMajorVersion = proto.CurrentMajor
|
|
}
|
|
deps.TimeNow = func() time.Time {
|
|
return now
|
|
}
|
|
|
|
ctrl := gomock.NewController(t)
|
|
mDB := dbmock.NewMockStore(ctrl)
|
|
mDB.EXPECT().GetProvisionerDaemons(gomock.Any()).AnyTimes().Return(tt.provisionerDaemons, tt.provisionerDaemonsErr)
|
|
deps.Store = mDB
|
|
|
|
rpt.Run(context.Background(), &deps)
|
|
|
|
assert.Equal(t, tt.expectedSeverity, rpt.Severity)
|
|
if tt.expectedWarningCode != "" && assert.NotEmpty(t, rpt.Warnings) {
|
|
var found bool
|
|
for _, w := range rpt.Warnings {
|
|
if w.Code == tt.expectedWarningCode {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "expected warning %s not found in %v", tt.expectedWarningCode, rpt.Warnings)
|
|
} else {
|
|
assert.Empty(t, rpt.Warnings)
|
|
}
|
|
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, now time.Time) database.ProvisionerDaemon {
|
|
t.Helper()
|
|
return database.ProvisionerDaemon{
|
|
ID: uuid.Nil,
|
|
Name: name,
|
|
CreatedAt: now,
|
|
LastSeenAt: sql.NullTime{Time: now, Valid: true},
|
|
Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho, database.ProvisionerTypeTerraform},
|
|
ReplicaID: uuid.NullUUID{},
|
|
Tags: map[string]string{},
|
|
Version: version,
|
|
APIVersion: apiVersion,
|
|
}
|
|
}
|
|
|
|
func fakeProvisionerDaemonStale(t *testing.T, name, version, apiVersion string, lastSeenAt, now time.Time) database.ProvisionerDaemon {
|
|
t.Helper()
|
|
d := fakeProvisionerDaemon(t, name, version, apiVersion, now)
|
|
d.LastSeenAt.Valid = true
|
|
d.LastSeenAt.Time = lastSeenAt
|
|
return d
|
|
}
|