git.iko.soy/typeable/oauth2-proxy
on master help sign in
Provide graceful shutdown of file watcher in tests
6a0f119Mike Bland
validator.go+5−5
@@ -15,13 +15,13 @@ type UserMap struct {
15 m unsafe.Pointer
15 m unsafe.Pointer
16}
16}
17
17
18−func NewUserMap(usersFile string, onUpdate func()) *UserMap {
18+func NewUserMap(usersFile string, done <-chan bool, onUpdate func()) *UserMap {
19 um := &UserMap{usersFile: usersFile}
19 um := &UserMap{usersFile: usersFile}
20 m := make(map[string]bool)
20 m := make(map[string]bool)
21 atomic.StorePointer(&um.m, unsafe.Pointer(&m))
21 atomic.StorePointer(&um.m, unsafe.Pointer(&m))
22 if usersFile != "" {
22 if usersFile != "" {
23 log.Printf("using authenticated emails file %s", usersFile)
23 log.Printf("using authenticated emails file %s", usersFile)
24− started := WatchForUpdates(usersFile, func() {
24+ started := WatchForUpdates(usersFile, done, func() {
25 um.LoadAuthenticatedEmailsFile()
25 um.LoadAuthenticatedEmailsFile()
26 onUpdate()
26 onUpdate()
27 })
27 })
@@ -62,8 +62,8 @@ func (um *UserMap) LoadAuthenticatedEmailsFile() {
62}
62}
63
63
64func newValidatorImpl(domains []string, usersFile string,
64func newValidatorImpl(domains []string, usersFile string,
65− onUpdate func()) func(string) bool {
65+ done <-chan bool, onUpdate func()) func(string) bool {
66− validUsers := NewUserMap(usersFile, onUpdate)
66+ validUsers := NewUserMap(usersFile, done, onUpdate)
67
67
68 for i, domain := range domains {
68 for i, domain := range domains {
69 domains[i] = fmt.Sprintf("@%s", strings.ToLower(domain))
69 domains[i] = fmt.Sprintf("@%s", strings.ToLower(domain))
@@ -85,5 +85,5 @@ func newValidatorImpl(domains []string, usersFile string,
85}
85}
86
86
87func NewValidator(domains []string, usersFile string) func(string) bool {
87func NewValidator(domains []string, usersFile string) func(string) bool {
88− return newValidatorImpl(domains, usersFile, func() {})
88+ return newValidatorImpl(domains, usersFile, nil, func() {})
89}
89}
validator_test.go+14−5
@@ -9,6 +9,7 @@ import (
9
9
10type ValidatorTest struct {
10type ValidatorTest struct {
11 auth_email_file *os.File
11 auth_email_file *os.File
12+ done chan bool
12}
13}
13
14
14func NewValidatorTest(t *testing.T) *ValidatorTest {
15func NewValidatorTest(t *testing.T) *ValidatorTest {
@@ -18,13 +19,21 @@ func NewValidatorTest(t *testing.T) *ValidatorTest {
18 if err != nil {
19 if err != nil {
19 t.Fatal("failed to create temp file: " + err.Error())
20 t.Fatal("failed to create temp file: " + err.Error())
20 }
21 }
22+ vt.done = make(chan bool)
21 return vt
23 return vt
22}
24}
23
25
24func (vt *ValidatorTest) TearDown() {
26func (vt *ValidatorTest) TearDown() {
27+ vt.done <- true
25 os.Remove(vt.auth_email_file.Name())
28 os.Remove(vt.auth_email_file.Name())
26}
29}
27
30
31+func (vt *ValidatorTest) NewValidator(domains []string,
32+ updated chan<- bool) func(string) bool {
33+ return newValidatorImpl(domains, vt.auth_email_file.Name(),
34+ vt.done, func() { updated <- true })
35+}
36+
28// This will close vt.auth_email_file.
37// This will close vt.auth_email_file.
29func (vt *ValidatorTest) WriteEmails(t *testing.T, emails []string) {
38func (vt *ValidatorTest) WriteEmails(t *testing.T, emails []string) {
30 defer vt.auth_email_file.Close()
39 defer vt.auth_email_file.Close()
@@ -41,7 +50,7 @@ func TestValidatorEmpty(t *testing.T) {
41
50
42 vt.WriteEmails(t, []string(nil))
51 vt.WriteEmails(t, []string(nil))
43 domains := []string(nil)
52 domains := []string(nil)
44− validator := NewValidator(domains, vt.auth_email_file.Name())
53+ validator := vt.NewValidator(domains, nil)
45
54
46 if validator("foo.bar@example.com") {
55 if validator("foo.bar@example.com") {
47 t.Error("nothing should validate when the email and " +
56 t.Error("nothing should validate when the email and " +
@@ -55,7 +64,7 @@ func TestValidatorSingleEmail(t *testing.T) {
55
64
56 vt.WriteEmails(t, []string{"foo.bar@example.com"})
65 vt.WriteEmails(t, []string{"foo.bar@example.com"})
57 domains := []string(nil)
66 domains := []string(nil)
58− validator := NewValidator(domains, vt.auth_email_file.Name())
67+ validator := vt.NewValidator(domains, nil)
59
68
60 if !validator("foo.bar@example.com") {
69 if !validator("foo.bar@example.com") {
61 t.Error("email should validate")
70 t.Error("email should validate")
@@ -72,7 +81,7 @@ func TestValidatorSingleDomain(t *testing.T) {
72
81
73 vt.WriteEmails(t, []string(nil))
82 vt.WriteEmails(t, []string(nil))
74 domains := []string{"example.com"}
83 domains := []string{"example.com"}
75− validator := NewValidator(domains, vt.auth_email_file.Name())
84+ validator := vt.NewValidator(domains, nil)
76
85
77 if !validator("foo.bar@example.com") {
86 if !validator("foo.bar@example.com") {
78 t.Error("email should validate")
87 t.Error("email should validate")
@@ -91,7 +100,7 @@ func TestValidatorMultipleEmailsMultipleDomains(t *testing.T) {
91 "plugh@example.com",
100 "plugh@example.com",
92 })
101 })
93 domains := []string{"example0.com", "example1.com"}
102 domains := []string{"example0.com", "example1.com"}
94− validator := NewValidator(domains, vt.auth_email_file.Name())
103+ validator := vt.NewValidator(domains, nil)
95
104
96 if !validator("foo.bar@example0.com") {
105 if !validator("foo.bar@example0.com") {
97 t.Error("email from first domain should validate")
106 t.Error("email from first domain should validate")
@@ -117,7 +126,7 @@ func TestValidatorComparisonsAreCaseInsensitive(t *testing.T) {
117
126
118 vt.WriteEmails(t, []string{"Foo.Bar@Example.Com"})
127 vt.WriteEmails(t, []string{"Foo.Bar@Example.Com"})
119 domains := []string{"Frobozz.Com"}
128 domains := []string{"Frobozz.Com"}
120− validator := NewValidator(domains, vt.auth_email_file.Name())
129+ validator := vt.NewValidator(domains, nil)
121
130
122 if !validator("foo.bar@example.com") {
131 if !validator("foo.bar@example.com") {
123 t.Error("loaded email addresses are not lower-cased")
132 t.Error("loaded email addresses are not lower-cased")
validator_watcher_copy_test.go+1−2
@@ -34,8 +34,7 @@ func TestValidatorOverwriteEmailListViaCopyingOver(t *testing.T) {
34 vt.WriteEmails(t, []string{"xyzzy@example.com"})
34 vt.WriteEmails(t, []string{"xyzzy@example.com"})
35 domains := []string(nil)
35 domains := []string(nil)
36 updated := make(chan bool)
36 updated := make(chan bool)
37− validator := newValidatorImpl(domains, vt.auth_email_file.Name(),
37+ validator := vt.NewValidator(domains, updated)
38− func() { updated <- true })
39
38
40 if !validator("xyzzy@example.com") {
39 if !validator("xyzzy@example.com") {
41 t.Error("email in list should validate")
40 t.Error("email in list should validate")
validator_watcher_test.go+2−4
@@ -51,8 +51,7 @@ func TestValidatorOverwriteEmailListDirectly(t *testing.T) {
51 })
51 })
52 domains := []string(nil)
52 domains := []string(nil)
53 updated := make(chan bool)
53 updated := make(chan bool)
54− validator := newValidatorImpl(domains, vt.auth_email_file.Name(),
54+ validator := vt.NewValidator(domains, updated)
55− func() { updated <- true })
56
55
57 if !validator("xyzzy@example.com") {
56 if !validator("xyzzy@example.com") {
58 t.Error("first email in list should validate")
57 t.Error("first email in list should validate")
@@ -89,8 +88,7 @@ func TestValidatorOverwriteEmailListViaRenameAndReplace(t *testing.T) {
89 vt.WriteEmails(t, []string{"xyzzy@example.com"})
88 vt.WriteEmails(t, []string{"xyzzy@example.com"})
90 domains := []string(nil)
89 domains := []string(nil)
91 updated := make(chan bool)
90 updated := make(chan bool)
92− validator := newValidatorImpl(domains, vt.auth_email_file.Name(),
91+ validator := vt.NewValidator(domains, updated)
93− func() { updated <- true })
94
92
95 if !validator("xyzzy@example.com") {
93 if !validator("xyzzy@example.com") {
96 t.Error("email in list should validate")
94 t.Error("email in list should validate")
watcher.go+5−1
@@ -30,7 +30,7 @@ func WaitForReplacement(event fsnotify.Event, watcher *fsnotify.Watcher) {
30 }
30 }
31}
31}
32
32
33−func WatchForUpdates(filename string, action func()) bool {
33+func WatchForUpdates(filename string, done <-chan bool, action func()) bool {
34 filename = filepath.Clean(filename)
34 filename = filepath.Clean(filename)
35 watcher, err := fsnotify.NewWatcher()
35 watcher, err := fsnotify.NewWatcher()
36 if err != nil {
36 if err != nil {
@@ -40,6 +40,10 @@ func WatchForUpdates(filename string, action func()) bool {
40 defer watcher.Close()
40 defer watcher.Close()
41 for {
41 for {
42 select {
42 select {
43+ case _ = <-done:
44+ log.Printf("Shutting down watcher for: %s",
45+ filename)
46+ return
43 case event := <-watcher.Events:
47 case event := <-watcher.Events:
44 // On Arch Linux, it appears Chmod events precede Remove events,
48 // On Arch Linux, it appears Chmod events precede Remove events,
45 // which causes a race between action() and the coming Remove event.
49 // which causes a race between action() and the coming Remove event.