Skip to content
This repository has been archived by the owner on Jan 24, 2019. It is now read-only.

Reload authenticated-emails-file upon update #89

Merged
merged 1 commit into from
May 12, 2015

Conversation

mbland
Copy link
Contributor

@mbland mbland commented May 10, 2015

Closes #41.

The tricky part here is that sometimes popping the file open in an editor and writing to it results not in a direct write, but in some kind of remove and/or rename operation. (At least, that's how Vim appears to roll.) I've confirmed that this works on our running instance, regardless of whether the file is updated in a text editor or, in our case, regenerated via Jekyll (which, oddly, results in a write event, not a remove/rename). Will test out on various Linux flavors and Windows 8.1 a little later today.

cc: @jehiah @razb

@mbland
Copy link
Contributor Author

mbland commented May 10, 2015

Also:

  • go-fsnotify/fsnotify#17: The exact issue I described above regarding editing the file with Vim. Seems my time.Sleep() solution was in line with conventional wisdom, much as I hate to sleep (which is why the action() callback argument exists, passed in via newValidatorImpl(), so I could use the updated channel in validator_watcher_test.go to synchronize).
  • go-fsnotify/fsnotify#49: I tried watching the directory, i.e. watcher.Add(filepath.Dir(filename)), but my tests aborted with too many open files.

Wish I'd read these issues before, but nice to see my findings corroborated. :-)

@mbland
Copy link
Contributor Author

mbland commented May 11, 2015

So I found a couple small tweaks needed to be made for Windows 8.1 and pushed a new commit. Also, tests would fail occasionally on Arch Linux; it seems that a Remove event is preceded by a Chmod on that system. Finally, the tests seemed to deadlock on the Ubuntu 12.04 machines running Travis before the final commit. (The tests take on the order of several seconds to run on Travis now, apparently since there's all this file system activity happening that wasn't before this change.)

After these small tweaks, everything works out on Windows 8.1, Ubuntu 12.04, Ubuntu 14.04, FreeBSD 10.1, FreeBSD 9.3, Debian Squeeze (though I had to change the fsnotify import to use github.com instead of gopkg.in), and Arch Linux.

@mbland
Copy link
Contributor Author

mbland commented May 11, 2015

Oh, and OS X 10.10.3, since that's what I normally develop on.

@mbland
Copy link
Contributor Author

mbland commented May 11, 2015

Just added WaitForReplacement() so watching isn't forever aborted when the authenticated emails file disappears for more than 150ms. It'll call os.Stat() up to twenty times a second if it's persistently missing, but that's the pathological case, not the common one.

What's better is that the os.Stat() and watcher.Add() calls usually succeed right away, and the wait to retry is much smaller if they don't, bringing test times back down to where everybody's happy.

@jehiah
Copy link
Member

jehiah commented May 12, 2015

I haven't looked through this closely yet, but at a quick glance this looks great! ⭐

Can you add the new dependency on gopkg.in/fsnotify.v1 to the Godeps file?

@mbland
Copy link
Contributor Author

mbland commented May 12, 2015

Done!

BTW, I'm now so intrigued by fsnotify that I forked the repo and am considering coming up with a generic file watcher utility based on watcher.go from this PR. We'll see. :-)

@jehiah
Copy link
Member

jehiah commented May 12, 2015

👍 squash down these commits?

mbland pushed a commit to cloud-gov/oauth2_proxy that referenced this pull request May 12, 2015
This change extracts the UserMap class from NewValidator() so that its
LoadAuthenticatedEmailsFile() method can be called concurrently. This method
is called by a goroutine containing a fsnotify.Watcher watching the
authenticated emails file.

Watching isn't forever aborted when the authenticated emails file disappears.
The goroutine will call os.Stat() up to twenty times a second if the file is
persistently missing, but that's the pathological case, not the common one.

The common case is that some editors (including Vim) will perform a
rename-and-replace when updating a file, triggering fsnotify.Rename events,
and the file will temporarily disappear. This watcher goroutine handles that
case.

Also, on some platforms (notably Arch Linux), a remove will be preceded by a
fsnotify.Chmod, causing a race between the upcoming fsnotify.Remove and the
call to UserMap.LoadAuthenticatedEmailsFile(). Hence, we treat fsnotify.Chmod
the same as fsnotify.Remove and fsnotify.Rename. There's no significant
penalty to re-adding a file to the watcher.

Also contains the following small changes from the summary of commits below:

- Minor optimization of email domain search
- Fixed api_test.go on Windows
- Add deferred File.Close() calls where needed
- Log error and return if emails file doesn't parse

