github.com/crspeller/mattermost-server@v0.0.0-20190328001957-a200beb3d111/config/watcher.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package config
     5  
     6  import (
     7  	"path/filepath"
     8  
     9  	"github.com/fsnotify/fsnotify"
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/crspeller/mattermost-server/mlog"
    13  )
    14  
    15  // watcher monitors a file for changes
    16  type watcher struct {
    17  	emitter
    18  
    19  	fsWatcher *fsnotify.Watcher
    20  	close     chan struct{}
    21  	closed    chan struct{}
    22  }
    23  
    24  // newWatcher creates a new instance of watcher to monitor for file changes.
    25  func newWatcher(path string, callback func()) (w *watcher, err error) {
    26  	fsWatcher, err := fsnotify.NewWatcher()
    27  	if err != nil {
    28  		return nil, errors.Wrapf(err, "failed to create fsnotify watcher for %s", path)
    29  	}
    30  
    31  	path = filepath.Clean(path)
    32  
    33  	// Watch the entire containing directory.
    34  	configDir, _ := filepath.Split(path)
    35  	if err := fsWatcher.Add(configDir); err != nil {
    36  		if closeErr := fsWatcher.Close(); closeErr != nil {
    37  			mlog.Error("failed to stop fsnotify watcher for %s", mlog.String("path", path), mlog.Err(closeErr))
    38  		}
    39  		return nil, errors.Wrapf(err, "failed to watch directory %s", configDir)
    40  	}
    41  
    42  	w = &watcher{
    43  		fsWatcher: fsWatcher,
    44  		close:     make(chan struct{}),
    45  		closed:    make(chan struct{}),
    46  	}
    47  
    48  	go func() {
    49  		defer close(w.closed)
    50  		defer func() {
    51  			if err := fsWatcher.Close(); err != nil {
    52  				mlog.Error("failed to stop fsnotify watcher for %s", mlog.String("path", path))
    53  			}
    54  		}()
    55  
    56  		for {
    57  			select {
    58  			case event := <-fsWatcher.Events:
    59  				// We only care about the given file.
    60  				if filepath.Clean(event.Name) == path {
    61  					if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
    62  						mlog.Info("Config file watcher detected a change", mlog.String("path", path))
    63  						go callback()
    64  					}
    65  				}
    66  			case err := <-fsWatcher.Errors:
    67  				mlog.Error("Failed while watching config file", mlog.String("path", path), mlog.Err(err))
    68  			case <-w.close:
    69  				return
    70  			}
    71  		}
    72  	}()
    73  
    74  	return w, nil
    75  }
    76  
    77  func (w *watcher) Close() error {
    78  	close(w.close)
    79  	<-w.closed
    80  
    81  	return nil
    82  }