This repository has been archived by the owner on Oct 23, 2020. It is now read-only.
/
cron.go
168 lines (140 loc) · 3.82 KB
/
cron.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package gitnotify
import (
"fmt"
"log"
"reflect"
"sync"
"time"
"gopkg.in/sairam/cron.v2"
)
// 1. load all files to get cron schedules, next_scheduled_date
// 2. if next_scheduled_date is in past, schedule a job now - NOTE: this is going to open a flood gate in case of backlog
// 3. find the next run date, save to file
// 4. Once the job runs, get the next date and save to file
// 5. When a user saves the schedule, delete the old schedule reference, add the cron and save the next date to file
var (
cronLocker sync.Mutex
crons *cron.Cron
runningCrons = make(map[string]cron.EntryID)
)
func isCronPresentFor(filename string) bool {
cronLocker.Lock()
id := runningCrons[filename]
cronLocker.Unlock()
entry := crons.Entry(id)
return entry.Valid()
}
func checkCronEntries(filename string) (nextRunTimes []string) {
nextRunTimes = make([]string, 0, 15)
cronLocker.Lock()
id := runningCrons[filename]
cronLocker.Unlock()
entry := crons.Entry(id)
if !entry.Valid() {
return
}
conf := new(Setting)
conf.load(filename)
tz := conf.User.TimeZoneName
loc, _ := time.LoadLocation(tz)
t := entry.Next
nextRunTimes = append(nextRunTimes, t.In(loc).String())
for i := 0; i < cap(nextRunTimes)-1; i++ {
t = entry.Schedule.Next(t)
nextRunTimes = append(nextRunTimes, t.In(loc).String())
}
return
}
func hasUserNotificationSet(s *Setting) bool {
return isValidEmail(s.usersEmail()) || s.User.isValidWebhook()
}
func upsertCronEntry(s *Setting) {
cronLocker.Lock()
defer cronLocker.Unlock()
tzName := s.User.TimeZoneName
hour := s.User.Hour
// weekday is day of week
weekday := s.User.WeekDay
a := s.Auth
filename := a.getConfigFile()
if weekday == "" || hour == "" || tzName == "" || !hasUserNotificationSet(s) {
log.Printf("Not starting cron for `%s` since attributes are not set\n", s.Auth.UserName)
stopCronIfAlreadyRunning(filename)
return
}
if s.User.Disabled == true {
log.Printf("User `%s` does not want any emails/notifications\n", s.Auth.UserName)
stopCronIfAlreadyRunning(filename)
return
}
log.Printf("(re)starting cron for %s/%s\n", s.Auth.Provider, s.Auth.UserName)
cronEntry := fmt.Sprintf("TZ=%s 0 0 %s * * %s", tzName, hour, weekday)
toStart := true
id := runningCrons[filename]
if id != 0 {
// check if entry was not modified
var entry cron.Entry
entry = crons.Entry(id)
if entry.Valid() {
s, _ := cron.Parse(cronEntry)
s2 := s.(cron.Schedule)
scheduleChanged := !compareSchedules(entry.Schedule, s2)
if scheduleChanged {
crons.Remove(id)
runningCrons[filename] = 0
toStart = true
} else {
toStart = false
}
}
}
if toStart {
startCronFor(cronEntry, filename)
}
}
func stopCronIfAlreadyRunning(filename string) {
id := runningCrons[filename]
entry := crons.Entry(id)
if entry.Valid() {
crons.Remove(id)
runningCrons[filename] = 0
}
}
type cronJob struct {
filename string
save bool
}
func (t cronJob) Run() {
filename := t.filename
conf := new(Setting)
statCount("cron.run")
log.Printf("Processing file through cron - %s", filename)
conf.load(filename)
processDiffForUser(conf)
if t.save {
conf.save(filename)
}
statCount("cron.ran")
}
func startCronFor(cronEntry, filename string) {
id, _ := crons.AddJob(cronEntry, cronJob{filename, true})
runningCrons[filename] = id
}
// InitCron initialises the cron related methods
func InitCron() {
crons = cron.New()
crons.Start()
if config.Providers[GithubProvider] != "" {
go getData(GithubProvider)
}
if config.Providers[GitlabProvider] != "" {
go getData(GitlabProvider)
}
}
// There is no idiomatic way to compare SpecSchedule, put in a sort of adjustment
func compareSchedules(schedule1, schedule2 cron.Schedule) bool {
v1 := reflect.ValueOf(schedule1).Elem()
v2 := reflect.ValueOf(schedule2).Elem()
s1, s2 := fmt.Sprintf("%v", v1), fmt.Sprintf("%v", v2)
return (s1 == s2)
}