@@ -7,18 +7,22 @@ import (
7
7
"os/signal"
8
8
"path/filepath"
9
9
"regexp"
10
+ "sync"
10
11
"time"
11
12
12
13
log "github.com/Sirupsen/logrus"
13
14
14
- "github.com/fsnotify/fsnotify "
15
+ "github.com/rjeczalik/notify "
15
16
"github.com/urfave/cli"
16
17
)
17
18
18
19
const (
19
- version = "0.2 "
20
+ version = "0.3 "
20
21
)
21
22
23
+ var resolvContent []byte
24
+ var rcl sync.RWMutex
25
+
22
26
func main () {
23
27
24
28
app := cli .NewApp ()
@@ -57,23 +61,25 @@ func Run(ctx *cli.Context) error {
57
61
log .Info ("Debug logging enabled" )
58
62
}
59
63
60
- resolvContent , err := refreshAll ([] byte {}, true )
64
+ err := refreshAll (true )
61
65
if err != nil {
62
66
log .WithError (err ).Error ("Error doing initial refresh" )
63
67
return err
64
68
}
65
69
66
- watcher , err := fsnotify .NewWatcher ()
70
+ resolvEvents := make (chan notify.EventInfo , 8 )
71
+ err = notify .Watch ("/etc/resolv.conf" , resolvEvents , notify .Write )
67
72
if err != nil {
68
- log .WithError (err ).Error ("Failed to create new watcher" )
73
+ log .WithError (err ).Error ("Failed to resolv.conf notifications" )
74
+ return err
75
+ }
76
+
77
+ dkrResolvEvents := make (chan notify.EventInfo , 256 )
78
+ err = notify .Watch ("/var/lib/docker/containers/..." , dkrResolvEvents , notify .Write )
79
+ if err != nil {
80
+ log .WithError (err ).Error ("Failed to container notifications" )
69
81
return err
70
82
}
71
- defer func () {
72
- err := watcher .Close ()
73
- if err != nil {
74
- log .WithError (err ).Error ("Error closing watcher" )
75
- }
76
- }()
77
83
78
84
stop := make (chan struct {})
79
85
done := make (chan struct {})
@@ -91,78 +97,40 @@ func Run(ctx *cli.Context) error {
91
97
refresh := ctx .Int ("refresh" )
92
98
go func () {
93
99
dockerResolv := regexp .MustCompile ("^/var/lib/docker/containers/[A-Za-z0-9]*/resolv.conf$" )
94
- dockerDir := regexp .MustCompile ("^/var/lib/docker/containers/[A-Za-z0-9]*$" )
95
100
t := time .NewTicker (time .Duration (max (refresh , 1 )) * time .Second )
96
101
if refresh <= 0 {
97
102
t .Stop ()
98
103
}
99
104
for {
100
105
select {
101
- case ev := <- watcher .Events :
102
- if ev .Name == "/etc/resolv.conf" {
103
- resolvContent , err = refreshAll (resolvContent , false )
104
- if err != nil {
105
- log .WithError (err ).Error ("Error refreshing after /etc/resolv.conf changed" )
106
- }
107
- continue
108
- }
109
- if dockerResolv .MatchString (ev .Name ) {
110
- go fixResolvConf (ev .Name , resolvContent )
111
- continue
112
- }
113
- if dockerDir .MatchString (ev .Name ) {
114
- if ev .Op & fsnotify .Remove != 0 {
115
- watcher .Remove (ev .Name )
116
- continue
117
- }
118
- fi , err := os .Stat (ev .Name )
119
- if err != nil && fi != nil && fi .IsDir () {
120
- err := watcher .Add (ev .Name )
121
- if err != nil {
122
- log .WithError (err ).WithField ("dir" , ev .Name ).Error ("Error adding watch" )
123
- }
124
- go fixResolvConf (ev .Name + "/resolv.conf" , resolvContent )
125
- }
126
- continue
127
- }
128
- continue
129
106
case <- t .C :
130
- resolvContent , err = refreshAll (resolvContent , true )
107
+ err = refreshAll (true )
131
108
if err != nil {
132
109
log .WithError (err ).Error ("Error during scheduled refresh" )
133
110
}
134
111
continue
112
+ case ev := <- dkrResolvEvents :
113
+ if ! dockerResolv .MatchString (ev .Path ()) {
114
+ continue
115
+ }
116
+ go fixResolvConf (ev .Path ())
117
+ case <- resolvEvents :
118
+ go func () {
119
+ err = refreshAll (false )
120
+ if err != nil {
121
+ log .WithError (err ).Error ("Error refreshing after /etc/resolv.conf changed" )
122
+ }
123
+ }()
135
124
case <- stop :
125
+ notify .Stop (dkrResolvEvents )
126
+ notify .Stop (resolvEvents )
127
+ rcl .Lock () // Ensure all writing is done by locking rcl
136
128
close (done )
137
129
return
138
130
}
139
131
}
140
132
}()
141
133
142
- err = watcher .Add ("/var/lib/docker/containers/" )
143
- if err != nil {
144
- log .WithError (err ).Error ("Failed to add container watch" )
145
- return err
146
- }
147
- err = watcher .Add ("/etc" )
148
- if err != nil {
149
- log .WithError (err ).Error ("Failed to add resolv.conf watch" )
150
- return err
151
- }
152
- conts , err := filepath .Glob ("/var/lib/docker/containers/*" )
153
- if err != nil {
154
- log .WithError (err ).Error ("Failed to list existing containers" )
155
- return err
156
- }
157
- if conts != nil {
158
- for _ , c := range conts {
159
- err := watcher .Add (c )
160
- if err != nil {
161
- log .WithError (err ).WithField ("container" , c ).Error ("Error adding watch for container" )
162
- }
163
- }
164
- }
165
-
166
134
c := make (chan os.Signal , 1 )
167
135
signal .Notify (c , os .Interrupt )
168
136
defer close (c )
@@ -175,30 +143,36 @@ func Run(ctx *cli.Context) error {
175
143
return nil
176
144
}
177
145
178
- func refreshAll (resolvContent [] byte , force bool ) ( newResolvContent [] byte , err error ) {
179
- newResolvContent , err = ioutil .ReadFile ("/etc/resolv.conf" )
146
+ func refreshAll (force bool ) error {
147
+ newResolvContent , err : = ioutil .ReadFile ("/etc/resolv.conf" )
180
148
if err != nil {
181
149
log .WithError (err ).Error ("Failed to read /etc/resolv.conf" )
182
- return
150
+ return err
183
151
}
152
+ rcl .RLock ()
184
153
if bytes .Equal (newResolvContent , resolvContent ) {
185
154
log .Debug ("/etc/resolv.conf unchanged" )
186
155
if ! force {
187
- return
156
+ rcl .RUnlock ()
157
+ return nil
188
158
}
189
159
}
160
+ rcl .RUnlock ()
161
+ rcl .Lock ()
162
+ resolvContent = newResolvContent
190
163
log .WithField ("content" , string (resolvContent )).Debug ("/etc/resov.conf content retrieved" )
164
+ rcl .Unlock ()
191
165
resolvs , err := filepath .Glob ("/var/lib/docker/containers/*/resolv.conf" )
192
166
if err != nil {
193
167
log .WithError (err ).Error ("Failed to list existing resolv.conf files" )
194
- return
168
+ return err
195
169
}
196
170
if resolvs != nil {
197
171
for _ , r := range resolvs {
198
- go fixResolvConf (r , newResolvContent )
172
+ go fixResolvConf (r )
199
173
}
200
174
}
201
- return
175
+ return nil
202
176
}
203
177
204
178
func max (a , b int ) int {
@@ -207,3 +181,30 @@ func max(a, b int) int {
207
181
}
208
182
return b
209
183
}
184
+
185
+ func fixResolvConf (path string ) {
186
+ _ , err := os .Stat (path )
187
+ if err != nil {
188
+ log .WithField ("Path" , path ).Debug ("File does not exist" )
189
+ return
190
+ }
191
+
192
+ c , err := ioutil .ReadFile (path )
193
+ if err != nil {
194
+ log .WithError (err ).WithField ("Path" , path ).Error ("Failed to read file" )
195
+ return
196
+ }
197
+
198
+ rcl .RLock ()
199
+ defer rcl .RUnlock ()
200
+ if bytes .Equal (c , resolvContent ) {
201
+ log .WithField ("Path" , path ).Debug ("File already has correct content" )
202
+ return
203
+ }
204
+
205
+ err = ioutil .WriteFile (path , resolvContent , 0644 )
206
+ if err != nil {
207
+ log .WithError (err ).WithField ("Path" , path ).Error ("Failed to write to file" )
208
+ }
209
+ log .WithField ("Path" , path ).Debug ("File content updated" )
210
+ }
0 commit comments