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