6a0f119fc2
This test failure from #92 inspired this change: https://travis-ci.org/bitly/google_auth_proxy/jobs/62425336 2015/05/13 16:27:33 using authenticated emails file /tmp/test_auth_emails_952353477 2015/05/13 16:27:33 watching /tmp/test_auth_emails_952353477 for updates 2015/05/13 16:27:33 validating: is xyzzy@example.com valid? true 2015/05/13 16:27:33 watching interrupted on event: "/tmp/test_auth_emails_952353477": CHMOD 2015/05/13 16:27:33 watching resumed for /tmp/test_auth_emails_952353477 2015/05/13 16:27:33 reloading after event: "/tmp/test_auth_emails_952353477": CHMOD 2015/05/13 16:27:33 watching interrupted on event: "/tmp/test_auth_emails_952353477": REMOVE 2015/05/13 16:27:33 validating: is xyzzy@example.com valid? false 2015/05/13 16:27:33 watching resumed for /tmp/test_auth_emails_952353477 2015/05/13 16:27:33 reloading after event: "/tmp/test_auth_emails_952353477": REMOVE 2015/05/13 16:27:33 failed opening authenticated-emails-file="/tmp/test_auth_emails_952353477", open /tmp/test_auth_emails_952353477: no such file or directory I believe that what happened was that the call to reload the file after the second "reloading after event" lost the race when the test shut down and the file was removed. This change introduces a `done` channel that ensures outstanding actions complete and the watcher exits before the test removes the file.
69 lines
1.7 KiB
Go
69 lines
1.7 KiB
Go
// +build go1.3
|
|
// +build !plan9,!solaris
|
|
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"gopkg.in/fsnotify.v1"
|
|
)
|
|
|
|
func WaitForReplacement(event fsnotify.Event, watcher *fsnotify.Watcher) {
|
|
const sleep_interval = 50 * time.Millisecond
|
|
|
|
// Avoid a race when fsnofity.Remove is preceded by fsnotify.Chmod.
|
|
if event.Op&fsnotify.Chmod != 0 {
|
|
time.Sleep(sleep_interval)
|
|
}
|
|
for {
|
|
if _, err := os.Stat(event.Name); err == nil {
|
|
if err := watcher.Add(event.Name); err == nil {
|
|
log.Printf("watching resumed for %s", event.Name)
|
|
return
|
|
}
|
|
}
|
|
time.Sleep(sleep_interval)
|
|
}
|
|
}
|
|
|
|
func WatchForUpdates(filename string, done <-chan bool, action func()) bool {
|
|
filename = filepath.Clean(filename)
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
log.Fatal("failed to create watcher for ", filename, ": ", err)
|
|
}
|
|
go func() {
|
|
defer watcher.Close()
|
|
for {
|
|
select {
|
|
case _ = <-done:
|
|
log.Printf("Shutting down watcher for: %s",
|
|
filename)
|
|
return
|
|
case event := <-watcher.Events:
|
|
// On Arch Linux, it appears Chmod events precede Remove events,
|
|
// which causes a race between action() and the coming Remove event.
|
|
// If the Remove wins, the action() (which calls
|
|
// UserMap.LoadAuthenticatedEmailsFile()) crashes when the file
|
|
// can't be opened.
|
|
if event.Op&(fsnotify.Remove|fsnotify.Rename|fsnotify.Chmod) != 0 {
|
|
log.Printf("watching interrupted on event: %s", event)
|
|
WaitForReplacement(event, watcher)
|
|
}
|
|
log.Printf("reloading after event: %s", event)
|
|
action()
|
|
case err := <-watcher.Errors:
|
|
log.Printf("error watching %s: %s", filename, err)
|
|
}
|
|
}
|
|
}()
|
|
if err = watcher.Add(filename); err != nil {
|
|
log.Fatal("failed to add ", filename, " to watcher: ", err)
|
|
}
|
|
return true
|
|
}
|