/
watcher.go
92 lines (77 loc) · 2.37 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
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/fsnotify/fsnotify"
"k8s.io/klog/v2"
)
type Watcher struct {
watcher *fsnotify.Watcher
configPath string
stopChan chan struct{}
callback func() error
}
func NewWatcher(configPath string, callback func() error) (*Watcher, error) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
klog.Warningf("config watch: failed to create the fsnotify watcher: %v", err)
return nil, err
}
err = watcher.Add(configPath)
if err != nil {
klog.Warningf("config watch: failed to watch configuration file %q: %v", configPath, err)
return nil, err
}
klog.Infof("config watch: added %q", configPath)
return &Watcher{
watcher: watcher,
configPath: configPath,
callback: callback,
stopChan: make(chan struct{}),
}, nil
}
func (cw *Watcher) Close() {
cw.watcher.Close()
}
func (cw *Watcher) Stop() {
cw.stopChan <- struct{}{}
}
// WaitUntilChanges wait until it notices the first change of the config file.
// It nevers rearm itself, so it fires at most once.
// Make sure this run on a separate (not main) goroutine: see https://github.com/fsnotify/fsnotify#faq
func (cw *Watcher) WaitUntilChanges() {
for {
select {
case <-cw.stopChan:
return
case event := <-cw.watcher.Events:
klog.V(2).Infof("config watch: fsnotify event from %q: %v", event.Name, event.Op)
if filterEvent(event) {
err := cw.callback()
if err != nil {
klog.Warning("config watch: callback failed for %q: %v", cw.configPath, err)
}
// we would need to rearm the watch, which we don't yet. So exit.
return
}
case err := <-cw.watcher.Errors:
// and yes, keep going
klog.Warningf("config watch: fsnotify error: %v", err)
}
}
}
func filterEvent(event fsnotify.Event) bool {
if (event.Op & fsnotify.Write) == fsnotify.Write {
return true
}
return (event.Op & fsnotify.Remove) == fsnotify.Remove
}