fix hot reloading of configuration on macOS and with text editors that deletes and recreates the configuration file
This commit is contained in:
parent
e0845a0af8
commit
0f11dd5c71
|
@ -2,11 +2,17 @@ package confwatcher
|
|||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
const (
|
||||
minInterval = 1 * time.Second
|
||||
additionalWait = 10 * time.Millisecond
|
||||
)
|
||||
|
||||
// ConfWatcher is a configuration file watcher.
|
||||
type ConfWatcher struct {
|
||||
inner *fsnotify.Watcher
|
||||
|
@ -24,7 +30,10 @@ func New(confPath string) (*ConfWatcher, error) {
|
|||
}
|
||||
|
||||
if _, err := os.Stat(confPath); err == nil {
|
||||
err := inner.Add(confPath)
|
||||
// use absolute path to support Darwin
|
||||
absolutePath, _ := filepath.Abs(confPath)
|
||||
|
||||
err := inner.Add(absolutePath)
|
||||
if err != nil {
|
||||
inner.Close()
|
||||
return nil, err
|
||||
|
@ -54,13 +63,22 @@ func (w *ConfWatcher) Close() {
|
|||
func (w *ConfWatcher) run() {
|
||||
defer close(w.done)
|
||||
|
||||
var lastCalled time.Time
|
||||
|
||||
outer:
|
||||
for {
|
||||
select {
|
||||
case event := <-w.inner.Events:
|
||||
if (event.Op & fsnotify.Write) == fsnotify.Write {
|
||||
// wait some additional time to avoid EOF
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
if time.Since(lastCalled) < minInterval {
|
||||
continue
|
||||
}
|
||||
|
||||
if (event.Op&fsnotify.Write) == fsnotify.Write ||
|
||||
(event.Op&fsnotify.Create) == fsnotify.Create {
|
||||
// wait some additional time to allow the writer to complete its job
|
||||
time.Sleep(additionalWait)
|
||||
|
||||
lastCalled = time.Now()
|
||||
w.signal <- struct{}{}
|
||||
}
|
||||
|
||||
|
@ -72,7 +90,7 @@ outer:
|
|||
close(w.signal)
|
||||
}
|
||||
|
||||
// Watch returns when the configuration file has changed.
|
||||
// Watch returns a channel that is called when the configuration file has changed.
|
||||
func (w *ConfWatcher) Watch() chan struct{} {
|
||||
return w.signal
|
||||
}
|
||||
|
|
30
main.go
30
main.go
|
@ -68,13 +68,20 @@ func newProgram(args []string) (*program, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = p.createResources(true)
|
||||
err = p.createDynamicResources(true)
|
||||
if err != nil {
|
||||
p.closeResources()
|
||||
p.closeAllResources()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.confWatcher, err = confwatcher.New(p.confPath)
|
||||
if err != nil {
|
||||
p.closeAllResources()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go p.run()
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -110,10 +117,10 @@ outer:
|
|||
}
|
||||
}
|
||||
|
||||
p.closeResources()
|
||||
p.closeAllResources()
|
||||
}
|
||||
|
||||
func (p *program) createResources(initial bool) error {
|
||||
func (p *program) createDynamicResources(initial bool) error {
|
||||
var err error
|
||||
|
||||
if p.stats == nil {
|
||||
|
@ -187,17 +194,10 @@ func (p *program) createResources(initial bool) error {
|
|||
p.pathMan, p.serverTcp, p)
|
||||
}
|
||||
|
||||
if p.confWatcher == nil {
|
||||
p.confWatcher, err = confwatcher.New(p.confPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *program) closeResources() {
|
||||
func (p *program) closeAllResources() {
|
||||
if p.confWatcher != nil {
|
||||
p.confWatcher.Close()
|
||||
}
|
||||
|
@ -243,10 +243,6 @@ func (p *program) reloadConf() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// always recreate confWatcher to avoid reloading twice
|
||||
p.confWatcher.Close()
|
||||
p.confWatcher = nil
|
||||
|
||||
closeLogHandler := false
|
||||
if !reflect.DeepEqual(conf.LogDestinationsParsed, p.conf.LogDestinationsParsed) ||
|
||||
conf.LogFile != p.conf.LogFile {
|
||||
|
@ -345,7 +341,7 @@ func (p *program) reloadConf() error {
|
|||
}
|
||||
|
||||
p.conf = conf
|
||||
return p.createResources(false)
|
||||
return p.createDynamicResources(false)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
Loading…
Reference in New Issue