net/dns: fix TestDNSTrampleRecovery failure under flakestress
The test had two problems: 1. runFileWatcher passed hardcoded "/etc/" to the inotify watcher, but the test filesystem uses a temp directory prefix. The watcher was watching the real /etc/, never seeing the test's file writes. 2. The test's watchFile used gonotify.NewDirWatcher which creates goroutines that block on real inotify syscalls. These don't work inside synctest's fake-time bubble. The test only passed standalone by accident: gonotify walks /etc/ on startup producing fake events that happened to trigger trample detection at the right time. Fix the path issue by adding ActualPath to the wholeFileFS interface, which translates logical paths (like "/etc/resolv.conf") to real filesystem paths (respecting any test prefix). Use it in runFileWatcher so the inotify watch targets the correct directory. Replace gonotify in the test with a one-shot timer that synctest can advance through fake time, reliably triggering the trample check. Fixes #19400 Change-Id: Idb252881ec24d0ab3b3c1d154dbdaf532db837d4 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
27f1d4c15d
commit
49eb1b5d26
+18
-1
@@ -442,7 +442,9 @@ func (m *directManager) runFileWatcher() {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := watchFile(m.ctx, "/etc/", resolvConf, m.checkForFileTrample); err != nil {
|
||||
dir := m.fs.ActualPath(filepath.Dir(resolvConf))
|
||||
file := m.fs.ActualPath(resolvConf)
|
||||
if err := watchFile(m.ctx, dir, file, m.checkForFileTrample); err != nil {
|
||||
// This is all best effort for now, so surface warnings to users.
|
||||
m.logf("dns: inotify: %s", err)
|
||||
}
|
||||
@@ -597,6 +599,19 @@ type wholeFileFS interface {
|
||||
ReadFile(name string) ([]byte, error)
|
||||
Remove(name string) error
|
||||
Rename(oldName, newName string) error
|
||||
// ActualPath returns the real filesystem path for the given absolute
|
||||
// logical path. All other methods in this interface accept logical
|
||||
// paths (like "/etc/resolv.conf") and translate them internally;
|
||||
// ActualPath exposes that same translation for callers that need
|
||||
// the real path for use outside the interface (e.g. setting up an
|
||||
// inotify watch on the correct directory).
|
||||
//
|
||||
// For directFS with an empty prefix (production), the input is
|
||||
// returned unchanged ("/etc" → "/etc"). For directFS with a test
|
||||
// prefix like "/tmp/test123", the prefix is joined
|
||||
// ("/etc" → "/tmp/test123/etc"). For wslFS the input is returned
|
||||
// unchanged, since paths are passed through to wsl.exe as-is.
|
||||
ActualPath(name string) string
|
||||
Stat(name string) (isRegular bool, err error)
|
||||
Truncate(name string) error
|
||||
WriteFile(name string, contents []byte, perm os.FileMode) error
|
||||
@@ -613,6 +628,8 @@ type directFS struct {
|
||||
|
||||
func (fs directFS) path(name string) string { return filepath.Join(fs.prefix, name) }
|
||||
|
||||
func (fs directFS) ActualPath(name string) string { return fs.path(name) }
|
||||
|
||||
func (fs directFS) Stat(name string) (isRegular bool, err error) {
|
||||
fi, err := os.Stat(fs.path(name))
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user