Skip to content

Commit 7f6e189

Browse files
nicksndeloof
authored andcommitted
watch: change the watcher interface to better match how we actually use it (docker#1835)
1 parent b5ccea7 commit 7f6e189

File tree

4 files changed

+168
-156
lines changed

4 files changed

+168
-156
lines changed

pkg/watch/notify.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,49 @@
11
package watch
22

3+
import "github.com/windmilleng/tilt/internal/logger"
4+
35
type FileEvent struct {
46
Path string
57
}
68

79
type Notify interface {
10+
// Start watching the paths set at init time
11+
Start() error
12+
13+
// Stop watching and close all channels
814
Close() error
9-
Add(name string) error
15+
16+
// A channel to read off incoming file changes
1017
Events() chan FileEvent
18+
19+
// A channel to read off show-stopping errors
1120
Errors() chan error
1221
}
22+
23+
// When we specify directories to watch, we often want to
24+
// ignore some subset of the files under those directories.
25+
//
26+
// For example:
27+
// - Watch /src/repo, but ignore /src/repo/.git
28+
// - Watch /src/repo, but ignore everything in /src/repo/bazel-bin except /src/repo/bazel-bin/app-binary
29+
//
30+
// The PathMatcher inteface helps us manage these ignores.
31+
// By design, fileutils.PatternMatcher (the interface that implements dockerignore)
32+
// satisfies this interface
33+
// https://godoc.org/github.com/docker/docker/pkg/fileutils#PatternMatcher
34+
type PathMatcher interface {
35+
Matches(file string) (bool, error)
36+
Exclusions() bool
37+
}
38+
39+
type EmptyMatcher struct {
40+
}
41+
42+
func (EmptyMatcher) Matches(f string) (bool, error) { return false, nil }
43+
func (EmptyMatcher) Exclusions() bool { return false }
44+
45+
var _ PathMatcher = EmptyMatcher{}
46+
47+
func NewWatcher(paths []string, ignore PathMatcher, l logger.Logger) (Notify, error) {
48+
return newWatcher(paths, ignore, l)
49+
}

pkg/watch/notify_test.go

Lines changed: 55 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,7 @@ func TestEventOrdering(t *testing.T) {
3636
for i, _ := range dirs {
3737
dir := f.TempDir("watched")
3838
dirs[i] = dir
39-
err := f.notify.Add(dir)
40-
if err != nil {
41-
t.Fatal(err)
42-
}
39+
f.watch(dir)
4340
}
4441

4542
f.fsync()
@@ -71,10 +68,7 @@ func TestGitBranchSwitch(t *testing.T) {
7168
for i, _ := range dirs {
7269
dir := f.TempDir("watched")
7370
dirs[i] = dir
74-
err := f.notify.Add(dir)
75-
if err != nil {
76-
t.Fatal(err)
77-
}
71+
f.watch(dir)
7872
}
7973

8074
f.fsync()
@@ -129,16 +123,13 @@ func TestWatchesAreRecursive(t *testing.T) {
129123
f.MkdirAll(subPath)
130124

131125
// watch parent
132-
err := f.notify.Add(root)
133-
if err != nil {
134-
t.Fatal(err)
135-
}
126+
f.watch(root)
136127

137128
f.fsync()
138129
f.events = nil
139130
// change sub directory
140131
changeFilePath := filepath.Join(subPath, "change")
141-
_, err = os.OpenFile(changeFilePath, os.O_RDONLY|os.O_CREATE, 0666)
132+
_, err := os.OpenFile(changeFilePath, os.O_RDONLY|os.O_CREATE, 0666)
142133
if err != nil {
143134
t.Fatal(err)
144135
}
@@ -153,10 +144,7 @@ func TestNewDirectoriesAreRecursivelyWatched(t *testing.T) {
153144
root := f.TempDir("root")
154145

155146
// watch parent
156-
err := f.notify.Add(root)
157-
if err != nil {
158-
t.Fatal(err)
159-
}
147+
f.watch(root)
160148
f.fsync()
161149
f.events = nil
162150

@@ -166,7 +154,7 @@ func TestNewDirectoriesAreRecursivelyWatched(t *testing.T) {
166154

167155
// change something inside sub directory
168156
changeFilePath := filepath.Join(subPath, "change")
169-
_, err = os.OpenFile(changeFilePath, os.O_RDONLY|os.O_CREATE, 0666)
157+
_, err := os.OpenFile(changeFilePath, os.O_RDONLY|os.O_CREATE, 0666)
170158
if err != nil {
171159
t.Fatal(err)
172160
}
@@ -180,11 +168,7 @@ func TestWatchNonExistentPath(t *testing.T) {
180168
root := f.TempDir("root")
181169
path := filepath.Join(root, "change")
182170

183-
err := f.notify.Add(path)
184-
if err != nil {
185-
t.Fatal(err)
186-
}
187-
171+
f.watch(path)
188172
f.fsync()
189173

190174
d1 := "hello\ngo\n"
@@ -200,11 +184,7 @@ func TestWatchNonExistentPathDoesNotFireSiblingEvent(t *testing.T) {
200184
watchedFile := filepath.Join(root, "a.txt")
201185
unwatchedSibling := filepath.Join(root, "b.txt")
202186

203-
err := f.notify.Add(watchedFile)
204-
if err != nil {
205-
t.Fatal(err)
206-
}
207-
187+
f.watch(watchedFile)
208188
f.fsync()
209189

210190
d1 := "hello\ngo\n"
@@ -222,13 +202,10 @@ func TestRemove(t *testing.T) {
222202
d1 := "hello\ngo\n"
223203
f.WriteFile(path, d1)
224204

225-
err := f.notify.Add(path)
226-
if err != nil {
227-
t.Fatal(err)
228-
}
205+
f.watch(path)
229206
f.fsync()
230207
f.events = nil
231-
err = os.Remove(path)
208+
err := os.Remove(path)
232209
if err != nil {
233210
t.Fatal(err)
234211
}
@@ -239,17 +216,14 @@ func TestRemoveAndAddBack(t *testing.T) {
239216
f := newNotifyFixture(t)
240217
defer f.tearDown()
241218

242-
path := filepath.Join(f.watched, "change")
219+
path := filepath.Join(f.paths[0], "change")
243220

244221
d1 := []byte("hello\ngo\n")
245222
err := ioutil.WriteFile(path, d1, 0644)
246223
if err != nil {
247224
t.Fatal(err)
248225
}
249-
err = f.notify.Add(path)
250-
if err != nil {
251-
t.Fatal(err)
252-
}
226+
f.watch(path)
253227
f.assertEvents(path)
254228

255229
err = os.Remove(path)
@@ -278,14 +252,11 @@ func TestSingleFile(t *testing.T) {
278252
d1 := "hello\ngo\n"
279253
f.WriteFile(path, d1)
280254

281-
err := f.notify.Add(path)
282-
if err != nil {
283-
t.Fatal(err)
284-
}
255+
f.watch(path)
285256
f.fsync()
286257

287258
d2 := []byte("hello\nworld\n")
288-
err = ioutil.WriteFile(path, d2, 0644)
259+
err := ioutil.WriteFile(path, d2, 0644)
289260
if err != nil {
290261
t.Fatal(err)
291262
}
@@ -296,8 +267,8 @@ func TestWriteBrokenLink(t *testing.T) {
296267
f := newNotifyFixture(t)
297268
defer f.tearDown()
298269

299-
link := filepath.Join(f.watched, "brokenLink")
300-
missingFile := filepath.Join(f.watched, "missingFile")
270+
link := filepath.Join(f.paths[0], "brokenLink")
271+
missingFile := filepath.Join(f.paths[0], "missingFile")
301272
err := os.Symlink(missingFile, link)
302273
if err != nil {
303274
t.Fatal(err)
@@ -310,13 +281,13 @@ func TestWriteGoodLink(t *testing.T) {
310281
f := newNotifyFixture(t)
311282
defer f.tearDown()
312283

313-
goodFile := filepath.Join(f.watched, "goodFile")
284+
goodFile := filepath.Join(f.paths[0], "goodFile")
314285
err := ioutil.WriteFile(goodFile, []byte("hello"), 0644)
315286
if err != nil {
316287
t.Fatal(err)
317288
}
318289

319-
link := filepath.Join(f.watched, "goodFileSymlink")
290+
link := filepath.Join(f.paths[0], "goodFileSymlink")
320291
err = os.Symlink(goodFile, link)
321292
if err != nil {
322293
t.Fatal(err)
@@ -342,11 +313,7 @@ func TestWatchBrokenLink(t *testing.T) {
342313
t.Fatal(err)
343314
}
344315

345-
err = f.notify.Add(newRoot.Path())
346-
if err != nil {
347-
t.Fatal(err)
348-
}
349-
316+
f.watch(newRoot.Path())
350317
os.Remove(link)
351318
f.assertEvents(link)
352319
}
@@ -359,15 +326,11 @@ func TestMoveAndReplace(t *testing.T) {
359326
file := filepath.Join(root, "myfile")
360327
f.WriteFile(file, "hello")
361328

362-
err := f.notify.Add(file)
363-
if err != nil {
364-
t.Fatal(err)
365-
}
366-
329+
f.watch(file)
367330
tmpFile := filepath.Join(root, ".myfile.swp")
368331
f.WriteFile(tmpFile, "world")
369332

370-
err = os.Rename(tmpFile, file)
333+
err := os.Rename(tmpFile, file)
371334
if err != nil {
372335
t.Fatal(err)
373336
}
@@ -478,37 +441,40 @@ func TestWatchNonexistentDirectory(t *testing.T) {
478441
type notifyFixture struct {
479442
out *bytes.Buffer
480443
*tempdir.TempDirFixture
481-
notify Notify
482-
watched string
483-
events []FileEvent
444+
notify Notify
445+
paths []string
446+
events []FileEvent
484447
}
485448

486449
func newNotifyFixture(t *testing.T) *notifyFixture {
487450
out := bytes.NewBuffer(nil)
488-
notify, err := NewWatcher(logger.NewLogger(logger.DebugLvl, out))
489-
if err != nil {
490-
t.Fatal(err)
491-
}
492-
493-
f := tempdir.NewTempDirFixture(t)
494-
watched := f.TempDir("watched")
495-
496-
err = notify.Add(watched)
497-
if err != nil {
498-
t.Fatal(err)
499-
}
500-
return &notifyFixture{
501-
TempDirFixture: f,
502-
watched: watched,
503-
notify: notify,
451+
nf := &notifyFixture{
452+
TempDirFixture: tempdir.NewTempDirFixture(t),
453+
paths: []string{},
504454
out: out,
505455
}
456+
nf.watch(nf.TempDir("watched"))
457+
return nf
506458
}
507459

508460
func (f *notifyFixture) watch(path string) {
509-
err := f.notify.Add(path)
461+
f.paths = append(f.paths, path)
462+
463+
// sync any outstanding events and close the old watcher
464+
if f.notify != nil {
465+
f.fsync()
466+
f.closeWatcher()
467+
}
468+
469+
// create a new watcher
470+
notify, err := NewWatcher(f.paths, EmptyMatcher{}, logger.NewLogger(logger.DebugLvl, f.out))
510471
if err != nil {
511-
f.T().Fatalf("notify.Add: %s", path)
472+
f.T().Fatal(err)
473+
}
474+
f.notify = notify
475+
err = f.notify.Start()
476+
if err != nil {
477+
f.T().Fatal(err)
512478
}
513479
}
514480

@@ -548,8 +514,8 @@ func (f *notifyFixture) consumeEventsInBackground(ctx context.Context) chan erro
548514

549515
func (f *notifyFixture) fsync() {
550516
syncPathBase := fmt.Sprintf("sync-%d.txt", time.Now().UnixNano())
551-
syncPath := filepath.Join(f.watched, syncPathBase)
552-
anySyncPath := filepath.Join(f.watched, "sync-")
517+
syncPath := filepath.Join(f.paths[0], syncPathBase)
518+
anySyncPath := filepath.Join(f.paths[0], "sync-")
553519
timeout := time.After(time.Second)
554520

555521
f.WriteFile(syncPath, fmt.Sprintf("%s", time.Now()))
@@ -582,21 +548,25 @@ F:
582548
}
583549
}
584550

585-
func (f *notifyFixture) tearDown() {
586-
err := f.notify.Close()
551+
func (f *notifyFixture) closeWatcher() {
552+
notify := f.notify
553+
err := notify.Close()
587554
if err != nil {
588555
f.T().Fatal(err)
589556
}
590557

591558
// drain channels from watcher
592559
go func() {
593-
for _ = range f.notify.Events() {
560+
for _ = range notify.Events() {
594561
}
595562
}()
596563
go func() {
597-
for _ = range f.notify.Errors() {
564+
for _ = range notify.Errors() {
598565
}
599566
}()
567+
}
600568

569+
func (f *notifyFixture) tearDown() {
570+
f.closeWatcher()
601571
f.TempDirFixture.TearDown()
602572
}

0 commit comments

Comments
 (0)