...

Source file src/github.com/redhat-developer/odo/pkg/watch/file_watcher.go

Documentation: github.com/redhat-developer/odo/pkg/watch

     1  package watch
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	dfutil "github.com/devfile/library/v2/pkg/util"
     9  	"github.com/fsnotify/fsnotify"
    10  	gitignore "github.com/sabhiram/go-gitignore"
    11  	"k8s.io/klog"
    12  
    13  	"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
    14  	"github.com/redhat-developer/odo/pkg/util"
    15  )
    16  
    17  func getFullSourcesWatcher(path string, fileIgnores []string) (*fsnotify.Watcher, error) {
    18  	absIgnorePaths := dfutil.GetAbsGlobExps(path, fileIgnores)
    19  
    20  	watcher, err := fsnotify.NewWatcher()
    21  	if err != nil {
    22  		return nil, fmt.Errorf("error setting up filesystem watcher: %v", err)
    23  	}
    24  
    25  	// adding watch on the root folder and the sub folders recursively
    26  	// so directory and the path in addRecursiveWatch() are the same
    27  	err = addRecursiveWatch(watcher, path, path, absIgnorePaths)
    28  	if err != nil {
    29  		return nil, fmt.Errorf("error watching source path %s: %v", path, err)
    30  	}
    31  	return watcher, nil
    32  }
    33  
    34  // addRecursiveWatch handles adding watches recursively for the path provided
    35  // and its subdirectories.  If a non-directory is specified, this call is a no-op.
    36  // Files matching glob pattern defined in ignores will be ignored.
    37  // Taken from https://github.com/openshift/origin/blob/85eb37b34f0657631592356d020cef5a58470f8e/pkg/util/fsnotification/fsnotification.go
    38  // rootPath is the root path of the file or directory,
    39  // path is the recursive path of the file or the directory,
    40  // ignores contains the glob rules for matching
    41  func addRecursiveWatch(watcher *fsnotify.Watcher, rootPath string, path string, ignores []string) error {
    42  
    43  	fsys := filesystem.DefaultFs{}
    44  
    45  	file, err := os.Stat(path)
    46  	if err != nil {
    47  		if os.IsNotExist(err) {
    48  			return nil
    49  		}
    50  		return fmt.Errorf("error introspecting path %s: %v", path, err)
    51  	}
    52  
    53  	ignoreMatcher := gitignore.CompileIgnoreLines(ignores...)
    54  
    55  	mode := file.Mode()
    56  	if mode.IsRegular() {
    57  		var rel string
    58  		rel, err = filepath.Rel(rootPath, path)
    59  		if err != nil {
    60  			return err
    61  		}
    62  		matched := ignoreMatcher.MatchesPath(rel)
    63  		if !matched {
    64  			klog.V(4).Infof("adding watch on path %s", path)
    65  
    66  			// checking if the file exits before adding the watcher to it
    67  			if !util.CheckPathExists(fsys, path) {
    68  				return nil
    69  			}
    70  
    71  			err = watcher.Add(path)
    72  			if err != nil {
    73  				klog.V(4).Infof("error adding watcher for path %s: %v", path, err)
    74  			}
    75  			return nil
    76  		}
    77  	}
    78  
    79  	folders := []string{}
    80  	err = filepath.Walk(path, func(newPath string, info os.FileInfo, err error) error {
    81  		if err != nil {
    82  			// Ignore the error if it's a 'path does not exist' error, no need to walk a non-existent path
    83  			if !util.CheckPathExists(fsys, newPath) {
    84  				klog.V(4).Infof("Walk func received an error for path %s, but the path doesn't exist so this is likely not an error. err: %v", path, err)
    85  				return nil
    86  			}
    87  			return fmt.Errorf("unable to walk path: %s: %w", newPath, err)
    88  		}
    89  
    90  		if info.IsDir() {
    91  			// If the current directory matches any of the ignore patterns, ignore them so that their contents are also not ignored
    92  			rel, err := filepath.Rel(rootPath, newPath)
    93  			if err != nil {
    94  				return err
    95  			}
    96  			matched := ignoreMatcher.MatchesPath(rel)
    97  			if err != nil {
    98  				return fmt.Errorf("unable to addRecursiveWatch on %s: %w", newPath, err)
    99  			}
   100  			if matched {
   101  				klog.V(4).Infof("ignoring watch on path %s", newPath)
   102  				return filepath.SkipDir
   103  			}
   104  			// Append the folder we just walked on
   105  			folders = append(folders, newPath)
   106  		}
   107  		return nil
   108  	})
   109  	if err != nil {
   110  		return err
   111  	}
   112  	for _, folder := range folders {
   113  
   114  		rel, err := filepath.Rel(rootPath, folder)
   115  		if err != nil {
   116  			return err
   117  		}
   118  		matched := ignoreMatcher.MatchesPath(rel)
   119  
   120  		if matched {
   121  			klog.V(4).Infof("ignoring watch for %s", folder)
   122  			continue
   123  		}
   124  
   125  		// checking if the file exits before adding the watcher to it
   126  		if !util.CheckPathExists(fsys, path) {
   127  			continue
   128  		}
   129  
   130  		klog.V(4).Infof("adding watch on path %s", folder)
   131  		err = watcher.Add(folder)
   132  		if err != nil {
   133  			// Linux "no space left on device" issues are usually resolved via
   134  			// $ sudo sysctl fs.inotify.max_user_watches=65536
   135  			// BSD / OSX: "too many open files" issues are ussualy resolved via
   136  			// $ sysctl variables "kern.maxfiles" and "kern.maxfilesperproc",
   137  			klog.V(4).Infof("error adding watcher for path %s: %v", folder, err)
   138  		}
   139  	}
   140  	return nil
   141  }
   142  

View as plain text