/
watcher.go
123 lines (114 loc) · 2.78 KB
/
watcher.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
package openssl
import (
"context"
"errors"
"os"
"path/filepath"
"time"
"github.com/fsnotify/fsnotify"
"github.com/sirupsen/logrus"
"gopkg.hrry.dev/ocsprey/ca"
"gopkg.hrry.dev/ocsprey/internal/log"
)
func (txt *IndexTXT) WatchFiles(ctx context.Context) error {
wtr, err := fsnotify.NewWatcher()
if err != nil {
return err
}
w := watcher{txt: txt, watcher: wtr}
for _, cfg := range txt.cfgs {
if err = w.watcher.Add(cfg.Index); err != nil {
return err
}
}
go w.watch(ctx)
return nil
}
type watcher struct {
txt *IndexTXT
watcher *fsnotify.Watcher
}
func (w *watcher) watch(ctx context.Context) {
defer w.watcher.Close()
logger := log.ContextLogger(ctx).
WithField("component", "openssl-index-file-watcher")
logger.WithField("files", w.watcher.WatchList()).Info("watching for file changes")
for {
select {
case <-ctx.Done():
logger.Info("closing file watcher")
return
case event, ok := <-w.watcher.Events:
if !ok {
return
}
l := logger.WithFields(logrus.Fields{
"op": event.Op.String(),
"name": event.Name,
"time": time.Now(),
})
l.Debug("received file watcher event")
err := w.handleEvent(l, &event)
if err != nil {
l.WithError(err).Error("failed to handle file event")
}
case err, ok := <-w.watcher.Errors:
if !ok {
return
}
logger.WithError(err).Error("received error from file watcher")
}
}
}
func (w *watcher) handleEvent(l logrus.FieldLogger, event *fsnotify.Event) error {
// If the operation was REMOVE but the file still exists then we will want
// to add it back to the pool of files being watched.
if isOp(event, fsnotify.Remove) && exists(event.Name) {
err := w.watcher.Add(event.Name)
if err != nil {
l.WithError(err).Error("failed to add file back to event listener pool")
return err
}
}
ix, cfg := w.findIndexConfig(event.Name)
if cfg == nil {
return errors.New("got event for untracked index config")
}
f, err := os.Open(event.Name)
if err != nil {
return err
}
defer f.Close()
entries, err := parseIndex(f)
if err != nil {
return err
}
for _, entry := range entries {
k := key(entry.serial, ix)
w.txt.mu.Lock()
old, ok := w.txt.certs[k]
// The in-memory status should hold precedence over the new file changes
if ok && old.Status != ca.Valid && entry.Status == ca.Valid {
entry.Status = old.Status
}
w.txt.certs[k] = *entry
w.txt.mu.Unlock()
}
return nil
}
func isOp(event *fsnotify.Event, op fsnotify.Op) bool {
return event.Op&op == op
}
func (w *watcher) findIndexConfig(filename string) (uint8, *IndexConfig) {
fn := filepath.Clean(filename)
for i, cfg := range w.txt.cfgs {
if filepath.Clean(cfg.Index) == fn {
return uint8(i), &cfg
}
}
return 0, nil
}
func exists(s string) bool {
_, err := os.Stat(s)
return !os.IsNotExist(err)
}