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

Commit6a14a8b

Browse files
test: improve test coverage for hard-limited presets
1 parent2667684 commit6a14a8b

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed

‎enterprise/coderd/prebuilds/reconcile_test.go

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,213 @@ func TestSkippingHardLimitedPresets(t *testing.T) {
815815
}
816816
}
817817

818+
funcTestHardLimitedPresetShouldNotBlockDeletion(t*testing.T) {
819+
t.Parallel()
820+
821+
if!dbtestutil.WillUsePostgres() {
822+
t.Skip("This test requires postgres")
823+
}
824+
825+
// Test cases verify the behavior of prebuild creation depending on configured failure limits.
826+
testCases:= []struct {
827+
namestring
828+
hardLimitint64
829+
isHardLimitHitbool
830+
}{
831+
{
832+
name:"hard limit is hit - skip creation of prebuilt workspace",
833+
hardLimit:1,
834+
isHardLimitHit:true,
835+
},
836+
}
837+
838+
for_,tc:=rangetestCases {
839+
t.Run(tc.name,func(t*testing.T) {
840+
t.Parallel()
841+
842+
clock:=quartz.NewMock(t)
843+
ctx:=testutil.Context(t,testutil.WaitShort)
844+
cfg:= codersdk.PrebuildsConfig{
845+
FailureHardLimit:serpent.Int64(tc.hardLimit),
846+
ReconciliationBackoffInterval:0,
847+
}
848+
logger:=slogtest.Make(
849+
t,&slogtest.Options{IgnoreErrors:true},
850+
).Leveled(slog.LevelDebug)
851+
db,pubSub:=dbtestutil.NewDB(t)
852+
fakeEnqueuer:=newFakeEnqueuer()
853+
registry:=prometheus.NewRegistry()
854+
controller:=prebuilds.NewStoreReconciler(db,pubSub,cfg,logger,clock,registry,fakeEnqueuer)
855+
856+
// Template admin to receive a notification.
857+
templateAdmin:=dbgen.User(t,db, database.User{
858+
RBACRoles: []string{codersdk.RoleTemplateAdmin},
859+
})
860+
861+
// Set up test environment with a template, version, and preset.
862+
ownerID:=uuid.New()
863+
dbgen.User(t,db, database.User{
864+
ID:ownerID,
865+
})
866+
org,template:=setupTestDBTemplate(t,db,ownerID,false)
867+
templateVersionID:=setupTestDBTemplateVersion(ctx,t,clock,db,pubSub,org.ID,ownerID,template.ID)
868+
preset:=setupTestDBPreset(t,db,templateVersionID,2,uuid.New().String())
869+
870+
// Create a successful prebuilt workspace.
871+
successfulWorkspace,_:=setupTestDBPrebuild(
872+
t,
873+
clock,
874+
db,
875+
pubSub,
876+
database.WorkspaceTransitionStart,
877+
database.ProvisionerJobStatusSucceeded,
878+
org.ID,
879+
preset,
880+
template.ID,
881+
templateVersionID,
882+
)
883+
884+
// Make sure that prebuilt workspaces created in such order: [successful, failed].
885+
clock.Advance(time.Second).MustWait(ctx)
886+
887+
// Create a failed prebuilt workspace that counts toward the hard failure limit.
888+
setupTestDBPrebuild(
889+
t,
890+
clock,
891+
db,
892+
pubSub,
893+
database.WorkspaceTransitionStart,
894+
database.ProvisionerJobStatusFailed,
895+
org.ID,
896+
preset,
897+
template.ID,
898+
templateVersionID,
899+
)
900+
901+
getJobStatusMap:=func(workspaces []database.WorkspaceTable)map[database.ProvisionerJobStatus]int {
902+
jobStatusMap:=make(map[database.ProvisionerJobStatus]int)
903+
for_,workspace:=rangeworkspaces {
904+
workspaceBuilds,err:=db.GetWorkspaceBuildsByWorkspaceID(ctx, database.GetWorkspaceBuildsByWorkspaceIDParams{
905+
WorkspaceID:workspace.ID,
906+
})
907+
require.NoError(t,err)
908+
909+
for_,workspaceBuild:=rangeworkspaceBuilds {
910+
job,err:=db.GetProvisionerJobByID(ctx,workspaceBuild.JobID)
911+
require.NoError(t,err)
912+
jobStatusMap[job.JobStatus]++
913+
}
914+
}
915+
returnjobStatusMap
916+
}
917+
918+
// Verify initial state: two workspaces exist, one successful, one failed.
919+
workspaces,err:=db.GetWorkspacesByTemplateID(ctx,template.ID)
920+
require.NoError(t,err)
921+
require.Equal(t,2,len(workspaces))
922+
jobStatusMap:=getJobStatusMap(workspaces)
923+
require.Len(t,jobStatusMap,2)
924+
require.Equal(t,1,jobStatusMap[database.ProvisionerJobStatusSucceeded])
925+
require.Equal(t,1,jobStatusMap[database.ProvisionerJobStatusFailed])
926+
927+
//Verify initial state: metric is not set - meaning preset is not hard limited.
928+
require.NoError(t,controller.ForceMetricsUpdate(ctx))
929+
mf,err:=registry.Gather()
930+
require.NoError(t,err)
931+
metric:=findMetric(mf,prebuilds.MetricPresetHardLimitedGauge,map[string]string{
932+
"template_name":template.Name,
933+
"preset_name":preset.Name,
934+
"org_name":org.Name,
935+
})
936+
require.Nil(t,metric)
937+
938+
// We simulate a failed prebuild in the test; Consequently, the backoff mechanism is triggered when ReconcileAll is called.
939+
// Even though ReconciliationBackoffInterval is set to zero, we still need to advance the clock by at least one nanosecond.
940+
clock.Advance(time.Nanosecond).MustWait(ctx)
941+
942+
// Trigger reconciliation to attempt creating a new prebuild.
943+
// The outcome depends on whether the hard limit has been reached.
944+
require.NoError(t,controller.ReconcileAll(ctx))
945+
946+
// These two additional calls to ReconcileAll should not trigger any notifications.
947+
// A notification is only sent once.
948+
require.NoError(t,controller.ReconcileAll(ctx))
949+
require.NoError(t,controller.ReconcileAll(ctx))
950+
951+
// Verify the final state after reconciliation.
952+
// When hard limit is reached, no new workspace should be created.
953+
workspaces,err=db.GetWorkspacesByTemplateID(ctx,template.ID)
954+
require.NoError(t,err)
955+
require.Equal(t,2,len(workspaces))
956+
jobStatusMap=getJobStatusMap(workspaces)
957+
require.Len(t,jobStatusMap,2)
958+
require.Equal(t,1,jobStatusMap[database.ProvisionerJobStatusSucceeded])
959+
require.Equal(t,1,jobStatusMap[database.ProvisionerJobStatusFailed])
960+
961+
updatedPreset,err:=db.GetPresetByID(ctx,preset.ID)
962+
require.NoError(t,err)
963+
require.Equal(t,database.PrebuildStatusHardLimited,updatedPreset.PrebuildStatus)
964+
965+
// When hard limit is reached, a notification should be sent.
966+
matching:=fakeEnqueuer.Sent(func(notification*notificationstest.FakeNotification)bool {
967+
if!assert.Equal(t,notifications.PrebuildFailureLimitReached,notification.TemplateID,"unexpected template") {
968+
returnfalse
969+
}
970+
971+
if!assert.Equal(t,templateAdmin.ID,notification.UserID,"unexpected receiver") {
972+
returnfalse
973+
}
974+
975+
returntrue
976+
})
977+
require.Len(t,matching,1)
978+
979+
// When hard limit is reached, metric is set to 1.
980+
mf,err=registry.Gather()
981+
require.NoError(t,err)
982+
metric=findMetric(mf,prebuilds.MetricPresetHardLimitedGauge,map[string]string{
983+
"template_name":template.Name,
984+
"preset_name":preset.Name,
985+
"org_name":org.Name,
986+
})
987+
require.NotNil(t,metric)
988+
require.NotNil(t,metric.GetGauge())
989+
require.EqualValues(t,1,metric.GetGauge().GetValue())
990+
991+
// When: the template is deleted.
992+
require.NoError(t,db.UpdateTemplateDeletedByID(ctx, database.UpdateTemplateDeletedByIDParams{
993+
ID:template.ID,
994+
Deleted:true,
995+
UpdatedAt:dbtime.Now(),
996+
}))
997+
998+
// Trigger reconciliation to make sure that successful, but outdated prebuilt workspace will be deleted.
999+
require.NoError(t,controller.ReconcileAll(ctx))
1000+
1001+
workspaces,err=db.GetWorkspacesByTemplateID(ctx,template.ID)
1002+
require.NoError(t,err)
1003+
require.Equal(t,2,len(workspaces))
1004+
1005+
jobStatusMap=getJobStatusMap(workspaces)
1006+
require.Len(t,jobStatusMap,3)
1007+
require.Equal(t,1,jobStatusMap[database.ProvisionerJobStatusSucceeded])
1008+
require.Equal(t,1,jobStatusMap[database.ProvisionerJobStatusFailed])
1009+
// Pending job should be the job that deletes successful, but outdated prebuilt workspace.
1010+
// Prebuilt workspace MUST be deleted, despite the fact that preset is marked as hard limited.
1011+
require.Equal(t,1,jobStatusMap[database.ProvisionerJobStatusPending])
1012+
1013+
workspaceBuilds,err:=db.GetWorkspaceBuildsByWorkspaceID(ctx, database.GetWorkspaceBuildsByWorkspaceIDParams{
1014+
WorkspaceID:successfulWorkspace.ID,
1015+
})
1016+
require.NoError(t,err)
1017+
require.Equal(t,2,len(workspaceBuilds))
1018+
// Make sure that successfully created, but outdated prebuilt workspace was scheduled for deletion.
1019+
require.Equal(t,database.WorkspaceTransitionDelete,workspaceBuilds[0].Transition)
1020+
require.Equal(t,database.WorkspaceTransitionStart,workspaceBuilds[1].Transition)
1021+
})
1022+
}
1023+
}
1024+
8181025
funcTestRunLoop(t*testing.T) {
8191026
t.Parallel()
8201027

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp