github.com/DataDog/viper@v1.13.3/watch_config.go (about) 1 // +build !aix 2 3 package viper 4 5 import ( 6 "log" 7 "path/filepath" 8 "sync" 9 10 "github.com/fsnotify/fsnotify" 11 ) 12 13 func WatchConfig() { v.WatchConfig() } 14 15 func (v *Viper) WatchConfig() { 16 initWG := sync.WaitGroup{} 17 initWG.Add(1) 18 go func() { 19 watcher, err := fsnotify.NewWatcher() 20 if err != nil { 21 log.Fatal(err) 22 } 23 defer watcher.Close() 24 // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way 25 filename, err := v.getConfigFile() 26 if err != nil { 27 log.Printf("error: %v\n", err) 28 return 29 } 30 31 configFile := filepath.Clean(filename) 32 configDir, _ := filepath.Split(configFile) 33 realConfigFile, _ := filepath.EvalSymlinks(filename) 34 35 eventsWG := sync.WaitGroup{} 36 eventsWG.Add(1) 37 go func() { 38 for { 39 select { 40 case event, ok := <-watcher.Events: 41 if !ok { // 'Events' channel is closed 42 eventsWG.Done() 43 return 44 } 45 currentConfigFile, _ := filepath.EvalSymlinks(filename) 46 // we only care about the config file with the following cases: 47 // 1 - if the config file was modified or created 48 // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement) 49 const writeOrCreateMask = fsnotify.Write | fsnotify.Create 50 if (filepath.Clean(event.Name) == configFile && 51 event.Op&writeOrCreateMask != 0) || 52 (currentConfigFile != "" && currentConfigFile != realConfigFile) { 53 realConfigFile = currentConfigFile 54 err := v.ReadInConfig() 55 if err != nil { 56 log.Printf("error reading config file: %v\n", err) 57 } 58 if v.onConfigChange != nil { 59 v.onConfigChange(event) 60 } 61 } else if filepath.Clean(event.Name) == configFile && 62 event.Op&fsnotify.Remove&fsnotify.Remove != 0 { 63 eventsWG.Done() 64 return 65 } 66 67 case err, ok := <-watcher.Errors: 68 if ok { // 'Errors' channel is not closed 69 log.Printf("watcher error: %v\n", err) 70 } 71 eventsWG.Done() 72 return 73 } 74 } 75 }() 76 watcher.Add(configDir) 77 initWG.Done() // done initalizing the watch in this go routine, so the parent routine can move on... 78 eventsWG.Wait() // now, wait for event loop to end in this go-routine... 79 }() 80 initWG.Wait() // make sure that the go routine above fully ended before returning 81 }