Implement CancelWatchConfig which allows one to turn off the watcher

This commit is contained in:
Daniel Einspanjer 2018-10-10 14:05:22 -04:00
parent 62edee3196
commit c3ab84f775
3 changed files with 59 additions and 0 deletions

View file

@ -123,6 +123,9 @@ viper.OnConfigChange(func(e fsnotify.Event) {
}) })
``` ```
If you wish to stop watching the configPaths, simply call viper.CancelWatchConfig().
Note: This might be necessary if your tests involve trying out various config files.
### Reading Config from io.Reader ### Reading Config from io.Reader
Viper predefines many configuration sources such as files, environment Viper predefines many configuration sources such as files, environment

View file

@ -202,6 +202,7 @@ type Viper struct {
properties *properties.Properties properties *properties.Properties
onConfigChange func(fsnotify.Event) onConfigChange func(fsnotify.Event)
watchConfigCancel func() (waiter func())
} }
// New returns an initialized Viper instance. // New returns an initialized Viper instance.
@ -300,6 +301,13 @@ func (v *Viper) WatchConfig() {
eventsWG := sync.WaitGroup{} eventsWG := sync.WaitGroup{}
eventsWG.Add(1) eventsWG.Add(1)
v.watchConfigCancel = func() (waiter func()) {
watcher.Close()
v.watchConfigCancel = nil
return func() {
eventsWG.Wait()
}
}
go func() { go func() {
for { for {
select { select {
@ -346,6 +354,16 @@ func (v *Viper) WatchConfig() {
initWG.Wait() // make sure that the go routine above fully ended before returning initWG.Wait() // make sure that the go routine above fully ended before returning
} }
func CancelWatchConfig() (future func()) {
return v.CancelWatchConfig()
}
func (v *Viper) CancelWatchConfig() (future func()) {
if v.watchConfigCancel != nil {
return v.watchConfigCancel()
}
return func(){}
}
// SetConfigFile explicitly defines the path, name and extension of the config file. // SetConfigFile explicitly defines the path, name and extension of the config file.
// Viper will use this and not check any of the config paths. // Viper will use this and not check any of the config paths.
func SetConfigFile(in string) { v.SetConfigFile(in) } func SetConfigFile(in string) { v.SetConfigFile(in) }

View file

@ -1510,6 +1510,44 @@ func TestWatchFile(t *testing.T) {
assert.Equal(t, "baz", v.Get("foo")) assert.Equal(t, "baz", v.Get("foo"))
}) })
t.Run("file content changed after cancel", func(t *testing.T) {
// given a `config.yaml` file being watched
v, configFile, cleanup := newViperWithConfigFile(t)
defer cleanup()
_, err := os.Stat(configFile)
require.NoError(t, err)
t.Logf("test config file: %s\n", configFile)
// first run through with watching enabled
wg := sync.WaitGroup{}
wg.Add(1)
v.OnConfigChange(func(in fsnotify.Event) {
t.Logf("WatchConfig saw first config change")
wg.Done()
})
v.WatchConfig()
// when overwriting the file and waiting for the custom change notification handler to be triggered
err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
wg.Wait()
// then the config value should have changed
require.Nil(t, err)
assert.Equal(t, "baz", v.Get("foo"))
// cancel and wait for the canceling to finish.
waitForCancel := v.CancelWatchConfig()
v.OnConfigChange(func(in fsnotify.Event) {
t.Error("CancelWatchConfig did not prevent second change from being seen.")
})
waitForCancel()
err = ioutil.WriteFile(configFile, []byte("foo: quz\n"), 0640)
// We need to sleep a bit here because there isn't any signal of use for this invisible write.
time.Sleep(30 * time.Millisecond)
// the config value should still be the same.
require.Nil(t, err)
assert.Equal(t, "baz", v.Get("foo"), "CancelWatchConfig did not prevent second change from being seen.")
})
} }
func BenchmarkGetBool(b *testing.B) { func BenchmarkGetBool(b *testing.B) {