These are the original commits from bitly#89 squashed into this one:

0c6f2b6 Refactor validator_test to prepare for more tests
e0c792b Add more test cases to validator_test
a9a9d93 Minor optimization of email domain search
b763ea5 Extract LoadAuthenticatedEmailsFile()
8cdaf7f Introduce synchronized UserMap type
1b84eef Add UserMap methods, locking
af15dcf Reload authenticated-emails-file upon update
6d95548 Make UserMap operations lock-free
        Per:
        - http://stackoverflow.com/questions/21447463/is-assigning-a-pointer-atomic-in-golang
        - https://groups.google.com/forum/#!msg/golang-nuts/ueSvaEKgyLY/ZW_74IC4PekJ
75755d5 Fix tests on Windows
d0eab2e Ignore email file watcher Chmod events
0b9798b Fix watcher on Ubuntu 12.04
3a8251a WaitForReplacement() to retry emails file watch
a57fd29 Add deferred File.Close() calls where needed
        Because correctness: Don't leak file handles anywhere, and prepare for
        future panics and early returns.
52ed3fd Log error and return if emails file doesn't parse
40100d4 Add gopkg.in/fsnotify.v1 dependency to Godeps file
@mbland
Copy link
Contributor Author

mbland commented May 12, 2015

Done!

@mbland
Copy link
Contributor Author

mbland commented May 12, 2015

Hmm, seems there's still a slight bit of a race in the fsnotify.Chmod case. Lemme see...

This change extracts the UserMap class from NewValidator() so that its
LoadAuthenticatedEmailsFile() method can be called concurrently. This method
is called by a goroutine containing a fsnotify.Watcher watching the
authenticated emails file.

Watching isn't forever aborted when the authenticated emails file disappears.
The goroutine will call os.Stat() up to twenty times a second if the file is
persistently missing, but that's the pathological case, not the common one.

The common case is that some editors (including Vim) will perform a
rename-and-replace when updating a file, triggering fsnotify.Rename events,
and the file will temporarily disappear. This watcher goroutine handles that
case.

Also, on some platforms (notably Arch Linux), a remove will be preceded by a
fsnotify.Chmod, causing a race between the upcoming fsnotify.Remove and the
call to UserMap.LoadAuthenticatedEmailsFile(). Hence, we treat fsnotify.Chmod
the same as fsnotify.Remove and fsnotify.Rename. There's no significant
penalty to re-adding a file to the watcher.

Also contains the following small changes from the summary of commits below:

- Minor optimization of email domain search
- Fixed api_test.go on Windows
- Add deferred File.Close() calls where needed
- Log error and return if emails file doesn't parse

These are the original commits from bitly#89 squashed into this one:

0c6f2b6 Refactor validator_test to prepare for more tests
e0c792b Add more test cases to validator_test
a9a9d93 Minor optimization of email domain search
b763ea5 Extract LoadAuthenticatedEmailsFile()
8cdaf7f Introduce synchronized UserMap type
1b84eef Add UserMap methods, locking
af15dcf Reload authenticated-emails-file upon update
6d95548 Make UserMap operations lock-free
        Per:
        - http://stackoverflow.com/questions/21447463/is-assigning-a-pointer-atomic-in-golang
        - https://groups.google.com/forum/#!msg/golang-nuts/ueSvaEKgyLY/ZW_74IC4PekJ
75755d5 Fix tests on Windows
d0eab2e Ignore email file watcher Chmod events
0b9798b Fix watcher on Ubuntu 12.04
3a8251a WaitForReplacement() to retry emails file watch
a57fd29 Add deferred File.Close() calls where needed
        Because correctness: Don't leak file handles anywhere, and prepare for
        future panics and early returns.
52ed3fd Log error and return if emails file doesn't parse
40100d4 Add gopkg.in/fsnotify.v1 dependency to Godeps file
17dfbbc Avoid a race when Remove is preceded by Chmod
@mbland
Copy link
Contributor Author

mbland commented May 12, 2015

Looks like that did the trick! :-)

jehiah added a commit that referenced this pull request May 12, 2015
Reload authenticated-emails-file upon update
@jehiah jehiah merged commit 254b26d into bitly:master May 12, 2015
@mbland mbland deleted the watch-email-file branch May 12, 2015 15:38
mbland pushed a commit to 18F/hub that referenced this pull request May 12, 2015
Now that we're running an instance with bitly/oauth2_proxy#89
integrated, this is no longer necessary.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Allow for dynamic updates of email authorization file
2 participants