Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1b2bc9a

Browse files
committed
WIP: add version healthcheck for provisioner daemons
1 parentf80a1cf commit1b2bc9a

File tree

3 files changed

+263
-0
lines changed

3 files changed

+263
-0
lines changed

‎coderd/healthcheck/health/model.go‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ const (
3434

3535
CodeDERPNodeUsesWebsocketCode=`EDERP01`
3636
CodeDERPOneNodeUnhealthyCode=`EDERP02`
37+
38+
CodeProvisionerDaemonsNoProvisionerDaemonsCode=`EPD01`
39+
CodeProvisionerDaemonVersionOutOfDateCode=`EPD02`
40+
CodeProvisionerDaemonAPIMajorVersionNotAvailableCode=`EPD03`
41+
CodeProvisionerDaemonAPIMinorVersionNotAvailableCode=`EPD04`
3742
)
3843

3944
// @typescript-generate Severity

‎coderd/healthcheck/provisioner.go‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package healthcheck
2+
3+
import (
4+
"context"
5+
6+
"golang.org/x/mod/semver"
7+
8+
"github.com/coder/coder/v2/coderd/database"
9+
"github.com/coder/coder/v2/coderd/healthcheck/health"
10+
"github.com/coder/coder/v2/codersdk"
11+
)
12+
13+
// @typescript-generate ProvisionerDaemonReport
14+
typeProvisionerDaemonReportstruct {
15+
Severity health.Severity`json:"severity"`
16+
Warnings []health.Message`json:"warnings"`
17+
Dismissedbool`json:"dismissed"`
18+
Error*string
19+
20+
Provisioners []codersdk.ProvisionerDaemon
21+
}
22+
23+
// @typescript-generate ProvisionerDaemonReportOptions
24+
typeProvisionerDaemonReportOptionsstruct {
25+
CurrentVersionstring
26+
CurrentAPIVersionstring
27+
28+
// ProvisionerDaemonsFn is a function that returns ProvisionerDaemons.
29+
// Satisfied by database.Store.ProvisionerDaemons
30+
ProvisionerDaemonsFnfunc(context.Context) ([]database.ProvisionerDaemon,error)
31+
32+
Dismissedbool
33+
}
34+
35+
func (r*ProvisionerDaemonReport)Run(ctx context.Context,opts*ProvisionerDaemonReportOptions) {
36+
r.Severity=health.SeverityOK
37+
r.Warnings=make([]health.Message,0)
38+
r.Dismissed=opts.Dismissed
39+
40+
ifopts.CurrentVersion=="" {
41+
r.Severity=health.SeverityError
42+
r.Warnings=append(r.Warnings,health.Messagef(health.CodeUnknown,"Developer error: CurrentVersion is empty!"))
43+
return
44+
}
45+
46+
ifopts.CurrentAPIVersion=="" {
47+
r.Severity=health.SeverityError
48+
r.Warnings=append(r.Warnings,health.Messagef(health.CodeUnknown,"Developer error: CurrentAPIVersion is empty!"))
49+
return
50+
}
51+
52+
ifopts.ProvisionerDaemonsFn==nil {
53+
r.Severity=health.SeverityError
54+
r.Warnings=append(r.Warnings,health.Messagef(health.CodeUnknown,"Developer error: ProvisionerDaemonsFn is nil!"))
55+
return
56+
}
57+
58+
daemons,err:=opts.ProvisionerDaemonsFn(ctx)
59+
iferr!=nil {
60+
r.Severity=health.SeverityError
61+
r.Warnings=append(r.Warnings,health.Messagef(health.CodeUnknown,"Unable to fetch provisioner daemons: %s",err.Error()))
62+
return
63+
}
64+
65+
iflen(daemons)==0 {
66+
r.Severity=health.SeverityError
67+
r.Warnings=append(r.Warnings,health.Messagef(health.CodeProvisionerDaemonsNoProvisionerDaemons,"No provisioner daemons found!"))
68+
}
69+
70+
for_,daemon:=rangedaemons {
71+
// For release versions, just check MAJOR.MINOR and ignore patch.
72+
if!semver.IsValid(daemon.Version) {
73+
r.Severity=health.SeverityWarning
74+
r.Warnings=append(r.Warnings,health.Messagef(health.CodeUnknown,"Provisioner daemon %q reports invalid version %q",opts.CurrentVersion,daemon.Version))
75+
}elseifsemver.Compare(semver.MajorMinor(opts.CurrentVersion),semver.MajorMinor(daemon.Version))>1 {
76+
r.Severity=health.SeverityWarning
77+
r.Warnings=append(r.Warnings,health.Messagef(health.CodeUnknown,"Provisioner daemon %q has outdated version %q",daemon.Name,daemon.Version))
78+
}
79+
80+
// Provisioner daemon API version follows different rules.
81+
// 1) Coderd must support the requested API major version.
82+
// 2) The requested API minor version must be less than or equal to that of Coderd.
83+
ourMaj:=semver.Major(opts.CurrentVersion)
84+
theirMaj:=semver.Major(daemon.APIVersion)
85+
ifsemver.Compare(ourMaj,theirMaj)!=0 {
86+
r.Severity=health.SeverityError
87+
r.Warnings=append(r.Warnings,health.Messagef("Provisioner daemon %q requested major API version %s but only %s is available",daemon.Name,theirMaj,ourMaj))
88+
}elseifsemver.Compare(semver.MajorMinor(opts.CurrentAPIVersion),semver.MajorMinor(daemon.APIVersion))>1 {
89+
r.Severity=health.SeverityWarning
90+
r.Warnings=append(r.Warnings,health.Messagef(health.CodeUnknown,"Provisioner daemon %q requested API version %q but only %q is available",daemon.Name,daemon.Version,opts.CurrentAPIVersion))
91+
}
92+
}
93+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package healthcheck_test
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"testing"
7+
8+
"github.com/google/uuid"
9+
"github.com/stretchr/testify/assert"
10+
11+
"github.com/coder/coder/v2/coderd/database"
12+
"github.com/coder/coder/v2/coderd/database/dbtime"
13+
"github.com/coder/coder/v2/coderd/healthcheck"
14+
"github.com/coder/coder/v2/coderd/healthcheck/health"
15+
)
16+
17+
funcTestProvisionerDaemonReport(t*testing.T) {
18+
t.Parallel()
19+
20+
var ()
21+
22+
for_,tt:=range []struct {
23+
namestring
24+
currentVersionstring
25+
currentAPIVersionstring
26+
provisionerDaemonsFnfunc(context.Context) ([]database.ProvisionerDaemon,error)
27+
expectedSeverity health.Severity
28+
expectedWarningCode health.Code
29+
}{
30+
{
31+
name:"current version empty",
32+
currentVersion:"",
33+
expectedSeverity:health.SeverityError,
34+
expectedWarningCode:health.CodeUnknown,
35+
},
36+
{
37+
name:"current api version empty",
38+
currentVersion:"v1.2.3",
39+
currentAPIVersion:"",
40+
expectedSeverity:health.SeverityError,
41+
expectedWarningCode:health.CodeUnknown,
42+
},
43+
{
44+
name:"provisionerdaemonsfn nil",
45+
currentVersion:"v1.2.3",
46+
currentAPIVersion:"v1.0",
47+
expectedSeverity:health.SeverityError,
48+
expectedWarningCode:health.CodeUnknown,
49+
},
50+
{
51+
name:"no daemons",
52+
currentVersion:"v1.2.3",
53+
currentAPIVersion:"v1.0",
54+
expectedSeverity:health.SeverityError,
55+
expectedWarningCode:health.CodeProvisionerDaemonsNoProvisionerDaemons,
56+
provisionerDaemonsFn:fakeProvisionerDaemonsFn(),
57+
},
58+
{
59+
name:"one daemon up to date",
60+
currentVersion:"v1.2.3",
61+
currentAPIVersion:"v1.0",
62+
expectedSeverity:health.SeverityOK,
63+
provisionerDaemonsFn:fakeProvisionerDaemonsFn(fakeProvisionerDaemon(t,"pd-ok","v1.2.3","v1.0")),
64+
},
65+
{
66+
name:"one daemon out of date",
67+
currentVersion:"v1.2.3",
68+
currentAPIVersion:"v1.0",
69+
expectedSeverity:health.SeverityWarning,
70+
expectedWarningCode:health.CodeProvisionerDaemonVersionOutOfDate,
71+
provisionerDaemonsFn:fakeProvisionerDaemonsFn(fakeProvisionerDaemon(t,"pd-old","v1.1.2","v1.0")),
72+
},
73+
{
74+
name:"major api version not available",
75+
currentVersion:"v1.2.3",
76+
currentAPIVersion:"v1.0",
77+
expectedSeverity:health.SeverityError,
78+
expectedWarningCode:health.CodeProvisionerDaemonAPIMajorVersionNotAvailable,
79+
provisionerDaemonsFn:fakeProvisionerDaemonsFn(fakeProvisionerDaemon(t,"pd-new-major","v1.2.3","v2.0")),
80+
},
81+
{
82+
name:"minor api version not available",
83+
currentVersion:"v1.2.3",
84+
currentAPIVersion:"v1.0",
85+
expectedSeverity:health.SeverityWarning,
86+
expectedWarningCode:health.CodeProvisionerDaemonAPIMinorVersionNotAvailable,
87+
provisionerDaemonsFn:fakeProvisionerDaemonsFn(fakeProvisionerDaemon(t,"pd-new-minor","v1.2.3","v1.1")),
88+
},
89+
{
90+
name:"one up to date, one out of date",
91+
currentVersion:"v1.2.3",
92+
currentAPIVersion:"v1.0",
93+
expectedSeverity:health.SeverityWarning,
94+
expectedWarningCode:health.CodeProvisionerDaemonVersionOutOfDate,
95+
provisionerDaemonsFn:fakeProvisionerDaemonsFn(
96+
fakeProvisionerDaemon(t,"pd-ok","v1.2.3","v1.0"),
97+
fakeProvisionerDaemon(t,"pd-old","v1.1.2","v1.0")),
98+
},
99+
{
100+
name:"one up to date, one newer",
101+
currentVersion:"v1.2.3",
102+
currentAPIVersion:"v1.0",
103+
expectedSeverity:health.SeverityOK,
104+
provisionerDaemonsFn:fakeProvisionerDaemonsFn(
105+
fakeProvisionerDaemon(t,"pd-ok","v1.2.3","v1.0"),
106+
fakeProvisionerDaemon(t,"pd-new","v2.3.4","v1.0")),
107+
},
108+
} {
109+
tt:=tt
110+
t.Run(tt.name,func(t*testing.T) {
111+
t.Parallel()
112+
113+
varrpt healthcheck.ProvisionerDaemonReport
114+
varopts healthcheck.ProvisionerDaemonReportOptions
115+
opts.CurrentVersion=tt.currentVersion
116+
opts.CurrentAPIVersion=tt.currentAPIVersion
117+
iftt.provisionerDaemonsFn!=nil {
118+
opts.ProvisionerDaemonsFn=tt.provisionerDaemonsFn
119+
}
120+
121+
rpt.Run(context.Background(),&opts)
122+
123+
assert.Equal(t,tt.expectedSeverity,rpt.Severity)
124+
iftt.expectedWarningCode!=""&&assert.NotEmpty(t,rpt.Warnings) {
125+
varfoundbool
126+
for_,w:=rangerpt.Warnings {
127+
ifw.Code==tt.expectedWarningCode {
128+
found=true
129+
break
130+
}
131+
}
132+
assert.True(t,found,"expected warning %s not found in %v",tt.expectedWarningCode,rpt.Warnings)
133+
}else {
134+
assert.Empty(t,rpt.Warnings)
135+
}
136+
})
137+
}
138+
}
139+
140+
funcfakeProvisionerDaemon(t*testing.T,name,version,apiVersionstring) database.ProvisionerDaemon {
141+
t.Helper()
142+
return database.ProvisionerDaemon{
143+
ID:uuid.New(),
144+
Name:name,
145+
CreatedAt:dbtime.Now(),
146+
LastSeenAt: sql.NullTime{Time:dbtime.Now(),Valid:true},
147+
Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho,database.ProvisionerTypeTerraform},
148+
ReplicaID: uuid.NullUUID{},
149+
Tags:map[string]string{},
150+
Version:version,
151+
APIVersion:apiVersion,
152+
}
153+
}
154+
155+
funcfakeProvisionerDaemonsFn(pds...database.ProvisionerDaemon)func(context.Context) ([]database.ProvisionerDaemon,error) {
156+
returnfunc(context.Context) ([]database.ProvisionerDaemon,error) {
157+
returnpds,nil
158+
}
159+
}
160+
161+
funcfakeProvisionerDaemonsFnErr(errerror)func(context.Context) ([]database.ProvisionerDaemon,error) {
162+
returnfunc(context.Context) ([]database.ProvisionerDaemon,error) {
163+
returnnil,err
164+
}
165+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp