Skip to content

Commit 80207fe

Browse files
authored
Merge pull request #612 from nanobox-io/bugfix/605
Enhance file watching
2 parents fb8d781 + 290182e commit 80207fe

File tree

9 files changed

+148
-103
lines changed

9 files changed

+148
-103
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
## 2.3.1 (unreleased)
22

33
FEATURES:
4+
- Allow `-v` and `--version` to print version [#612](https://github.com/nanobox-io/nanobox/pull/612)
5+
- Make nanobox-server service able to survive reboots [#612](https://github.com/nanobox-io/nanobox/pull/612)
46
- Add dns alias' to run and dry-run containers [#592](https://github.com/nanobox-io/nanobox/pull/592)
57
- Support service configuration enhancements [#586](https://github.com/nanobox-io/nanobox/pull/586)
68
- Support password protected ssh keys [#576](https://github.com/nanobox-io/nanobox/pull/576)[#580](https://github.com/nanobox-io/nanobox/pull/580)[#582](https://github.com/nanobox-io/nanobox/pull/582)
79
- Untrack vendored files [#567](https://github.com/nanobox-io/nanobox/pull/567)
810

911
BUG FIXES:
12+
- Fix linux fs_watch issues [#612](https://github.com/nanobox-io/nanobox/pull/612)
1013
- Refactor local engine configuration [#604](https://github.com/nanobox-io/nanobox/pull/604)
1114
- Fix panic on linux if nfs isn't running [#601](https://github.com/nanobox-io/nanobox/pull/601)
1215
- Handle `nanobox-update` not being in global path [#596](https://github.com/nanobox-io/nanobox/pull/596)

commands/commands.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package commands
33

44
import (
5+
"fmt"
56
"os"
67
"path/filepath"
78
"strings"
@@ -19,7 +20,6 @@ import (
1920
"github.com/nanobox-io/nanobox/util/update"
2021
)
2122

22-
//
2323
var (
2424
// debug mode
2525
debugMode bool
@@ -30,11 +30,9 @@ var (
3030
// display level trace
3131
displayTraceMode bool
3232

33-
//
3433
internalCommand bool
35-
36-
//
37-
endpoint string
34+
showVersion bool
35+
endpoint string
3836

3937
// NanoboxCmd ...
4038
NanoboxCmd = &cobra.Command{
@@ -101,9 +99,11 @@ var (
10199
}
102100
},
103101

104-
//
105102
Run: func(ccmd *cobra.Command, args []string) {
106-
103+
if displayDebugMode || showVersion {
104+
fmt.Println(models.VersionString())
105+
return
106+
}
107107
// fall back on default help if no args/flags are passed
108108
ccmd.HelpFunc()(ccmd, args)
109109
},
@@ -120,6 +120,7 @@ func init() {
120120
NanoboxCmd.PersistentFlags().MarkHidden("internal")
121121
NanoboxCmd.PersistentFlags().BoolVarP(&debugMode, "debug", "", false, "In the event of a failure, drop into debug context")
122122
NanoboxCmd.PersistentFlags().BoolVarP(&displayDebugMode, "verbose", "v", false, "Increases display output and sets level to debug")
123+
NanoboxCmd.PersistentFlags().BoolVarP(&showVersion, "version", "", false, "Print version information and exit")
123124
NanoboxCmd.PersistentFlags().BoolVarP(&displayTraceMode, "trace", "t", false, "Increases display output and sets level to trace")
124125

125126
// log specific flags

util/provider/share/share_darwin.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,15 @@ func Remove(path string) error {
160160
shareRPC := &ShareRPC{}
161161
err := shareRPC.Remove(req, resp)
162162
if err != nil || !resp.Success {
163-
err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
163+
err = fmt.Errorf("failed to remove share %v %v", err, resp.Message)
164164
}
165165
return err
166166
}
167167

168168
// have the server run the share command
169169
err = server.ClientRun("ShareRPC.Remove", req, resp)
170170
if err != nil || !resp.Success {
171-
err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
171+
err = fmt.Errorf("failed to remove share %v %v", err, resp.Message)
172172
}
173173
return err
174174

util/provider/share/share_linux.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,15 @@ func Remove(path string) error {
123123
shareRPC := &ShareRPC{}
124124
err := shareRPC.Remove(req, resp)
125125
if err != nil || !resp.Success {
126-
err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
126+
err = fmt.Errorf("failed to remove share %v %v", err, resp.Message)
127127
}
128128
return err
129129
}
130130

131131
// have the server run the share command
132132
err = server.ClientRun("ShareRPC.Remove", req, resp)
133133
if err != nil || !resp.Success {
134-
err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
134+
err = fmt.Errorf("failed to remove share %v %v", err, resp.Message)
135135
}
136136
return err
137137
}

util/provider/share/share_windows.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,15 @@ func Remove(path string) error {
126126
shareRPC := &ShareRPC{}
127127
err := shareRPC.Remove(req, resp)
128128
if err != nil || !resp.Success {
129-
err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
129+
err = fmt.Errorf("failed to remove share %v %v", err, resp.Message)
130130
}
131131
return err
132132
}
133133

134134
// have the server run the share command
135135
err := server.ClientRun("ShareRPC.Remove", req, resp)
136136
if err != nil || !resp.Success {
137-
err = fmt.Errorf("failed to add share %v %v", err, resp.Message)
137+
err = fmt.Errorf("failed to remove share %v %v", err, resp.Message)
138138
}
139139
return err
140140

util/service/create_linux.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ After=network.target
2121
2222
[Service]
2323
Type=simple
24-
EnvironmentFile=-/etc/sysconfig/network
2524
ExecStart=%s
25+
26+
[Install]
27+
WantedBy=multi-user.target
2628
`, name, strings.Join(command, " "))
2729

2830
case "upstart":

util/watch/notify.go

+101-69
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package watch
22

33
import (
4+
"fmt"
45
"os"
56
"path/filepath"
67
"strings"
@@ -9,99 +10,130 @@ import (
910
"github.com/jcelliott/lumber"
1011
)
1112

12-
type notify struct {
13-
path string
14-
events chan event
15-
watcher *fsnotify.Watcher
13+
type event struct {
14+
file string
15+
error error
16+
fsnotify.Event
1617
}
1718

18-
func newNotifyWatcher(path string) Watcher {
19-
return &notify{
20-
path: path,
21-
events: make(chan event, 10),
22-
}
19+
type Watcher interface {
20+
watch() error
21+
eventChan() chan event
22+
close() error
2323
}
2424

25-
// start the watching process and return an error if we cant watch all the files
26-
func (n *notify) watch() (err error) {
25+
type notify struct {
26+
events chan event // separate event channel so we don't send on all fsnotify.Events
27+
*fsnotify.Watcher
28+
}
2729

28-
n.watcher, err = fsnotify.NewWatcher()
29-
if err != nil {
30-
return err
30+
func newRecursiveWatcher(path string) (Watcher, error) {
31+
folders := subfolders(path)
32+
if len(folders) == 0 {
33+
return nil, fmt.Errorf("No folders to watch")
3134
}
3235

33-
err = filepath.Walk(n.path, n.walkFunc)
36+
watcher, err := fsnotify.NewWatcher()
3437
if err != nil {
35-
return err
38+
return nil, err
3639
}
3740

38-
go n.EventHandler()
41+
notifyWatcher := &notify{Watcher: watcher, events: make(chan event)}
3942

40-
return
43+
for i := range folders {
44+
lumber.Info("Adding %s", folders[i])
45+
err = notifyWatcher.Add(folders[i])
46+
if err != nil {
47+
return nil, err
48+
}
49+
}
50+
return notifyWatcher, nil
4151
}
4252

43-
func (n *notify) eventChan() chan event {
44-
return n.events
45-
}
53+
func run(watcher *notify) {
54+
for {
55+
select {
56+
case evnt := <-watcher.Events:
57+
if shouldIgnoreFile(filepath.Base(evnt.Name)) {
58+
continue
59+
}
4660

47-
// close the watcher
48-
func (n *notify) close() error {
49-
return n.watcher.Close()
50-
}
61+
if evnt.Op&fsnotify.Create == fsnotify.Create {
62+
fi, err := os.Stat(evnt.Name)
63+
if err != nil {
64+
// stat ./4913: no such file or directory
65+
lumber.Error("Failed to stat on event - %s", err.Error())
66+
} else if fi.IsDir() {
67+
lumber.Info("Detected dir creation: %s", evnt.Name) // todo: Debug doesn't work
68+
if !shouldIgnoreFile(filepath.Base(evnt.Name)) {
69+
err = watcher.Add(evnt.Name)
70+
if err != nil {
71+
lumber.Error("ERROR - %s", err.Error())
72+
}
73+
}
74+
} else {
75+
lumber.Info("Detected creation: %s", evnt.Name) // todo: Debug doesn't work
76+
watcher.events <- event{file: evnt.Name}
77+
}
78+
}
5179

52-
// add a file that is being walked to the watch system
53-
func (n *notify) walkFunc(path string, info os.FileInfo, err error) error {
54-
if err != nil {
55-
return err
80+
if evnt.Op&fsnotify.Write == fsnotify.Write {
81+
lumber.Info("Detected modification: %s", evnt.Name) // todo: Debug doesn't work
82+
watcher.events <- event{file: evnt.Name}
83+
}
84+
85+
case err := <-watcher.Errors:
86+
lumber.Error("Watcher error encountered - %s", err.Error())
87+
}
5688
}
89+
}
5790

58-
for _, ignoreName := range ignoreFile {
59-
if strings.HasSuffix(path, ignoreName) {
60-
lumber.Info("watcher: skipping %s", path)
61-
if info.IsDir() {
62-
// if the thing we are ignoring is a directory
91+
// subfolders returns a slice of subfolders (recursive), including the folder provided.
92+
func subfolders(path string) (paths []string) {
93+
filepath.Walk(path, func(newPath string, info os.FileInfo, err error) error {
94+
if err != nil {
95+
return err
96+
}
97+
98+
if info.IsDir() {
99+
name := info.Name()
100+
// skip folders that begin with a dot
101+
if shouldIgnoreFile(name) && name != "." && name != ".." {
63102
return filepath.SkipDir
64103
}
65-
// if its not just skip the file
66-
return nil
104+
paths = append(paths, newPath)
105+
}
106+
return nil
107+
})
108+
return paths
109+
}
110+
111+
// shouldIgnoreFile determines if a file should be ignored.
112+
// Ignore files that start with `.` or `_` or end with `~`.
113+
func shouldIgnoreFile(name string) bool {
114+
for i := range ignoreFile {
115+
if name == ignoreFile[i] {
116+
return true
67117
}
68118
}
69119

70-
return n.watcher.Add(path)
120+
return strings.HasPrefix(name, ".") ||
121+
strings.HasPrefix(name, "_") ||
122+
strings.HasSuffix(name, ".swp") ||
123+
strings.HasSuffix(name, "~")
71124
}
72125

73-
func (n *notify) EventHandler() {
74-
for {
75-
select {
76-
case e := <-n.watcher.Events:
77-
lumber.Debug("e: %+v", e)
78-
switch {
79-
case e.Op&fsnotify.Create == fsnotify.Create:
80-
// a new file/folder was created.. add it
81-
n.watcher.Add(e.Name)
82-
// send an event
83-
n.events <- event{file: e.Name}
84-
85-
case e.Op&fsnotify.Write == fsnotify.Write:
86-
// a file was written to.. send the event
87-
n.events <- event{file: e.Name}
88-
89-
case e.Op&fsnotify.Remove == fsnotify.Remove:
90-
// a file was removed. remove it
91-
n.watcher.Remove(e.Name)
92-
93-
case e.Op&fsnotify.Rename == fsnotify.Rename:
94-
// remove from watcher because we no longer need to watch
95-
n.watcher.Remove(e.Name)
96-
97-
case e.Op&fsnotify.Chmod == fsnotify.Chmod:
98-
// ignore anything that is just changing modes
99-
// mostlikely it was just a touch
126+
// start the watching process and return an error if we cant watch all the files
127+
func (n *notify) watch() (err error) {
128+
go run(n)
129+
return nil
130+
}
100131

101-
}
102-
case err := <-n.watcher.Errors:
103-
n.events <- event{file: "", error: err}
104-
}
105-
}
132+
func (n *notify) eventChan() chan event {
133+
return n.events
134+
}
106135

136+
// close the watcher
137+
func (n *notify) close() error {
138+
return n.Close()
107139
}

util/watch/notify_internal_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import (
99

1010
func TestNotifyFiles(t *testing.T) {
1111
os.MkdirAll("/tmp/nanobox/", 0777)
12-
notifyWatcher := newNotifyWatcher("/tmp/nanobox/")
13-
err := notifyWatcher.watch()
12+
notifyWatcher, err := newRecursiveWatcher("/tmp/nanobox/")
13+
defer notifyWatcher.close()
1414
if err != nil {
1515
t.Fatalf("failed to watch: %s", err)
1616
}
17+
notifyWatcher.watch()
1718

18-
defer notifyWatcher.close()
1919
<-time.After(time.Second)
2020
ioutil.WriteFile("/tmp/nanobox/notify.tmp", []byte("hi"), 0777)
2121

0 commit comments

Comments
 (0)