-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdaily-runner.go
135 lines (111 loc) · 3.36 KB
/
daily-runner.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
package main
import (
"cloud.google.com/go/civil"
"fmt"
"github.com/nightlyone/lockfile"
"log"
"os"
"path"
"time"
)
func shouldRun(lastRun time.Time, atTime time.Time, configuration Configuration, loc *time.Location) bool {
if configuration.HasPreferredRunTime {
preferredRunTimeInstance := timeInstanceFromLocalTime(configuration.PreferredRunTime, atTime, loc)
upperLimitTime := preferredRunTimeInstance.Add(configuration.Interval + 10*time.Second)
if preferredRunTimeInstance.Before(atTime) &&
upperLimitTime.After(atTime) {
return true
}
}
if lastRun.Add(24 * time.Hour).Before(atTime) {
return true
}
return false
}
func timeInstanceFromLocalTime(localTime civil.Time, dateSource time.Time, loc *time.Location) time.Time {
localDateTime := civil.DateTime{
Date: civil.DateOf(dateSource),
Time: localTime,
}
return localDateTime.In(loc)
}
func main() {
configuration := ParseConfigFromFlags()
SetProfile(configuration.Profile)
fmt.Printf("Starting daily-runner with configuration:\n%+v\n", configuration)
fmt.Println("Please see logs at: ", WrapperLogFilePath())
logFile := setupWrapperLogger()
defer func() {
if e := logFile.Close(); e != nil {
panic(e)
}
}()
log.Printf("Starting daily-runner with configuration:\n%+v\n", configuration)
if configuration.RunNow {
runCommandAndLogTime(configuration)
} else {
runSingleProcess(configuration)
}
defer func() {
if x := recover(); x != nil {
log.Println(x)
panic(x)
}
}()
}
func setupWrapperLogger() *os.File {
logFilePath := WrapperLogFilePath()
if err := os.MkdirAll(path.Dir(logFilePath), 0755); err != nil {
panic(fmt.Errorf("unable to create directories to write logfile %v, %v", logFilePath, err))
}
f, err := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(fmt.Errorf("error opening log file: %v", err))
}
log.SetOutput(f)
return f
}
// This function employs a pid lockfile so that only one process of daily-runner is running at one time
func runSingleProcess(configuration Configuration) {
lock, err := lockfile.New(LockFilePath())
if err != nil {
panic(err) // handle properly please!
}
// Error handling is essential, as we only try to get the lock.
if err = lock.TryLock(); err != nil {
fmt.Println("Another process of daily-runner for profile " + GetProfile() + " is already running.")
os.Exit(1)
}
defer func() {
if err := lock.Unlock(); err != nil {
fmt.Printf("Cannot unlock %q, reason: %v", lock, err)
panic(err)
}
}()
mainLoop(configuration)
}
func mainLoop(configuration Configuration) {
log.Print("Starting main loop")
for {
hasLastRun := LastRunTimeExists()
log.Printf("Deciding whether to run with hasLastRun: %v", hasLastRun)
if hasLastRun {
log.Printf("Last run read from file: %v", ReadLastRunTime())
}
if !hasLastRun || shouldRun(ReadLastRunTime(), time.Now(), configuration, time.Local) {
runCommandAndLogTime(configuration)
}
time.Sleep(configuration.Interval)
}
}
func runCommandAndLogTime(configuration Configuration) {
// Reading the time before starting the operation as an upload cat take many days to upload
// and the start time is more indicative of the backup's freshness
startTime := time.Now()
if RunCommand(configuration) {
log.Printf("Writing time to file %v", startTime)
WriteLastRunTime(startTime)
} else {
log.Printf("Command failed to run")
}
}