Skip to content

Commit

Permalink
Merge pull request #6172 from timmysilv/master
Browse files Browse the repository at this point in the history
Update OSFileWatcher to support symlinks
  • Loading branch information
k8s-ci-robot authored Sep 18, 2020
2 parents 87aa96b + d280a34 commit a6994be
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 4 deletions.
26 changes: 22 additions & 4 deletions internal/watch/file_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package watch

import (
"log"
"os"
"path"
"path/filepath"
"strings"

"github.com/fsnotify/fsnotify"
Expand Down Expand Up @@ -60,15 +62,31 @@ func (f *OSFileWatcher) watch() error {
}
f.watcher = watcher

realFile, err := filepath.EvalSymlinks(f.file)
if err != nil {
return err
}

dir, file := path.Split(f.file)
go func(file string) {
for {
select {
case event := <-watcher.Events:
if (event.Op&fsnotify.Write == fsnotify.Write ||
event.Op&fsnotify.Create == fsnotify.Create) &&
strings.HasSuffix(event.Name, file) {
f.onEvent()
if event.Op&fsnotify.Create == fsnotify.Create ||
event.Op&fsnotify.Write == fsnotify.Write {
if finfo, err := os.Lstat(event.Name); err != nil {
log.Printf("can not lstat file: %v\n", err)
} else if finfo.Mode()&os.ModeSymlink != 0 {
if currentRealFile, err := filepath.EvalSymlinks(f.file); err == nil &&
currentRealFile != realFile {
f.onEvent()
realFile = currentRealFile
}
continue
}
if strings.HasSuffix(event.Name, file) {
f.onEvent()
}
}
case err := <-watcher.Errors:
if err != nil {
Expand Down
72 changes: 72 additions & 0 deletions internal/watch/file_watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package watch
import (
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"time"

Expand Down Expand Up @@ -67,3 +69,73 @@ func TestFileWatcher(t *testing.T) {
t.Fatalf("expected an event shortly after writing a file")
}
}

func TestFileWatcherWithNestedSymlink(t *testing.T) {
target1, err := ioutil.TempFile("", "t1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer target1.Close()
defer os.Remove(target1.Name())
dir := path.Dir(target1.Name())

innerLink := path.Join(dir, "innerLink")
if err = os.Symlink(target1.Name(), innerLink); err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer os.Remove(innerLink)
mainLink := path.Join(dir, "mainLink")
if err = os.Symlink(innerLink, mainLink); err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer os.Remove(mainLink)

targetName, err := filepath.EvalSymlinks(mainLink)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if targetName != target1.Name() {
t.Fatalf("expected symlink to point to %v, not %v", target1.Name(), targetName)
}

count := 0
events := make(chan bool, 10)
fw, err := NewFileWatcher(mainLink, func() {
count++
if count != 1 {
t.Fatalf("expected 1 but returned %v", count)
}
if targetName, err = filepath.EvalSymlinks(mainLink); err != nil {
t.Fatalf("unexpected error: %v", err)
}
events <- true
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer fw.Close()

target2, err := ioutil.TempFile("", "t2")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer target2.Close()
defer os.Remove(target2.Name())

if err = os.Remove(innerLink); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = os.Symlink(target2.Name(), innerLink); err != nil {
t.Fatalf("unexpected error: %v", err)
}

timeoutChan := prepareTimeout()
select {
case <-events:
case <-timeoutChan:
t.Fatalf("expected an event shortly after creating a file and relinking")
}
if targetName != target2.Name() {
t.Fatalf("expected symlink to point to %v, not %v", target2.Name(), targetName)
}
}

0 comments on commit a6994be

Please sign in to comment.