forked from adamlaska/boulder
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreloader.go
More file actions
84 lines (78 loc) · 1.98 KB
/
reloader.go
File metadata and controls
84 lines (78 loc) · 1.98 KB
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
// Package reloader provides a method to load a file whenever it changes.
package reloader
import (
"io/ioutil"
"os"
"time"
)
// Wrap time.Tick so we can override it in tests.
var makeTicker = func() (func(), <-chan time.Time) {
t := time.NewTicker(1 * time.Second)
return t.Stop, t.C
}
// Reloader represents an ongoing reloader task.
type Reloader struct {
stopChan chan<- struct{}
}
// Stop stops an active reloader, release its resources.
func (r *Reloader) Stop() {
r.stopChan <- struct{}{}
}
// A pointer we can override for testing.
var readFile = ioutil.ReadFile
// New loads the filename provided, and calls the callback. It then spawns a
// goroutine to check for updates to that file, calling the callback again with
// any new contents. The first load, and the first call to callback, are run
// synchronously, so it is easy for the caller to check for errors and fail
// fast. New will return an error if it occurs on the first load. Otherwise all
// errors are sent to the callback.
func New(filename string, dataCallback func([]byte) error, errorCallback func(error)) (*Reloader, error) {
if errorCallback == nil {
errorCallback = func(e error) {}
}
fileInfo, err := os.Stat(filename)
if err != nil {
return nil, err
}
b, err := readFile(filename)
if err != nil {
return nil, err
}
stopChan := make(chan struct{})
tickerStop, tickChan := makeTicker()
loop := func() {
for {
select {
case <-stopChan:
tickerStop()
return
case <-tickChan:
currentFileInfo, err := os.Stat(filename)
if err != nil {
errorCallback(err)
continue
}
if !currentFileInfo.ModTime().After(fileInfo.ModTime()) {
continue
}
b, err := readFile(filename)
if err != nil {
errorCallback(err)
continue
}
fileInfo = currentFileInfo
err = dataCallback(b)
if err != nil {
errorCallback(err)
}
}
}
}
err = dataCallback(b)
if err != nil {
tickerStop()
return nil, err
}
go loop()
return &Reloader{stopChan}, nil
}