summaryrefslogtreecommitdiff
path: root/service/reload/watcher.go
diff options
context:
space:
mode:
Diffstat (limited to 'service/reload/watcher.go')
-rw-r--r--service/reload/watcher.go285
1 files changed, 103 insertions, 182 deletions
diff --git a/service/reload/watcher.go b/service/reload/watcher.go
index 8b1ee7b2..da8007a3 100644
--- a/service/reload/watcher.go
+++ b/service/reload/watcher.go
@@ -10,46 +10,20 @@ import (
"time"
)
-// An Op is a type that is used to describe what type
-// of event has occurred during the watching process.
-type Op uint32
-
-// Ops
-const (
- Create Op = iota
- Write
- Remove
- Rename
- Chmod
- Move
-)
-
-var ops = map[Op]string{
- Create: "CREATE",
- Write: "WRITE",
- Remove: "REMOVE",
- Rename: "RENAME",
- Chmod: "CHMOD",
- Move: "MOVE",
-}
-
var ErrorSkip = errors.New("file is skipped")
var NoWalkerConfig = errors.New("should add at least one walker config, when reload is set to true")
// SimpleHook is used to filter by simple criteria, CONTAINS
-type SimpleHook func(filename, pattern string) error
+type SimpleHook func(filename string, pattern []string) error
// An Event describes an event that is received when files or directory
// changes occur. It includes the os.FileInfo of the changed file or
// directory and the type of event that's occurred and the full path of the file.
type Event struct {
- Op
+ path string
+ info os.FileInfo
- Path string
- OldPath string
- os.FileInfo
-
- Type string // type of event, http, grpc, etc...
+ service string // type of service, http, grpc, etc...
}
type WatcherConfig struct {
@@ -60,20 +34,20 @@ type WatcherConfig struct {
// directories used per-service
directories []string
// simple hook, just CONTAINS
- filterHooks SimpleHook
+ filterHooks func(filename string, pattern []string) error
// path to file with files
files map[string]os.FileInfo
- // //ignored files or directories, used map for O(1) amortized get
+ // ignored directories, used map for O(1) amortized get
ignored map[string]string
+ // filePatterns to ignore
+ filePatterns []string
}
type Watcher struct {
// main event channel
Event chan Event
+ close chan struct{}
- errors chan error
- close chan struct{}
- Closed chan struct{}
//=============================
mu *sync.Mutex
wg *sync.WaitGroup
@@ -83,9 +57,6 @@ type Watcher struct {
// working directory, same for all
workingDir string
- // operation type
- operations map[Op]struct{} // Op filtering.
-
// config for each service
// need pointer here to assign files
watcherConfigs map[string]WatcherConfig
@@ -94,22 +65,15 @@ type Watcher struct {
// Options is used to set Watcher Options
type Options func(*Watcher)
-func NewWatcher(configs []WatcherConfig, options ...Options) (*Watcher, error) {
- dir, err := os.Getwd()
- if err != nil {
- return nil, err
- }
-
+func NewWatcher(workDir string, configs []WatcherConfig, options ...Options) (*Watcher, error) {
w := &Watcher{
Event: make(chan Event),
mu: &sync.Mutex{},
wg: &sync.WaitGroup{},
- Closed: make(chan struct{}),
- close: make(chan struct{}),
+ close: make(chan struct{}),
- workingDir: dir,
- operations: make(map[Op]struct{}),
+ workingDir: workDir,
watcherConfigs: make(map[string]WatcherConfig),
}
@@ -126,7 +90,7 @@ func NewWatcher(configs []WatcherConfig, options ...Options) (*Watcher, error) {
return nil, NoWalkerConfig
}
- err = w.initFs()
+ err := w.initFs()
if err != nil {
return nil, err
}
@@ -140,6 +104,7 @@ func (w *Watcher) initFs() error {
if err != nil {
return err
}
+ // workaround. in golang you can't assign to map in struct field
tmp := w.watcherConfigs[srvName]
tmp.files = fileList
w.watcherConfigs[srvName] = tmp
@@ -147,6 +112,21 @@ func (w *Watcher) initFs() error {
return nil
}
+func ConvertIgnored(workdir string, ignored []string) map[string]string {
+ abs, _ := filepath.Abs(workdir)
+ if len(ignored) == 0 {
+ return nil
+ }
+
+ ign := make(map[string]string, len(ignored))
+ for i := 0; i < len(ignored); i++ {
+ ign[filepath.Join(abs, ignored[i])] = filepath.Join(abs, ignored[i])
+ }
+
+ return ign
+
+}
+
func (w *Watcher) AddWatcherConfig(config WatcherConfig) {
w.watcherConfigs[config.serviceName] = config
}
@@ -260,7 +240,6 @@ func (w *Watcher) retrieveFilesSingle(serviceName, path string) (map[string]os.F
// recursive calls are slow in compare to goto
// so, we will add files with goto pattern
-
outer:
for i := 0; i < len(fileInfoList); i++ {
var pathToFile string
@@ -277,9 +256,9 @@ outer:
continue
}
- err := w.watcherConfigs[serviceName].filterHooks(fileInfoList[i].Name(), pathToFile)
- if err != nil {
- // if err is not nil, move to the start of the cycle since the pathToFile not match the hook
+ // if filename does not contain pattern --> ignore that file
+ err = w.watcherConfigs[serviceName].filterHooks(fileInfoList[i].Name(), w.watcherConfigs[serviceName].filePatterns)
+ if err == ErrorSkip {
continue outer
}
@@ -309,48 +288,24 @@ func (w *Watcher) StartPolling(duration time.Duration) error {
return w.waitEvent(duration)
}
-//func (w *Watcher) updatedFileListForConfig(config WatcherConfig) (map[string]os.FileInfo, error) {
-// if config.recursive {
-// return nil, nil
-// }
-//
-// for _, v := range config.directories {
-// files, err := w.retrieveFilesSingle(path.Join(w.workingDir, v))
-// if err != nil {
-// return nil, err
-// }
-//
-// }
-//
-// return nil, nil
-//}
-
// this is blocking operation
func (w *Watcher) waitEvent(d time.Duration) error {
+ ticker := time.NewTicker(d)
for {
- cancel := make(chan struct{})
- ticker := time.NewTicker(d)
- for {
- select {
- case <-w.close:
- close(cancel)
- close(w.Closed)
- return nil
- case <-ticker.C:
- //fileList := make(map[string]os.FileInfo, 100)
- //w.mu.Lock()
-
- for serviceName, config := range w.watcherConfigs {
- go func(sn string, c WatcherConfig) {
- fileList, _ := w.retrieveFileList(sn, c)
- w.pollEvents(c.serviceName, fileList, cancel)
- }(serviceName, config)
- }
- default:
-
+ select {
+ case <-w.close:
+ ticker.Stop()
+ return nil
+ case <-ticker.C:
+ for serviceName, config := range w.watcherConfigs {
+ go func(sn string, c WatcherConfig) {
+ fileList, _ := w.retrieveFileList(sn, c)
+ w.pollEvents(c.serviceName, fileList)
+ }(serviceName, config)
}
}
}
+
}
func (w *Watcher) retrieveFileList(serviceName string, config WatcherConfig) (map[string]os.FileInfo, error) {
@@ -392,43 +347,8 @@ func (w *Watcher) retrieveFileList(serviceName string, config WatcherConfig) (ma
}
return fileList, nil
-
- // Add the file's to the file list.
-
- //return nil
}
-// RemoveRecursive removes either a single file or a directory recursively from
-// the file's list.
-//func (w *Watcher) RemoveRecursive(name string) (err error) {
-// w.mu.Lock()
-// defer w.mu.Unlock()
-//
-// name, err = filepath.Abs(name)
-// if err != nil {
-// return err
-// }
-//
-// // If name is a single file, remove it and return.
-// info, found := w.files[name]
-// if !found {
-// return nil // Doesn't exist, just return.
-// }
-// if !info.IsDir() {
-// delete(w.files, name)
-// return nil
-// }
-//
-// // If it's a directory, delete all of it's contents recursively
-// // from w.files.
-// for path := range w.files {
-// if strings.HasPrefix(path, name) {
-// delete(w.files, path)
-// }
-// }
-// return nil
-//}
-
func (w *Watcher) retrieveFilesRecursive(serviceName, root string) (map[string]os.FileInfo, error) {
fileList := make(map[string]os.FileInfo)
@@ -437,14 +357,11 @@ func (w *Watcher) retrieveFilesRecursive(serviceName, root string) (map[string]o
return err
}
- // filename, pattern TODO
- //err = w.watcherConfigs[serviceName].filterHooks(info.Name(), path)
- //if err == ErrorSkip {
- // return nil
- //}
- //if err != nil {
- // return err
- //}
+ // if filename does not contain pattern --> ignore that file
+ err = w.watcherConfigs[serviceName].filterHooks(info.Name(), w.watcherConfigs[serviceName].filePatterns)
+ if err == ErrorSkip {
+ return nil
+ }
// If path is ignored and it's a directory, skip the directory. If it's
// ignored and it's a single file, skip the file.
@@ -462,7 +379,7 @@ func (w *Watcher) retrieveFilesRecursive(serviceName, root string) (map[string]o
})
}
-func (w *Watcher) pollEvents(serviceName string, files map[string]os.FileInfo, cancel chan struct{}) {
+func (w *Watcher) pollEvents(serviceName string, files map[string]os.FileInfo) {
w.mu.Lock()
defer w.mu.Unlock()
@@ -491,62 +408,66 @@ func (w *Watcher) pollEvents(serviceName string, files map[string]os.FileInfo, c
if oldInfo.ModTime() != info.ModTime() {
w.watcherConfigs[serviceName].files[pth] = info
select {
- case <-cancel:
- return
- case w.Event <- Event{Write, pth, pth, info, serviceName}:
+ case w.Event <- Event{
+ path: pth,
+ info: info,
+ service: serviceName,
+ }:
}
}
if oldInfo.Mode() != info.Mode() {
w.watcherConfigs[serviceName].files[pth] = info
select {
- case <-cancel:
- return
- case w.Event <- Event{Chmod, pth, pth, info, serviceName}:
+ case w.Event <- Event{
+ path: pth,
+ info: info,
+ service: serviceName,
+ }:
}
}
}
- // Check for renames and moves.
- //for path1, info1 := range removes {
- // for path2, info2 := range creates {
- // if sameFile(info1, info2) {
- // e := Event{
- // Op: Move,
- // Path: path2,
- // OldPath: path1,
- // FileInfo: info1,
- // }
- // // If they are from the same directory, it's a rename
- // // instead of a move event.
- // if filepath.Dir(path1) == filepath.Dir(path2) {
- // e.Op = Rename
- // }
- //
- // delete(removes, path1)
- // delete(creates, path2)
- //
- // select {
- // case <-cancel:
- // return
- // case w.Event <- e:
- // }
- // }
- // }
- //}
- //
- ////Send all the remaining create and remove events.
- //for pth, info := range creates {
- // select {
- // case <-cancel:
- // return
- // case w.Event <- Event{Create, pth, pth, info, serviceName}:
- // }
- //}
- //for pth, info := range removes {
- // select {
- // case <-cancel:
- // return
- // case w.Event <- Event{Remove, pth, pth, info, serviceName}:
- // }
- //}
+ //Check for renames and moves.
+ for path1, info1 := range removes {
+ for path2, info2 := range creates {
+ if sameFile(info1, info2) {
+ e := Event{
+ path: path2,
+ info: info2,
+ service: serviceName,
+ }
+
+ delete(removes, path1)
+ delete(creates, path2)
+
+ select {
+ case w.Event <- e:
+ }
+ }
+ }
+ }
+
+ //Send all the remaining create and remove events.
+ for pth, info := range creates {
+ select {
+ case w.Event <- Event{
+ path: pth,
+ info: info,
+ service: serviceName,
+ }:
+ }
+ }
+ for pth, info := range removes {
+ select {
+ case w.Event <- Event{
+ path: pth,
+ info: info,
+ service: serviceName,
+ }:
+ }
+ }
+}
+
+func (w *Watcher) Stop() {
+ w.close <- struct{}{}
}