mirror of
https://github.com/containers/gvisor-tap-vsock.git
synced 2025-10-04 08:26:40 +08:00
Remove filewatcher package
Since #339 was reverted, the filewatcher functionality is no longer used. Signed-off-by: Jan Dubois <jan.dubois@suse.com>
This commit is contained in:
@@ -1,63 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileWatcher is an utility that
|
|
||||||
type FileWatcher struct {
|
|
||||||
path string
|
|
||||||
|
|
||||||
closeCh chan struct{}
|
|
||||||
pollInterval time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFileWatcher(path string) *FileWatcher {
|
|
||||||
return &FileWatcher{
|
|
||||||
path: path,
|
|
||||||
pollInterval: 5 * time.Second, // 5s is the default inode cache timeout in linux for most systems.
|
|
||||||
closeCh: make(chan struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fw *FileWatcher) Start(changeHandler func()) {
|
|
||||||
prevModTime := fw.fileModTime(fw.path)
|
|
||||||
|
|
||||||
// use polling-based approach to detect file changes
|
|
||||||
// we can't use fsnotify/fsnotify due to issues with symlink+socket. see #462.
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case _, ok := <-fw.closeCh:
|
|
||||||
if !ok {
|
|
||||||
return // watcher is closed.
|
|
||||||
}
|
|
||||||
case <-time.After(fw.pollInterval):
|
|
||||||
}
|
|
||||||
|
|
||||||
modTime := fw.fileModTime(fw.path)
|
|
||||||
if modTime.IsZero() {
|
|
||||||
continue // file does not exists
|
|
||||||
}
|
|
||||||
|
|
||||||
if !prevModTime.Equal(modTime) {
|
|
||||||
changeHandler()
|
|
||||||
prevModTime = modTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fw *FileWatcher) fileModTime(path string) time.Time {
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return info.ModTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fw *FileWatcher) Stop() {
|
|
||||||
close(fw.closeCh)
|
|
||||||
}
|
|
@@ -1,101 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync/atomic"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFileWatcher(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
// we can't really speed up the test as we need to wait for the
|
|
||||||
// inode cache to expire so that we can read the latest
|
|
||||||
// file's modtime.
|
|
||||||
t.Skip("skipping test in short mode.")
|
|
||||||
}
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
assertSpec := func(t *testing.T, watchedPath, filePath string) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
fw := NewFileWatcher(watchedPath)
|
|
||||||
|
|
||||||
var numTriggered atomic.Int64
|
|
||||||
assertNumTriggered := func(expected int) {
|
|
||||||
time.Sleep(fw.pollInterval + 200*time.Millisecond)
|
|
||||||
assert.Equal(t, int64(expected), numTriggered.Load())
|
|
||||||
}
|
|
||||||
|
|
||||||
fw.Start(func() {
|
|
||||||
numTriggered.Add(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
// important: adding a random sleep here so that the next fw's poll
|
|
||||||
// results in inode cache miss, hence calling stat will return the latest
|
|
||||||
// modtime.
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
|
|
||||||
// CASE: can detect changes to the file.
|
|
||||||
if err := os.WriteFile(filePath, []byte("2"), 0o600); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
assertNumTriggered(1)
|
|
||||||
|
|
||||||
// CASE: can detect "swap"-based file modification.
|
|
||||||
tmpFile := filepath.Join(filepath.Dir(filePath), "tmp.txt")
|
|
||||||
if err := os.WriteFile(tmpFile, []byte("lol"), 0o600); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := os.Rename(tmpFile, filePath); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
assertNumTriggered(2)
|
|
||||||
|
|
||||||
// CASE: combine multiple partial writes into single event.
|
|
||||||
fd, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0o600)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// we assume these writes happens in less than 50ms.
|
|
||||||
_, _ = fd.Write([]byte("a"))
|
|
||||||
_ = fd.Sync()
|
|
||||||
_, _ = fd.Write([]byte("b"))
|
|
||||||
fd.Close()
|
|
||||||
assertNumTriggered(3)
|
|
||||||
|
|
||||||
// CASE: closed file watcher should not call the callback after Stop() is called.
|
|
||||||
fw.Stop()
|
|
||||||
if err := os.WriteFile(filePath, []byte("2"), 0o600); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
assertNumTriggered(3) // does not change.
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("normal file", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
path := filepath.Join(dir, "file.txt")
|
|
||||||
_ = os.WriteFile(path, []byte("1"), 0o600)
|
|
||||||
|
|
||||||
assertSpec(t, path, path)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("symlink", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
path := filepath.Join(dir, "file.txt")
|
|
||||||
_ = os.WriteFile(path, []byte("1"), 0o600)
|
|
||||||
|
|
||||||
symlinkPath := filepath.Join(dir, "symlink.txt")
|
|
||||||
_ = os.Symlink(path, symlinkPath)
|
|
||||||
|
|
||||||
assertSpec(t, symlinkPath, path)
|
|
||||||
})
|
|
||||||
}
|
|
Reference in New Issue
Block a user