@@ -46,6 +46,7 @@ import (
4646 "github.com/coder/coder/v2/provisionerd/proto"
4747 "github.com/coder/coder/v2/provisionersdk"
4848 sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
49+ "github.com/coder/quartz"
4950)
5051
5152const (
@@ -61,8 +62,9 @@ const (
6162type Options struct {
6263 OIDCConfig promoauth.OAuth2Config
6364 ExternalAuthConfigs []* externalauth.Config
64- // TimeNowFn is only used in tests
65- TimeNowFn func () time.Time
65+
66+ // Clock for testing
67+ Clock quartz.Clock
6668
6769 // AcquireJobLongPollDur is used in tests
6870 AcquireJobLongPollDur time.Duration
@@ -104,7 +106,7 @@ type server struct {
104106
105107 OIDCConfig promoauth.OAuth2Config
106108
107- TimeNowFn func () time. Time
109+ Clock quartz. Clock
108110
109111 acquireJobLongPollDur time.Duration
110112
@@ -191,6 +193,9 @@ func NewServer(
191193 if options .HeartbeatInterval == 0 {
192194 options .HeartbeatInterval = DefaultHeartbeatInterval
193195 }
196+ if options .Clock == nil {
197+ options .Clock = quartz .NewReal ()
198+ }
194199
195200 s := & server {
196201 lifecycleCtx : lifecycleCtx ,
@@ -213,7 +218,7 @@ func NewServer(
213218 UserQuietHoursScheduleStore : userQuietHoursScheduleStore ,
214219 DeploymentValues : deploymentValues ,
215220 OIDCConfig : options .OIDCConfig ,
216- TimeNowFn : options .TimeNowFn ,
221+ Clock : options .Clock ,
217222 acquireJobLongPollDur : options .AcquireJobLongPollDur ,
218223 heartbeatInterval : options .HeartbeatInterval ,
219224 heartbeatFn : options .HeartbeatFn ,
@@ -229,11 +234,8 @@ func NewServer(
229234
230235// timeNow should be used when trying to get the current time for math
231236// calculations regarding workspace start and stop time.
232- func (s * server ) timeNow () time.Time {
233- if s .TimeNowFn != nil {
234- return dbtime .Time (s .TimeNowFn ())
235- }
236- return dbtime .Now ()
237+ func (s * server ) timeNow (tags ... string ) time.Time {
238+ return dbtime .Time (s .Clock .Now (tags ... ))
237239}
238240
239241// heartbeatLoop runs heartbeatOnce at the interval specified by HeartbeatInterval
@@ -365,7 +367,7 @@ func (s *server) AcquireJobWithCancel(stream proto.DRPCProvisionerDaemon_Acquire
365367 logger .Error (streamCtx , "recv error and failed to cancel acquire job" , slog .Error (recvErr ))
366368 // Well, this is awkward. We hit an error receiving from the stream, but didn't cancel before we locked a job
367369 // in the database. We need to mark this job as failed so the end user can retry if they want to.
368- now := dbtime . Now ()
370+ now := s . timeNow ()
369371 err := s .Database .UpdateProvisionerJobWithCompleteByID (
370372 //nolint:gocritic // Provisionerd has specific authz rules.
371373 dbauthz .AsProvisionerd (context .Background ()),
@@ -406,15 +408,15 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
406408 err := s .Database .UpdateProvisionerJobWithCompleteByID (ctx , database.UpdateProvisionerJobWithCompleteByIDParams {
407409 ID : job .ID ,
408410 CompletedAt : sql.NullTime {
409- Time : dbtime . Now (),
411+ Time : s . timeNow (),
410412 Valid : true ,
411413 },
412414 Error : sql.NullString {
413415 String : errorMessage ,
414416 Valid : true ,
415417 },
416418 ErrorCode : job .ErrorCode ,
417- UpdatedAt : dbtime . Now (),
419+ UpdatedAt : s . timeNow (),
418420 })
419421 if err != nil {
420422 return xerrors .Errorf ("update provisioner job: %w" , err )
@@ -792,7 +794,7 @@ func (s *server) UpdateJob(ctx context.Context, request *proto.UpdateJobRequest)
792794 }
793795 err = s .Database .UpdateProvisionerJobByID (ctx , database.UpdateProvisionerJobByIDParams {
794796 ID : parsedID ,
795- UpdatedAt : dbtime . Now (),
797+ UpdatedAt : s . timeNow (),
796798 })
797799 if err != nil {
798800 return nil , xerrors .Errorf ("update job: %w" , err )
@@ -869,7 +871,7 @@ func (s *server) UpdateJob(ctx context.Context, request *proto.UpdateJobRequest)
869871 err := s .Database .UpdateTemplateVersionDescriptionByJobID (ctx , database.UpdateTemplateVersionDescriptionByJobIDParams {
870872 JobID : job .ID ,
871873 Readme : string (request .Readme ),
872- UpdatedAt : dbtime . Now (),
874+ UpdatedAt : s . timeNow (),
873875 })
874876 if err != nil {
875877 return nil , xerrors .Errorf ("update template version description: %w" , err )
@@ -958,7 +960,7 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
958960 return nil , xerrors .Errorf ("job already completed" )
959961 }
960962 job .CompletedAt = sql.NullTime {
961- Time : dbtime . Now (),
963+ Time : s . timeNow (),
962964 Valid : true ,
963965 }
964966 job .Error = sql.NullString {
@@ -973,7 +975,7 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
973975 err = s .Database .UpdateProvisionerJobWithCompleteByID (ctx , database.UpdateProvisionerJobWithCompleteByIDParams {
974976 ID : jobID ,
975977 CompletedAt : job .CompletedAt ,
976- UpdatedAt : dbtime . Now (),
978+ UpdatedAt : s . timeNow (),
977979 Error : job .Error ,
978980 ErrorCode : job .ErrorCode ,
979981 })
@@ -1008,15 +1010,15 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
10081010 if jobType .WorkspaceBuild .State != nil {
10091011 err = db .UpdateWorkspaceBuildProvisionerStateByID (ctx , database.UpdateWorkspaceBuildProvisionerStateByIDParams {
10101012 ID : input .WorkspaceBuildID ,
1011- UpdatedAt : dbtime . Now (),
1013+ UpdatedAt : s . timeNow (),
10121014 ProvisionerState : jobType .WorkspaceBuild .State ,
10131015 })
10141016 if err != nil {
10151017 return xerrors .Errorf ("update workspace build state: %w" , err )
10161018 }
10171019 err = db .UpdateWorkspaceBuildDeadlineByID (ctx , database.UpdateWorkspaceBuildDeadlineByIDParams {
10181020 ID : input .WorkspaceBuildID ,
1019- UpdatedAt : dbtime . Now (),
1021+ UpdatedAt : s . timeNow (),
10201022 Deadline : build .Deadline ,
10211023 MaxDeadline : build .MaxDeadline ,
10221024 })
@@ -1382,17 +1384,17 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
13821384 err = s .Database .UpdateTemplateVersionExternalAuthProvidersByJobID (ctx , database.UpdateTemplateVersionExternalAuthProvidersByJobIDParams {
13831385 JobID : jobID ,
13841386 ExternalAuthProviders : json .RawMessage (externalAuthProvidersMessage ),
1385- UpdatedAt : dbtime . Now (),
1387+ UpdatedAt : s . timeNow (),
13861388 })
13871389 if err != nil {
13881390 return nil , xerrors .Errorf ("update template version external auth providers: %w" , err )
13891391 }
13901392
13911393 err = s .Database .UpdateProvisionerJobWithCompleteByID (ctx , database.UpdateProvisionerJobWithCompleteByIDParams {
13921394 ID : jobID ,
1393- UpdatedAt : dbtime . Now (),
1395+ UpdatedAt : s . timeNow (),
13941396 CompletedAt : sql.NullTime {
1395- Time : dbtime . Now (),
1397+ Time : s . timeNow (),
13961398 Valid : true ,
13971399 },
13981400 Error : completedError ,
@@ -1687,9 +1689,9 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
16871689
16881690 err = s .Database .UpdateProvisionerJobWithCompleteByID (ctx , database.UpdateProvisionerJobWithCompleteByIDParams {
16891691 ID : jobID ,
1690- UpdatedAt : dbtime . Now (),
1692+ UpdatedAt : s . timeNow (),
16911693 CompletedAt : sql.NullTime {
1692- Time : dbtime . Now (),
1694+ Time : s . timeNow (),
16931695 Valid : true ,
16941696 },
16951697 Error : sql.NullString {},
0 commit comments