istio
343 строки · 7.9 Кб
1// Copyright Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package filewatcher
16
17import (
18"errors"
19"fmt"
20"os"
21"os/exec"
22"path"
23"runtime"
24"sync"
25"testing"
26
27"github.com/fsnotify/fsnotify"
28. "github.com/onsi/gomega"
29)
30
31func newWatchFile(t *testing.T) string {
32g := NewGomegaWithT(t)
33
34watchDir := t.TempDir()
35watchFile := path.Join(watchDir, "test.conf")
36err := os.WriteFile(watchFile, []byte("foo: bar\n"), 0o640)
37g.Expect(err).NotTo(HaveOccurred())
38
39return watchFile
40}
41
42func newWatchFileThatDoesNotExist(t *testing.T) string {
43watchDir := t.TempDir()
44
45watchFile := path.Join(watchDir, "test.conf")
46
47return watchFile
48}
49
50// newTwoWatchFile returns with two watch files that exist in the same base dir.
51func newTwoWatchFile(t *testing.T) (string, string) {
52g := NewGomegaWithT(t)
53
54watchDir := t.TempDir()
55
56watchFile1 := path.Join(watchDir, "test1.conf")
57err := os.WriteFile(watchFile1, []byte("foo: bar\n"), 0o640)
58g.Expect(err).NotTo(HaveOccurred())
59
60watchFile2 := path.Join(watchDir, "test2.conf")
61err = os.WriteFile(watchFile2, []byte("foo: baz\n"), 0o640)
62g.Expect(err).NotTo(HaveOccurred())
63
64return watchFile1, watchFile2
65}
66
67// newSymlinkedWatchFile simulates the behavior of k8s configmap/secret.
68// Path structure looks like:
69//
70// <watchDir>/test.conf
71// ^
72// |
73//
74// <watchDir>/data/test.conf
75//
76// ^
77// |
78//
79// <watchDir>/data1/test.conf
80func newSymlinkedWatchFile(t *testing.T) (string, string) {
81g := NewGomegaWithT(t)
82
83watchDir := t.TempDir()
84
85dataDir1 := path.Join(watchDir, "data1")
86err := os.Mkdir(dataDir1, 0o777)
87g.Expect(err).NotTo(HaveOccurred())
88
89realTestFile := path.Join(dataDir1, "test.conf")
90t.Logf("Real test file location: %s\n", realTestFile)
91err = os.WriteFile(realTestFile, []byte("foo: bar\n"), 0o640)
92g.Expect(err).NotTo(HaveOccurred())
93
94// Now, symlink the tmp `data1` dir to `data` in the baseDir
95os.Symlink(dataDir1, path.Join(watchDir, "data"))
96// And link the `<watchdir>/datadir/test.conf` to `<watchdir>/test.conf`
97watchFile := path.Join(watchDir, "test.conf")
98os.Symlink(path.Join(watchDir, "data", "test.conf"), watchFile)
99fmt.Printf("Watch file location: %s\n", path.Join(watchDir, "test.conf"))
100return watchDir, watchFile
101}
102
103func TestWatchFile(t *testing.T) {
104t.Run("file content changed", func(t *testing.T) {
105g := NewGomegaWithT(t)
106
107// Given a file being watched
108watchFile := newWatchFile(t)
109_, err := os.Stat(watchFile)
110g.Expect(err).NotTo(HaveOccurred())
111
112w := NewWatcher()
113w.Add(watchFile)
114events := w.Events(watchFile)
115
116wg := sync.WaitGroup{}
117wg.Add(1)
118go func() {
119<-events
120wg.Done()
121}()
122
123// Overwriting the file and waiting its event to be received.
124err = os.WriteFile(watchFile, []byte("foo: baz\n"), 0o640)
125g.Expect(err).NotTo(HaveOccurred())
126wg.Wait()
127
128_ = w.Close()
129})
130
131t.Run("link to real file changed (for k8s configmap/secret path)", func(t *testing.T) {
132// skip if not executed on Linux
133if runtime.GOOS != "linux" {
134t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
135}
136g := NewGomegaWithT(t)
137
138watchDir, watchFile := newSymlinkedWatchFile(t)
139
140w := NewWatcher()
141w.Add(watchFile)
142events := w.Events(watchFile)
143
144wg := sync.WaitGroup{}
145wg.Add(1)
146go func() {
147<-events
148wg.Done()
149}()
150
151// Link to another `test.conf` file
152dataDir2 := path.Join(watchDir, "data2")
153err := os.Mkdir(dataDir2, 0o777)
154g.Expect(err).NotTo(HaveOccurred())
155
156watchFile2 := path.Join(dataDir2, "test.conf")
157err = os.WriteFile(watchFile2, []byte("foo: baz\n"), 0o640)
158g.Expect(err).NotTo(HaveOccurred())
159
160// change the symlink using the `ln -sfn` command
161err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
162g.Expect(err).NotTo(HaveOccurred())
163
164// Wait its event to be received.
165wg.Wait()
166
167_ = w.Close()
168})
169
170t.Run("file added later", func(t *testing.T) {
171g := NewGomegaWithT(t)
172
173// Given a file being watched
174watchFile := newWatchFileThatDoesNotExist(t)
175
176w := NewWatcher()
177w.Add(watchFile)
178events := w.Events(watchFile)
179
180wg := sync.WaitGroup{}
181wg.Add(1)
182go func() {
183<-events
184wg.Done()
185}()
186
187// Overwriting the file and waiting its event to be received.
188err := os.WriteFile(watchFile, []byte("foo: baz\n"), 0o640)
189g.Expect(err).NotTo(HaveOccurred())
190wg.Wait()
191
192_ = w.Close()
193})
194}
195
196func TestWatcherLifecycle(t *testing.T) {
197g := NewGomegaWithT(t)
198
199watchFile1, watchFile2 := newTwoWatchFile(t)
200
201w := NewWatcher()
202
203// Validate Add behavior
204err := w.Add(watchFile1)
205g.Expect(err).NotTo(HaveOccurred())
206err = w.Add(watchFile2)
207g.Expect(err).NotTo(HaveOccurred())
208
209// Validate events and errors channel are fulfilled.
210events1 := w.Events(watchFile1)
211g.Expect(events1).NotTo(BeNil())
212events2 := w.Events(watchFile2)
213g.Expect(events2).NotTo(BeNil())
214
215errors1 := w.Errors(watchFile1)
216g.Expect(errors1).NotTo(BeNil())
217errors2 := w.Errors(watchFile2)
218g.Expect(errors2).NotTo(BeNil())
219
220// Validate Remove behavior
221err = w.Remove(watchFile1)
222g.Expect(err).NotTo(HaveOccurred())
223events1 = w.Events(watchFile1)
224g.Expect(events1).To(BeNil())
225errors1 = w.Errors(watchFile1)
226g.Expect(errors1).To(BeNil())
227events2 = w.Events(watchFile2)
228g.Expect(events2).NotTo(BeNil())
229errors2 = w.Errors(watchFile2)
230g.Expect(errors2).NotTo(BeNil())
231
232fmt.Printf("2\n")
233// Validate Close behavior
234err = w.Close()
235g.Expect(err).NotTo(HaveOccurred())
236events1 = w.Events(watchFile1)
237g.Expect(events1).To(BeNil())
238errors1 = w.Errors(watchFile1)
239g.Expect(errors1).To(BeNil())
240events2 = w.Events(watchFile2)
241g.Expect(events2).To(BeNil())
242errors2 = w.Errors(watchFile2)
243g.Expect(errors2).To(BeNil())
244}
245
246func TestErrors(t *testing.T) {
247w := NewWatcher()
248
249if ch := w.Errors("XYZ"); ch != nil {
250t.Error("Expected no channel")
251}
252
253if ch := w.Events("XYZ"); ch != nil {
254t.Error("Expected no channel")
255}
256
257name := newWatchFile(t)
258_ = w.Add(name)
259_ = w.Remove(name)
260
261if ch := w.Errors("XYZ"); ch != nil {
262t.Error("Expected no channel")
263}
264
265if ch := w.Events(name); ch != nil {
266t.Error("Expected no channel")
267}
268
269_ = w.Close()
270
271if err := w.Add(name); err == nil {
272t.Error("Expecting error")
273}
274
275if err := w.Remove(name); err == nil {
276t.Error("Expecting error")
277}
278
279if ch := w.Errors(name); ch != nil {
280t.Error("Expecting nil")
281}
282
283if ch := w.Events(name); ch != nil {
284t.Error("Expecting nil")
285}
286}
287
288func TestBadWatcher(t *testing.T) {
289w := NewWatcher()
290w.(*fileWatcher).funcs.newWatcher = func() (*fsnotify.Watcher, error) {
291return nil, errors.New("FOOBAR")
292}
293
294name := newWatchFile(t)
295if err := w.Add(name); err == nil {
296t.Errorf("Expecting error, got nil")
297}
298if err := w.Close(); err != nil {
299t.Errorf("Expecting nil, got %v", err)
300}
301}
302
303func TestBadAddWatcher(t *testing.T) {
304w := NewWatcher()
305w.(*fileWatcher).funcs.addWatcherPath = func(*fsnotify.Watcher, string) error {
306return errors.New("FOOBAR")
307}
308
309name := newWatchFile(t)
310if err := w.Add(name); err == nil {
311t.Errorf("Expecting error, got nil")
312}
313if err := w.Close(); err != nil {
314t.Errorf("Expecting nil, got %v", err)
315}
316}
317
318func TestDuplicateAdd(t *testing.T) {
319w := NewWatcher()
320
321name := newWatchFile(t)
322
323if err := w.Add(name); err != nil {
324t.Errorf("Expecting nil, got %v", err)
325}
326
327if err := w.Add(name); err == nil {
328t.Errorf("Expecting error, got nil")
329}
330
331_ = w.Close()
332}
333
334func TestBogusRemove(t *testing.T) {
335w := NewWatcher()
336
337name := newWatchFile(t)
338if err := w.Remove(name); err == nil {
339t.Errorf("Expecting error, got nil")
340}
341
342_ = w.Close()
343}
344