refactor(coderd/provisionerdserver): use quartz.Clock instead of TimeNowFn (#15642)

Replace `TimeNowFn` in `provisionerdserver` with `quartz.Clock` as
well as pass `coderd`'s `Clock` to `provisionerdserver`.
This commit is contained in:
Danielle Maywood
2024-11-25 16:25:36 +00:00
committed by GitHub
parent bbc549d2df
commit 0896f339c4
4 changed files with 36 additions and 32 deletions
+1
View File
@@ -1648,6 +1648,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
provisionerdserver.Options{
OIDCConfig: api.OIDCConfig,
ExternalAuthConfigs: api.ExternalAuthConfigs,
Clock: api.Clock,
},
api.NotificationsEnqueuer,
)
+25 -23
View File
@@ -46,6 +46,7 @@ import (
"github.com/coder/coder/v2/provisionerd/proto"
"github.com/coder/coder/v2/provisionersdk"
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/quartz"
)
const (
@@ -61,8 +62,9 @@ const (
type Options struct {
OIDCConfig promoauth.OAuth2Config
ExternalAuthConfigs []*externalauth.Config
// TimeNowFn is only used in tests
TimeNowFn func() time.Time
// Clock for testing
Clock quartz.Clock
// AcquireJobLongPollDur is used in tests
AcquireJobLongPollDur time.Duration
@@ -104,7 +106,7 @@ type server struct {
OIDCConfig promoauth.OAuth2Config
TimeNowFn func() time.Time
Clock quartz.Clock
acquireJobLongPollDur time.Duration
@@ -191,6 +193,9 @@ func NewServer(
if options.HeartbeatInterval == 0 {
options.HeartbeatInterval = DefaultHeartbeatInterval
}
if options.Clock == nil {
options.Clock = quartz.NewReal()
}
s := &server{
lifecycleCtx: lifecycleCtx,
@@ -213,7 +218,7 @@ func NewServer(
UserQuietHoursScheduleStore: userQuietHoursScheduleStore,
DeploymentValues: deploymentValues,
OIDCConfig: options.OIDCConfig,
TimeNowFn: options.TimeNowFn,
Clock: options.Clock,
acquireJobLongPollDur: options.AcquireJobLongPollDur,
heartbeatInterval: options.HeartbeatInterval,
heartbeatFn: options.HeartbeatFn,
@@ -229,11 +234,8 @@ func NewServer(
// timeNow should be used when trying to get the current time for math
// calculations regarding workspace start and stop time.
func (s *server) timeNow() time.Time {
if s.TimeNowFn != nil {
return dbtime.Time(s.TimeNowFn())
}
return dbtime.Now()
func (s *server) timeNow(tags ...string) time.Time {
return dbtime.Time(s.Clock.Now(tags...))
}
// heartbeatLoop runs heartbeatOnce at the interval specified by HeartbeatInterval
@@ -365,7 +367,7 @@ func (s *server) AcquireJobWithCancel(stream proto.DRPCProvisionerDaemon_Acquire
logger.Error(streamCtx, "recv error and failed to cancel acquire job", slog.Error(recvErr))
// Well, this is awkward. We hit an error receiving from the stream, but didn't cancel before we locked a job
// in the database. We need to mark this job as failed so the end user can retry if they want to.
now := dbtime.Now()
now := s.timeNow()
err := s.Database.UpdateProvisionerJobWithCompleteByID(
//nolint:gocritic // Provisionerd has specific authz rules.
dbauthz.AsProvisionerd(context.Background()),
@@ -406,7 +408,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
err := s.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
ID: job.ID,
CompletedAt: sql.NullTime{
Time: dbtime.Now(),
Time: s.timeNow(),
Valid: true,
},
Error: sql.NullString{
@@ -414,7 +416,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
Valid: true,
},
ErrorCode: job.ErrorCode,
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
})
if err != nil {
return xerrors.Errorf("update provisioner job: %w", err)
@@ -792,7 +794,7 @@ func (s *server) UpdateJob(ctx context.Context, request *proto.UpdateJobRequest)
}
err = s.Database.UpdateProvisionerJobByID(ctx, database.UpdateProvisionerJobByIDParams{
ID: parsedID,
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
})
if err != nil {
return nil, xerrors.Errorf("update job: %w", err)
@@ -869,7 +871,7 @@ func (s *server) UpdateJob(ctx context.Context, request *proto.UpdateJobRequest)
err := s.Database.UpdateTemplateVersionDescriptionByJobID(ctx, database.UpdateTemplateVersionDescriptionByJobIDParams{
JobID: job.ID,
Readme: string(request.Readme),
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
})
if err != nil {
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.
return nil, xerrors.Errorf("job already completed")
}
job.CompletedAt = sql.NullTime{
Time: dbtime.Now(),
Time: s.timeNow(),
Valid: true,
}
job.Error = sql.NullString{
@@ -973,7 +975,7 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
err = s.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
ID: jobID,
CompletedAt: job.CompletedAt,
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
Error: job.Error,
ErrorCode: job.ErrorCode,
})
@@ -1008,7 +1010,7 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
if jobType.WorkspaceBuild.State != nil {
err = db.UpdateWorkspaceBuildProvisionerStateByID(ctx, database.UpdateWorkspaceBuildProvisionerStateByIDParams{
ID: input.WorkspaceBuildID,
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
ProvisionerState: jobType.WorkspaceBuild.State,
})
if err != nil {
@@ -1016,7 +1018,7 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
}
err = db.UpdateWorkspaceBuildDeadlineByID(ctx, database.UpdateWorkspaceBuildDeadlineByIDParams{
ID: input.WorkspaceBuildID,
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
Deadline: build.Deadline,
MaxDeadline: build.MaxDeadline,
})
@@ -1382,7 +1384,7 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
err = s.Database.UpdateTemplateVersionExternalAuthProvidersByJobID(ctx, database.UpdateTemplateVersionExternalAuthProvidersByJobIDParams{
JobID: jobID,
ExternalAuthProviders: json.RawMessage(externalAuthProvidersMessage),
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
})
if err != nil {
return nil, xerrors.Errorf("update template version external auth providers: %w", err)
@@ -1390,9 +1392,9 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
err = s.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
ID: jobID,
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
CompletedAt: sql.NullTime{
Time: dbtime.Now(),
Time: s.timeNow(),
Valid: true,
},
Error: completedError,
@@ -1687,9 +1689,9 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
err = s.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
ID: jobID,
UpdatedAt: dbtime.Now(),
UpdatedAt: s.timeNow(),
CompletedAt: sql.NullTime{
Time: dbtime.Now(),
Time: s.timeNow(),
Valid: true,
},
Error: sql.NullString{},
@@ -22,6 +22,7 @@ import (
"storj.io/drpc"
"cdr.dev/slog/sloggers/slogtest"
"github.com/coder/quartz"
"github.com/coder/serpent"
"github.com/coder/coder/v2/buildinfo"
@@ -1211,14 +1212,13 @@ func TestCompleteJob(t *testing.T) {
// Simulate the given time starting from now.
require.False(t, c.now.IsZero())
start := time.Now()
clock := quartz.NewMock(t)
clock.Set(c.now)
tss := &atomic.Pointer[schedule.TemplateScheduleStore]{}
uqhss := &atomic.Pointer[schedule.UserQuietHoursScheduleStore]{}
auditor := audit.NewMock()
srv, db, ps, pd := setup(t, false, &overrides{
timeNowFn: func() time.Time {
return c.now.Add(time.Since(start))
},
clock: clock,
templateScheduleStore: tss,
userQuietHoursScheduleStore: uqhss,
auditor: auditor,
@@ -2189,7 +2189,7 @@ type overrides struct {
externalAuthConfigs []*externalauth.Config
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
userQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
timeNowFn func() time.Time
clock *quartz.Mock
acquireJobLongPollDuration time.Duration
heartbeatFn func(ctx context.Context) error
heartbeatInterval time.Duration
@@ -2209,7 +2209,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
var externalAuthConfigs []*externalauth.Config
tss := testTemplateScheduleStore()
uqhss := testUserQuietHoursScheduleStore()
var timeNowFn func() time.Time
clock := quartz.NewReal()
pollDur := time.Duration(0)
if ov == nil {
ov = &overrides{}
@@ -2246,8 +2246,8 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
require.True(t, swapped)
}
}
if ov.timeNowFn != nil {
timeNowFn = ov.timeNowFn
if ov.clock != nil {
clock = ov.clock
}
auditPtr := &atomic.Pointer[audit.Auditor]{}
var auditor audit.Auditor = audit.NewMock()
@@ -2296,7 +2296,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
deploymentValues,
provisionerdserver.Options{
ExternalAuthConfigs: externalAuthConfigs,
TimeNowFn: timeNowFn,
Clock: clock,
OIDCConfig: &oauth2.Config{},
AcquireJobLongPollDur: pollDur,
HeartbeatInterval: ov.heartbeatInterval,