diff --git a/go.mod b/go.mod index 4bead8a6c..7bbd59c98 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( k8s.io/kube-openapi v0.0.0-20241127205056-99599406b04f ) -require github.com/go-co-op/gocron/v2 v2.12.4 +require github.com/go-co-op/gocron/v2 v2.14.2 require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect diff --git a/go.sum b/go.sum index 55dfbce30..1ef29d51d 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-co-op/gocron/v2 v2.12.4 h1:h1HWApo3T+61UrZqEY2qG1LUpDnB7tkYITxf6YIK354= -github.com/go-co-op/gocron/v2 v2.12.4/go.mod h1:xY7bJxGazKam1cz04EebrlP4S9q4iWdiAylMGP3jY9w= +github.com/go-co-op/gocron/v2 v2.14.2 h1:S6CbI7MVfD3S/aPJNLoSg2YcGyEqzEMwUopDejuT4Oc= +github.com/go-co-op/gocron/v2 v2.14.2/go.mod h1:ZF70ZwEqz0OO4RBXE1sNxnANy/zvwLcattWEFsqpKig= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -124,8 +124,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= diff --git a/vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml b/vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml index 99b237e39..cad029257 100644 --- a/vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml +++ b/vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/golangci/golangci-lint - rev: v1.55.2 + rev: v1.61.0 hooks: - id: golangci-lint - repo: https://github.com/TekWizely/pre-commit-golang diff --git a/vendor/github.com/go-co-op/gocron/v2/Makefile b/vendor/github.com/go-co-op/gocron/v2/Makefile index abaf708a9..de747147a 100644 --- a/vendor/github.com/go-co-op/gocron/v2/Makefile +++ b/vendor/github.com/go-co-op/gocron/v2/Makefile @@ -6,7 +6,7 @@ fmt: @go list -f {{.Dir}} ./... | xargs -I{} gofmt -w -s {} lint: - @grep "^func " example_test.go | sort -c + @grep "^func [a-zA-Z]" example_test.go | sort -c @golangci-lint run test: diff --git a/vendor/github.com/go-co-op/gocron/v2/README.md b/vendor/github.com/go-co-op/gocron/v2/README.md index 4a1de758e..483730be2 100644 --- a/vendor/github.com/go-co-op/gocron/v2/README.md +++ b/vendor/github.com/go-co-op/gocron/v2/README.md @@ -151,6 +151,7 @@ The provided NewLogger uses the standard library's log package. ### Metrics Metrics may be collected from the execution of each job. - [**Monitor**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#Monitor): +- [**MonitorStatus**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#MonitorStatus) (includes status and error (if any) of the Job) A monitor can be used to collect metrics for each job from a scheduler. - Implementations: [go-co-op monitors](https://github.com/go-co-op?q=-monitor&type=all&language=&sort=) (don't see what you need? request on slack to get a repo created to contribute it!) @@ -168,8 +169,11 @@ We appreciate the support for free and open source software! This project is supported by: -- [Jetbrains](https://www.jetbrains.com/?from=gocron) -- [Sentry](https://sentry.io/welcome/) +[Jetbrains](https://www.jetbrains.com/?from=gocron) +![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png) + + +[Sentry](https://sentry.io/welcome/) ## Star History diff --git a/vendor/github.com/go-co-op/gocron/v2/executor.go b/vendor/github.com/go-co-op/gocron/v2/executor.go index af4b7c985..3a9dc0d33 100644 --- a/vendor/github.com/go-co-op/gocron/v2/executor.go +++ b/vendor/github.com/go-co-op/gocron/v2/executor.go @@ -49,6 +49,8 @@ type executor struct { locker Locker // monitor for reporting metrics monitor Monitor + // monitorStatus for reporting metrics + monitorStatus MonitorStatus } type jobIn struct { @@ -368,7 +370,7 @@ func (e *executor) runJob(j internalJob, jIn jobIn) { e.incrementJobCounter(j, Skip) return } - } else if j.locker != nil { + } else if !j.disabledLocker && j.locker != nil { lock, err := j.locker.Lock(j.ctx, j.name) if err != nil { _ = callJobFuncWithParams(j.afterLockError, j.id, j.name, err) @@ -377,7 +379,7 @@ func (e *executor) runJob(j internalJob, jIn jobIn) { return } defer func() { _ = lock.Unlock(j.ctx) }() - } else if e.locker != nil { + } else if !j.disabledLocker && e.locker != nil { lock, err := e.locker.Lock(j.ctx, j.name) if err != nil { _ = callJobFuncWithParams(j.afterLockError, j.id, j.name, err) @@ -387,8 +389,21 @@ func (e *executor) runJob(j internalJob, jIn jobIn) { } defer func() { _ = lock.Unlock(j.ctx) }() } + _ = callJobFuncWithParams(j.beforeJobRuns, j.id, j.name) + err := callJobFuncWithParams(j.beforeJobRunsSkipIfBeforeFuncErrors, j.id, j.name) + if err != nil { + e.sendOutForRescheduling(&jIn) + + select { + case e.jobsOutCompleted <- j.id: + case <-e.ctx.Done(): + } + + return + } + e.sendOutForRescheduling(&jIn) select { case e.jobsOutCompleted <- j.id: @@ -396,7 +411,6 @@ func (e *executor) runJob(j internalJob, jIn jobIn) { } startTime := time.Now() - var err error if j.afterJobRunsWithPanic != nil { err = e.callJobWithRecover(j) } else { @@ -406,9 +420,11 @@ func (e *executor) runJob(j internalJob, jIn jobIn) { if err != nil { _ = callJobFuncWithParams(j.afterJobRunsWithError, j.id, j.name, err) e.incrementJobCounter(j, Fail) + e.recordJobTimingWithStatus(startTime, time.Now(), j, Fail, err) } else { _ = callJobFuncWithParams(j.afterJobRuns, j.id, j.name) e.incrementJobCounter(j, Success) + e.recordJobTimingWithStatus(startTime, time.Now(), j, Success, nil) } } @@ -431,6 +447,12 @@ func (e *executor) recordJobTiming(start time.Time, end time.Time, j internalJob } } +func (e *executor) recordJobTimingWithStatus(start time.Time, end time.Time, j internalJob, status JobStatus, err error) { + if e.monitorStatus != nil { + e.monitorStatus.RecordJobTimingWithStatus(start, end, j.id, j.name, j.tags, status, err) + } +} + func (e *executor) incrementJobCounter(j internalJob, status JobStatus) { if e.monitor != nil { e.monitor.IncrementJob(j.id, j.name, j.tags, status) diff --git a/vendor/github.com/go-co-op/gocron/v2/job.go b/vendor/github.com/go-co-op/gocron/v2/job.go index 890889eda..700a0b656 100644 --- a/vendor/github.com/go-co-op/gocron/v2/job.go +++ b/vendor/github.com/go-co-op/gocron/v2/job.go @@ -40,11 +40,13 @@ type internalJob struct { startImmediately bool stopTime time.Time // event listeners - afterJobRuns func(jobID uuid.UUID, jobName string) - beforeJobRuns func(jobID uuid.UUID, jobName string) - afterJobRunsWithError func(jobID uuid.UUID, jobName string, err error) - afterJobRunsWithPanic func(jobID uuid.UUID, jobName string, recoverData any) - afterLockError func(jobID uuid.UUID, jobName string, err error) + afterJobRuns func(jobID uuid.UUID, jobName string) + beforeJobRuns func(jobID uuid.UUID, jobName string) + beforeJobRunsSkipIfBeforeFuncErrors func(jobID uuid.UUID, jobName string) error + afterJobRunsWithError func(jobID uuid.UUID, jobName string, err error) + afterJobRunsWithPanic func(jobID uuid.UUID, jobName string, recoverData any) + afterLockError func(jobID uuid.UUID, jobName string, err error) + disabledLocker bool locker Locker } @@ -311,8 +313,7 @@ type Weekdays func() []time.Weekday // NewWeekdays provide the days of the week the job should run. func NewWeekdays(weekday time.Weekday, weekdays ...time.Weekday) Weekdays { return func() []time.Weekday { - weekdays = append(weekdays, weekday) - return weekdays + return append([]time.Weekday{weekday}, weekdays...) } } @@ -400,8 +401,7 @@ type DaysOfTheMonth func() days // -5 == 5 days before the end of the month. func NewDaysOfTheMonth(day int, moreDays ...int) DaysOfTheMonth { return func() days { - moreDays = append(moreDays, day) - return moreDays + return append([]int{day}, moreDays...) } } @@ -413,6 +413,14 @@ func (a atTime) time(location *time.Location) time.Time { return time.Date(0, 0, 0, int(a.hours), int(a.minutes), int(a.seconds), 0, location) } +// TimeFromAtTime is a helper function to allow converting AtTime into a time.Time value +// Note: the time.Time value will have zero values for all Time fields except Hours, Minutes, Seconds. +// +// For example: time.Date(0, 0, 0, 1, 1, 1, 0, time.UTC) +func TimeFromAtTime(at AtTime, loc *time.Location) time.Time { + return at().time(loc) +} + // AtTime defines a function that returns the internal atTime type AtTime func() atTime @@ -431,8 +439,7 @@ type AtTimes func() []AtTime // the job should be run func NewAtTimes(atTime AtTime, atTimes ...AtTime) AtTimes { return func() []AtTime { - atTimes = append(atTimes, atTime) - return atTimes + return append([]AtTime{atTime}, atTimes...) } } @@ -551,6 +558,16 @@ func WithDistributedJobLocker(locker Locker) JobOption { } } +// WithDisabledDistributedJobLocker disables the distributed job locker. +// This is useful when a global distributed locker has been set on the scheduler +// level using WithDistributedLocker and need to be disabled for specific jobs. +func WithDisabledDistributedJobLocker(disabled bool) JobOption { + return func(j *internalJob, _ time.Time) error { + j.disabledLocker = disabled + return nil + } +} + // WithEventListeners sets the event listeners that should be // run for the job. func WithEventListeners(eventListeners ...EventListener) JobOption { @@ -708,6 +725,19 @@ func BeforeJobRuns(eventListenerFunc func(jobID uuid.UUID, jobName string)) Even } } +// BeforeJobRunsSkipIfBeforeFuncErrors is used to listen for when a job is about to run and +// then runs the provided function. If the provided function returns an error, the job will be +// rescheduled and the current run will be skipped. +func BeforeJobRunsSkipIfBeforeFuncErrors(eventListenerFunc func(jobID uuid.UUID, jobName string) error) EventListener { + return func(j *internalJob) error { + if eventListenerFunc == nil { + return ErrEventListenerFuncNil + } + j.beforeJobRunsSkipIfBeforeFuncErrors = eventListenerFunc + return nil + } +} + // AfterJobRuns is used to listen for when a job has run // without an error, and then run the provided function. func AfterJobRuns(eventListenerFunc func(jobID uuid.UUID, jobName string)) EventListener { @@ -1099,12 +1129,14 @@ func (j job) RunNow() error { defer cancel() resp := make(chan error, 1) + t := time.NewTimer(100 * time.Millisecond) select { case j.runJobRequest <- runJobRequest{ id: j.id, outChan: resp, }: - case <-time.After(100 * time.Millisecond): + t.Stop() + case <-t.C: return ErrJobRunNowFailed } var err error diff --git a/vendor/github.com/go-co-op/gocron/v2/monitor.go b/vendor/github.com/go-co-op/gocron/v2/monitor.go index d3c5bbd9a..4f25fd869 100644 --- a/vendor/github.com/go-co-op/gocron/v2/monitor.go +++ b/vendor/github.com/go-co-op/gocron/v2/monitor.go @@ -26,3 +26,11 @@ type Monitor interface { // to handle instantiating and recording the value RecordJobTiming(startTime, endTime time.Time, id uuid.UUID, name string, tags []string) } + +// MonitorStatus extends RecordJobTiming with the job status. +type MonitorStatus interface { + Monitor + // RecordJobTimingWithStatus will provide details about the job, its status, error and the timing and expects the underlying implementation + // to handle instantiating and recording the value + RecordJobTimingWithStatus(startTime, endTime time.Time, id uuid.UUID, name string, tags []string, status JobStatus, err error) +} diff --git a/vendor/github.com/go-co-op/gocron/v2/scheduler.go b/vendor/github.com/go-co-op/gocron/v2/scheduler.go index 90ff52125..825323ef5 100644 --- a/vendor/github.com/go-co-op/gocron/v2/scheduler.go +++ b/vendor/github.com/go-co-op/gocron/v2/scheduler.go @@ -241,9 +241,11 @@ func (s *scheduler) stopScheduler() { } var err error if s.started { + t := time.NewTimer(s.exec.stopTimeout + 1*time.Second) select { case err = <-s.exec.done: - case <-time.After(s.exec.stopTimeout + 1*time.Second): + t.Stop() + case <-t.C: err = ErrStopExecutorTimedOut } } @@ -741,20 +743,27 @@ func (s *scheduler) StopJobs() error { return nil case s.stopCh <- struct{}{}: } + + t := time.NewTimer(s.exec.stopTimeout + 2*time.Second) select { case err := <-s.stopErrCh: + t.Stop() return err - case <-time.After(s.exec.stopTimeout + 2*time.Second): + case <-t.C: return ErrStopSchedulerTimedOut } } func (s *scheduler) Shutdown() error { s.shutdownCancel() + + t := time.NewTimer(s.exec.stopTimeout + 2*time.Second) select { case err := <-s.stopErrCh: + + t.Stop() return err - case <-time.After(s.exec.stopTimeout + 2*time.Second): + case <-t.C: return ErrStopSchedulerTimedOut } } @@ -809,6 +818,8 @@ func WithDistributedElector(elector Elector) SchedulerOption { // WithDistributedLocker sets the locker to be used by multiple // Scheduler instances to ensure that only one instance of each // job is run. +// To disable this global locker for specific jobs, see +// WithDisabledDistributedJobLocker. func WithDistributedLocker(locker Locker) SchedulerOption { return func(s *scheduler) error { if locker == nil { @@ -945,3 +956,14 @@ func WithMonitor(monitor Monitor) SchedulerOption { return nil } } + +// WithMonitorStatus sets the metrics provider to be used by the Scheduler. +func WithMonitorStatus(monitor MonitorStatus) SchedulerOption { + return func(s *scheduler) error { + if monitor == nil { + return ErrWithMonitorNil + } + s.exec.monitorStatus = monitor + return nil + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 40938b036..dcbf94df2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -33,7 +33,7 @@ github.com/fsnotify/fsnotify/internal # github.com/fxamacker/cbor/v2 v2.7.0 ## explicit; go 1.17 github.com/fxamacker/cbor/v2 -# github.com/go-co-op/gocron/v2 v2.12.4 +# github.com/go-co-op/gocron/v2 v2.14.2 ## explicit; go 1.20 github.com/go-co-op/gocron/v2 # github.com/go-logr/logr v1.4.2