Skip to content

Commit bb479c2

Browse files
committed
Eligibility tests
Signed-off-by: Danny Kopping <dannykopping@gmail.com>
1 parent a56b763 commit bb479c2

File tree

3 files changed

+155
-80
lines changed

3 files changed

+155
-80
lines changed

enterprise/coderd/prebuilds/claim_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ func TestClaimPrebuild_CheckDifferentErrors(t *testing.T) {
420420
EntitlementsUpdateInterval: time.Second,
421421
})
422422

423-
reconciler := prebuilds.NewStoreReconciler(errorStore, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t))
423+
reconciler := prebuilds.NewStoreReconciler(errorStore, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), api.PrometheusRegistry)
424424
var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(errorStore)
425425
api.AGPL.PrebuildsClaimer.Store(&claimer)
426426

enterprise/coderd/prebuilds/metricscollector_test.go

Lines changed: 125 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,26 @@ func TestMetricsCollector(t *testing.T) {
4545
ownerIDs []uuid.UUID
4646
metrics []metricCheck
4747
templateDeleted []bool
48+
eligible []bool
4849
}
4950

5051
tests := []testCase{
5152
{
52-
name: "prebuild created",
53+
name: "prebuild provisioned but not completed",
5354
transitions: allTransitions,
54-
jobStatuses: allJobStatuses,
55+
jobStatuses: allJobStatusesExcept(database.ProvisionerJobStatusPending, database.ProvisionerJobStatusRunning, database.ProvisionerJobStatusCanceling),
5556
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
56-
// TODO: reexamine and refactor the test cases and assertions:
57-
// * a running prebuild that is not elibible to be claimed currently seems to be eligible.
58-
// * a prebuild that was claimed should not be deemed running, not eligible.
59-
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID, uuid.New()},
57+
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
6058
metrics: []metricCheck{
6159
{"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
60+
{"coderd_prebuilt_workspaces_claimed_total", ptr.To(0.0), true},
61+
{"coderd_prebuilt_workspaces_failed_total", ptr.To(0.0), true},
6262
{"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
6363
{"coderd_prebuilt_workspaces_running", ptr.To(0.0), false},
6464
{"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
6565
},
6666
templateDeleted: []bool{false},
67+
eligible: []bool{false},
6768
},
6869
{
6970
name: "prebuild running",
@@ -73,11 +74,14 @@ func TestMetricsCollector(t *testing.T) {
7374
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
7475
metrics: []metricCheck{
7576
{"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
77+
{"coderd_prebuilt_workspaces_claimed_total", ptr.To(0.0), true},
78+
{"coderd_prebuilt_workspaces_failed_total", ptr.To(0.0), true},
7679
{"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
7780
{"coderd_prebuilt_workspaces_running", ptr.To(1.0), false},
7881
{"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
7982
},
8083
templateDeleted: []bool{false},
84+
eligible: []bool{false},
8185
},
8286
{
8387
name: "prebuild failed",
@@ -93,6 +97,41 @@ func TestMetricsCollector(t *testing.T) {
9397
{"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
9498
},
9599
templateDeleted: []bool{false},
100+
eligible: []bool{false},
101+
},
102+
{
103+
name: "prebuild eligible",
104+
transitions: []database.WorkspaceTransition{database.WorkspaceTransitionStart},
105+
jobStatuses: []database.ProvisionerJobStatus{database.ProvisionerJobStatusSucceeded},
106+
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
107+
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
108+
metrics: []metricCheck{
109+
{"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
110+
{"coderd_prebuilt_workspaces_claimed_total", ptr.To(0.0), true},
111+
{"coderd_prebuilt_workspaces_failed_total", ptr.To(0.0), true},
112+
{"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
113+
{"coderd_prebuilt_workspaces_running", ptr.To(1.0), false},
114+
{"coderd_prebuilt_workspaces_eligible", ptr.To(1.0), false},
115+
},
116+
templateDeleted: []bool{false},
117+
eligible: []bool{true},
118+
},
119+
{
120+
name: "prebuild ineligible",
121+
transitions: allTransitions,
122+
jobStatuses: allJobStatusesExcept(database.ProvisionerJobStatusSucceeded),
123+
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
124+
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
125+
metrics: []metricCheck{
126+
{"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
127+
{"coderd_prebuilt_workspaces_claimed_total", ptr.To(0.0), true},
128+
{"coderd_prebuilt_workspaces_failed_total", ptr.To(0.0), true},
129+
{"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
130+
{"coderd_prebuilt_workspaces_running", ptr.To(1.0), false},
131+
{"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
132+
},
133+
templateDeleted: []bool{false},
134+
eligible: []bool{false},
96135
},
97136
{
98137
name: "prebuild claimed",
@@ -108,6 +147,7 @@ func TestMetricsCollector(t *testing.T) {
108147
{"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
109148
},
110149
templateDeleted: []bool{false},
150+
eligible: []bool{false},
111151
},
112152
{
113153
name: "workspaces that were not created by the prebuilds user are not counted",
@@ -121,6 +161,7 @@ func TestMetricsCollector(t *testing.T) {
121161
{"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
122162
},
123163
templateDeleted: []bool{false},
164+
eligible: []bool{false},
124165
},
125166
{
126167
name: "deleted templates never desire prebuilds",
@@ -132,6 +173,7 @@ func TestMetricsCollector(t *testing.T) {
132173
{"coderd_prebuilt_workspaces_desired", ptr.To(0.0), false},
133174
},
134175
templateDeleted: []bool{true},
176+
eligible: []bool{false},
135177
},
136178
{
137179
name: "running prebuilds for deleted templates are still counted, so that they can be deleted",
@@ -144,6 +186,7 @@ func TestMetricsCollector(t *testing.T) {
144186
{"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
145187
},
146188
templateDeleted: []bool{true},
189+
eligible: []bool{false},
147190
},
148191
}
149192
for _, test := range tests {
@@ -158,95 +201,99 @@ func TestMetricsCollector(t *testing.T) {
158201
ownerID := ownerID // capture for parallel
159202
for _, templateDeleted := range test.templateDeleted {
160203
templateDeleted := templateDeleted // capture for parallel
161-
t.Run(fmt.Sprintf("%v/transition:%s/jobStatus:%s", test.name, transition, jobStatus), func(t *testing.T) {
162-
t.Parallel()
204+
for _, eligible := range test.eligible {
205+
eligible := eligible // capture for parallel
206+
t.Run(fmt.Sprintf("%v/transition:%s/jobStatus:%s", test.name, transition, jobStatus), func(t *testing.T) {
207+
t.Parallel()
163208

164-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
165-
t.Cleanup(func() {
166-
if t.Failed() {
167-
t.Logf("failed to run test: %s", test.name)
168-
t.Logf("transition: %s", transition)
169-
t.Logf("jobStatus: %s", jobStatus)
170-
t.Logf("initiatorID: %s", initiatorID)
171-
t.Logf("ownerID: %s", ownerID)
172-
t.Logf("templateDeleted: %t", templateDeleted)
173-
}
174-
})
175-
clock := quartz.NewMock(t)
176-
db, pubsub := dbtestutil.NewDB(t)
177-
reconciler := prebuilds.NewStoreReconciler(db, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), prometheus.NewRegistry())
178-
ctx := testutil.Context(t, testutil.WaitLong)
209+
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
210+
t.Cleanup(func() {
211+
if t.Failed() {
212+
t.Logf("failed to run test: %s", test.name)
213+
t.Logf("transition: %s", transition)
214+
t.Logf("jobStatus: %s", jobStatus)
215+
t.Logf("initiatorID: %s", initiatorID)
216+
t.Logf("ownerID: %s", ownerID)
217+
t.Logf("templateDeleted: %t", templateDeleted)
218+
}
219+
})
220+
clock := quartz.NewMock(t)
221+
db, pubsub := dbtestutil.NewDB(t)
222+
reconciler := prebuilds.NewStoreReconciler(db, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), prometheus.NewRegistry())
223+
ctx := testutil.Context(t, testutil.WaitLong)
179224

180-
createdUsers := []uuid.UUID{agplprebuilds.SystemUserID}
181-
for _, user := range slices.Concat(test.ownerIDs, test.initiatorIDs) {
182-
if !slices.Contains(createdUsers, user) {
183-
dbgen.User(t, db, database.User{
184-
ID: user,
185-
})
186-
createdUsers = append(createdUsers, user)
225+
createdUsers := []uuid.UUID{agplprebuilds.SystemUserID}
226+
for _, user := range slices.Concat(test.ownerIDs, test.initiatorIDs) {
227+
if !slices.Contains(createdUsers, user) {
228+
dbgen.User(t, db, database.User{
229+
ID: user,
230+
})
231+
createdUsers = append(createdUsers, user)
232+
}
187233
}
188-
}
189234

190-
collector := prebuilds.NewMetricsCollector(db, logger, reconciler)
191-
registry := prometheus.NewPedanticRegistry()
192-
registry.Register(collector)
235+
collector := prebuilds.NewMetricsCollector(db, logger, reconciler)
236+
registry := prometheus.NewPedanticRegistry()
237+
registry.Register(collector)
193238

194-
numTemplates := 2
195-
for i := 0; i < numTemplates; i++ {
196-
org, template := setupTestDBTemplate(t, db, ownerID, templateDeleted)
197-
templateVersionID := setupTestDBTemplateVersion(ctx, t, clock, db, pubsub, org.ID, ownerID, template.ID)
198-
preset := setupTestDBPreset(t, db, templateVersionID, 1, uuid.New().String())
199-
setupTestDBWorkspace(
200-
t, clock, db, pubsub,
201-
transition, jobStatus, org.ID, preset, template.ID, templateVersionID, initiatorID, ownerID,
202-
)
203-
}
204-
205-
metricsFamilies, err := registry.Gather()
206-
require.NoError(t, err)
207-
208-
templates, err := db.GetTemplates(ctx)
209-
require.NoError(t, err)
210-
require.Equal(t, numTemplates, len(templates))
239+
numTemplates := 2
240+
for i := 0; i < numTemplates; i++ {
241+
org, template := setupTestDBTemplate(t, db, ownerID, templateDeleted)
242+
templateVersionID := setupTestDBTemplateVersion(ctx, t, clock, db, pubsub, org.ID, ownerID, template.ID)
243+
preset := setupTestDBPreset(t, db, templateVersionID, 1, uuid.New().String())
244+
workspace := setupTestDBWorkspace(
245+
t, clock, db, pubsub,
246+
transition, jobStatus, org.ID, preset, template.ID, templateVersionID, initiatorID, ownerID,
247+
)
248+
setupTestDBWorkspaceAgent(t, db, workspace.ID, eligible)
249+
}
211250

212-
for _, template := range templates {
213-
org, err := db.GetOrganizationByID(ctx, template.OrganizationID)
214-
require.NoError(t, err)
215-
templateVersions, err := db.GetTemplateVersionsByTemplateID(ctx, database.GetTemplateVersionsByTemplateIDParams{
216-
TemplateID: template.ID,
217-
})
251+
metricsFamilies, err := registry.Gather()
218252
require.NoError(t, err)
219-
require.Equal(t, 1, len(templateVersions))
220253

221-
presets, err := db.GetPresetsByTemplateVersionID(ctx, templateVersions[0].ID)
254+
templates, err := db.GetTemplates(ctx)
222255
require.NoError(t, err)
223-
require.Equal(t, 1, len(presets))
256+
require.Equal(t, numTemplates, len(templates))
224257

225-
for _, preset := range presets {
226-
preset := preset // capture for parallel
227-
labels := map[string]string{
228-
"template_name": template.Name,
229-
"preset_name": preset.Name,
230-
"organization_name": org.Name,
231-
}
258+
for _, template := range templates {
259+
org, err := db.GetOrganizationByID(ctx, template.OrganizationID)
260+
require.NoError(t, err)
261+
templateVersions, err := db.GetTemplateVersionsByTemplateID(ctx, database.GetTemplateVersionsByTemplateIDParams{
262+
TemplateID: template.ID,
263+
})
264+
require.NoError(t, err)
265+
require.Equal(t, 1, len(templateVersions))
266+
267+
presets, err := db.GetPresetsByTemplateVersionID(ctx, templateVersions[0].ID)
268+
require.NoError(t, err)
269+
require.Equal(t, 1, len(presets))
232270

233-
for _, check := range test.metrics {
234-
metric := findMetric(metricsFamilies, check.name, labels)
235-
if check.value == nil {
236-
continue
271+
for _, preset := range presets {
272+
preset := preset // capture for parallel
273+
labels := map[string]string{
274+
"template_name": template.Name,
275+
"preset_name": preset.Name,
276+
"organization_name": org.Name,
237277
}
238278

239-
require.NotNil(t, metric, "metric %s should exist", check.name)
279+
for _, check := range test.metrics {
280+
metric := findMetric(metricsFamilies, check.name, labels)
281+
if check.value == nil {
282+
continue
283+
}
240284

241-
if check.isCounter {
242-
require.Equal(t, *check.value, metric.GetCounter().GetValue(), "counter %s value mismatch", check.name)
243-
} else {
244-
require.Equal(t, *check.value, metric.GetGauge().GetValue(), "gauge %s value mismatch", check.name)
285+
require.NotNil(t, metric, "metric %s should exist", check.name)
286+
287+
if check.isCounter {
288+
require.Equal(t, *check.value, metric.GetCounter().GetValue(), "counter %s value mismatch", check.name)
289+
} else {
290+
require.Equal(t, *check.value, metric.GetGauge().GetValue(), "gauge %s value mismatch", check.name)
291+
}
245292
}
246293
}
247294
}
248-
}
249-
})
295+
})
296+
}
250297
}
251298
}
252299
}

enterprise/coderd/prebuilds/reconcile_test.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/prometheus/client_golang/prometheus"
1212

13+
"github.com/coder/coder/v2/coderd/database/dbtime"
1314
"github.com/coder/coder/v2/coderd/util/slice"
1415

1516
"github.com/google/uuid"
@@ -1011,6 +1012,29 @@ func setupTestDBWorkspace(
10111012
return workspace
10121013
}
10131014

1015+
func setupTestDBWorkspaceAgent(t *testing.T, db database.Store, workspaceID uuid.UUID, eligible bool) database.WorkspaceAgent {
1016+
build, err := db.GetLatestWorkspaceBuildByWorkspaceID(t.Context(), workspaceID)
1017+
require.NoError(t, err)
1018+
1019+
res := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{JobID: build.JobID})
1020+
agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
1021+
ResourceID: res.ID,
1022+
})
1023+
1024+
// A prebuilt workspace is considered eligible when its agent is in a "ready" lifecycle state.
1025+
// i.e. connected to the control plane and all startup scripts have run.
1026+
if eligible {
1027+
require.NoError(t, db.UpdateWorkspaceAgentLifecycleStateByID(t.Context(), database.UpdateWorkspaceAgentLifecycleStateByIDParams{
1028+
ID: agent.ID,
1029+
LifecycleState: database.WorkspaceAgentLifecycleStateReady,
1030+
StartedAt: sql.NullTime{Time: dbtime.Now().Add(-time.Minute), Valid: true},
1031+
ReadyAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
1032+
}))
1033+
}
1034+
1035+
return agent
1036+
}
1037+
10141038
var allTransitions = []database.WorkspaceTransition{
10151039
database.WorkspaceTransitionStart,
10161040
database.WorkspaceTransitionStop,
@@ -1026,4 +1050,8 @@ var allJobStatuses = []database.ProvisionerJobStatus{
10261050
database.ProvisionerJobStatusCanceling,
10271051
}
10281052

1029-
// TODO (sasswart): test mutual exclusion
1053+
func allJobStatusesExcept(except ...database.ProvisionerJobStatus) []database.ProvisionerJobStatus {
1054+
return slice.Filter(except, func(status database.ProvisionerJobStatus) bool {
1055+
return !slice.Contains(allJobStatuses, status)
1056+
})
1057+
}

0 commit comments

Comments
 (0)