diff options
author | Valery Piashchynski <[email protected]> | 2020-12-16 18:25:11 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2020-12-16 18:25:11 +0300 |
commit | cd62adcf3a9ee99c146df283a3dcad182f55aefe (patch) | |
tree | a0443eb6687c76ae3f22fe32b19fea8a237809b8 /plugins/reload | |
parent | 4f9b2aacd77d4bcddccda04abf9d18c649f69a02 (diff) |
Finish tests for the Reload plugin
Diffstat (limited to 'plugins/reload')
-rw-r--r-- | plugins/reload/tests/configs/.rr-reload-2.yaml | 41 | ||||
-rw-r--r-- | plugins/reload/tests/configs/.rr-reload-3.yaml | 43 | ||||
-rw-r--r-- | plugins/reload/tests/configs/.rr-reload-4.yaml | 43 | ||||
-rw-r--r-- | plugins/reload/tests/plugin_test.go | 1 | ||||
-rw-r--r-- | plugins/reload/tests/reload_plugin_test.go | 586 | ||||
-rw-r--r-- | plugins/reload/watcher.go | 17 |
6 files changed, 688 insertions, 43 deletions
diff --git a/plugins/reload/tests/configs/.rr-reload-2.yaml b/plugins/reload/tests/configs/.rr-reload-2.yaml new file mode 100644 index 00000000..6c00eeb9 --- /dev/null +++ b/plugins/reload/tests/configs/.rr-reload-2.yaml @@ -0,0 +1,41 @@ +server: + command: php ../../../tests/psr-worker-bench.php + user: '' + group: '' + env: + RR_HTTP: 'true' + relay: pipes + relayTimeout: 20s +http: + debug: true + address: '127.0.0.1:27388' + maxRequestSize: 1024 + middleware: + - '' + uploads: + forbid: + - .php + - .exe + - .bat + trustedSubnets: + - 10.0.0.0/8 + - 127.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + - '::1/128' + - 'fc00::/7' + - 'fe80::/10' + pool: + numWorkers: 2 + maxJobs: 0 + allocateTimeout: 60s + destroyTimeout: 60s +reload: + interval: 2s + patterns: + - .txt + services: + http: + dirs: + - './unit_tests' + recursive: true diff --git a/plugins/reload/tests/configs/.rr-reload-3.yaml b/plugins/reload/tests/configs/.rr-reload-3.yaml new file mode 100644 index 00000000..ad883f2d --- /dev/null +++ b/plugins/reload/tests/configs/.rr-reload-3.yaml @@ -0,0 +1,43 @@ +server: + command: php ../../../tests/psr-worker-bench.php + user: '' + group: '' + env: + RR_HTTP: 'true' + relay: pipes + relayTimeout: 20s +http: + debug: true + address: '127.0.0.1:37388' + maxRequestSize: 1024 + middleware: + - '' + uploads: + forbid: + - .php + - .exe + - .bat + trustedSubnets: + - 10.0.0.0/8 + - 127.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + - '::1/128' + - 'fc00::/7' + - 'fe80::/10' + pool: + numWorkers: 2 + maxJobs: 0 + allocateTimeout: 60s + destroyTimeout: 60s +reload: + interval: 2s + patterns: + - .txt + services: + http: + dirs: + - './unit_tests' + - './unit_tests_copied' + - './dir1' + recursive: true diff --git a/plugins/reload/tests/configs/.rr-reload-4.yaml b/plugins/reload/tests/configs/.rr-reload-4.yaml new file mode 100644 index 00000000..b74effe6 --- /dev/null +++ b/plugins/reload/tests/configs/.rr-reload-4.yaml @@ -0,0 +1,43 @@ +server: + command: php ../../../tests/psr-worker-bench.php + user: '' + group: '' + env: + RR_HTTP: 'true' + relay: pipes + relayTimeout: 20s +http: + debug: true + address: '127.0.0.1:22766' + maxRequestSize: 1024 + middleware: + - '' + uploads: + forbid: + - .php + - .exe + - .bat + trustedSubnets: + - 10.0.0.0/8 + - 127.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + - '::1/128' + - 'fc00::/7' + - 'fe80::/10' + pool: + numWorkers: 2 + maxJobs: 0 + allocateTimeout: 60s + destroyTimeout: 60s +reload: + interval: 2s + patterns: + - .aaa + services: + http: + dirs: + - './unit_tests' + - './unit_tests_copied' + - './dir1' + recursive: false diff --git a/plugins/reload/tests/plugin_test.go b/plugins/reload/tests/plugin_test.go deleted file mode 100644 index ca8701d2..00000000 --- a/plugins/reload/tests/plugin_test.go +++ /dev/null @@ -1 +0,0 @@ -package tests diff --git a/plugins/reload/tests/reload_plugin_test.go b/plugins/reload/tests/reload_plugin_test.go index 9f1e5517..dc4ef827 100644 --- a/plugins/reload/tests/reload_plugin_test.go +++ b/plugins/reload/tests/reload_plugin_test.go @@ -20,7 +20,6 @@ import ( "github.com/spiral/roadrunner/v2/mocks" "github.com/spiral/roadrunner/v2/plugins/config" httpPlugin "github.com/spiral/roadrunner/v2/plugins/http" - "github.com/spiral/roadrunner/v2/plugins/logger" "github.com/spiral/roadrunner/v2/plugins/reload" "github.com/spiral/roadrunner/v2/plugins/resetter" "github.com/spiral/roadrunner/v2/plugins/server" @@ -28,6 +27,7 @@ import ( ) const testDir string = "unit_tests" +const testCopyToDir string = "unit_tests_copied" const hugeNumberOfFiles uint = 5000 func TestReloadInit(t *testing.T) { @@ -51,7 +51,9 @@ func TestReloadInit(t *testing.T) { controller := gomock.NewController(t) mockLogger := mocks.NewMockLogger(controller) + mockLogger.EXPECT().Debug("http handler response received", "elapsed", gomock.Any(), "remote address", "127.0.0.1").Times(1) mockLogger.EXPECT().Debug("file was created", "path", gomock.Any(), "name", "file.txt", "size", gomock.Any()).Times(2) + mockLogger.EXPECT().Debug("file was added to watcher", "path", gomock.Any(), "name", "file.txt", "size", gomock.Any()).Times(2) mockLogger.EXPECT().Info("HTTP plugin got restart request. Restarting...").Times(1) mockLogger.EXPECT().Info("HTTP workers Pool successfully restarted").Times(1) mockLogger.EXPECT().Info("HTTP listeners successfully re-added").Times(1) @@ -111,6 +113,7 @@ func TestReloadInit(t *testing.T) { }() t.Run("ReloadTestInit", reloadTestInit) + reloadHTTPLiveAfterReset(t, "22388") wg.Wait() } @@ -132,23 +135,32 @@ func TestReloadHugeNumberOfFiles(t *testing.T) { // try to remove, skip error assert.NoError(t, freeResources(testDir)) + assert.NoError(t, freeResources(testCopyToDir)) err = os.Mkdir(testDir, 0755) assert.NoError(t, err) + err = os.Mkdir(testCopyToDir, 0755) + assert.NoError(t, err) defer func() { assert.NoError(t, freeResources(testDir)) + assert.NoError(t, freeResources(testCopyToDir)) }() - // controller := gomock.NewController(t) - // mockLogger := mocks.NewMockLogger(controller) - // - // mockLogger.EXPECT().Debug("file was created", "path", gomock.Any(), "name", "file.txt", "size", gomock.Any()).Times(2) - // mockLogger.EXPECT().Info("Resetting http plugin").Times(1) + controller := gomock.NewController(t) + mockLogger := mocks.NewMockLogger(controller) + + mockLogger.EXPECT().Debug("http handler response received", "elapsed", gomock.Any(), "remote address", "127.0.0.1").Times(1) + mockLogger.EXPECT().Debug("file was created", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(1) + mockLogger.EXPECT().Debug("file was updated", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(1) + mockLogger.EXPECT().Debug("file was added to watcher", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(1) + mockLogger.EXPECT().Info("HTTP plugin got restart request. Restarting...").MinTimes(1) + mockLogger.EXPECT().Info("HTTP workers Pool successfully restarted").MinTimes(1) + mockLogger.EXPECT().Info("HTTP listeners successfully re-added").MinTimes(1) + mockLogger.EXPECT().Info("HTTP plugin successfully restarted").MinTimes(1) err = cont.RegisterAll( cfg, - // mockLogger, - &logger.ZapLogger{}, + mockLogger, &server.Plugin{}, &httpPlugin.Plugin{}, &reload.Plugin{}, @@ -170,7 +182,7 @@ func TestReloadHugeNumberOfFiles(t *testing.T) { wg := &sync.WaitGroup{} wg.Add(1) - tt := time.NewTimer(time.Second * 160) + tt := time.NewTimer(time.Second * 100) go func() { defer wg.Done() @@ -202,48 +214,569 @@ func TestReloadHugeNumberOfFiles(t *testing.T) { t.Run("ReloadTestHugeNumberOfFiles", reloadHugeNumberOfFiles) ttt := time.Now() t.Run("ReloadRandomlyChangeFile", randomlyChangeFile) - if time.Since(ttt).Seconds() > 140 { + if time.Since(ttt).Seconds() > 80 { t.Fatal("spend too much time on reloading") } - t.Run("ReloadHTTPLiveAfterReset", reloadHTTPLiveAfterReset) + reloadHTTPLiveAfterReset(t, "22388") wg.Wait() } -func reloadHTTPLiveAfterReset(t *testing.T) { - req, err := http.NewRequest("GET", "http://localhost:22388", nil) +func randomlyChangeFile(t *testing.T) { + // we know, that directory contains 5000 files (0-4999) + // let's try to randomly change it + for i := 0; i < 100; i++ { + // rand sleep + rSleep := rand.Int63n(1000) // nolint:gosec + time.Sleep(time.Millisecond * time.Duration(rSleep)) + rNum := rand.Int63n(int64(hugeNumberOfFiles)) // nolint:gosec + err := ioutil.WriteFile(filepath.Join(testDir, "file_"+strconv.Itoa(int(rNum))+".txt"), []byte("Hello, Gophers!"), 0755) // nolint:gosec + assert.NoError(t, err) + } +} + +func reloadHugeNumberOfFiles(t *testing.T) { + for i := uint(0); i < hugeNumberOfFiles; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".txt")) + } +} + +// Should be events only about creating files with txt ext +func TestReloadFilterFileExt(t *testing.T) { + cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel)) assert.NoError(t, err) - r, err := http.DefaultClient.Do(req) + cfg := &config.Viper{ + Path: "configs/.rr-reload-2.yaml", + Prefix: "rr", + } + + // try to remove, skip error + assert.NoError(t, freeResources(testDir)) + err = os.Mkdir(testDir, 0755) assert.NoError(t, err) - b, err := ioutil.ReadAll(r.Body) + defer func() { + assert.NoError(t, freeResources(testDir)) + }() + + controller := gomock.NewController(t) + mockLogger := mocks.NewMockLogger(controller) + + mockLogger.EXPECT().Debug("http handler response received", "elapsed", gomock.Any(), "remote address", "127.0.0.1").Times(1) + mockLogger.EXPECT().Debug("file was created", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(100) + mockLogger.EXPECT().Debug("file was added to watcher", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(1) + mockLogger.EXPECT().Info("HTTP plugin got restart request. Restarting...").Times(1) + mockLogger.EXPECT().Info("HTTP workers Pool successfully restarted").Times(1) + mockLogger.EXPECT().Info("HTTP listeners successfully re-added").Times(1) + mockLogger.EXPECT().Info("HTTP plugin successfully restarted").Times(1) + + err = cont.RegisterAll( + cfg, + mockLogger, + &server.Plugin{}, + &httpPlugin.Plugin{}, + &reload.Plugin{}, + &resetter.Plugin{}, + ) assert.NoError(t, err) - assert.Equal(t, 200, r.StatusCode) - assert.Equal(t, "hello world", string(b)) + err = cont.Init() + if err != nil { + t.Fatal(err) + } - err = r.Body.Close() + ch, err := cont.Serve() assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + tt := time.NewTimer(time.Second * 40) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-tt.C: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + t.Run("ReloadMakeFiles", reloadMakeFiles) + ttt := time.Now() + t.Run("ReloadFilteredExt", reloadFilteredExt) + if time.Since(ttt).Seconds() > 20 { + t.Fatal("spend too much time on reloading") + } + + reloadHTTPLiveAfterReset(t, "27388") + + wg.Wait() } -func randomlyChangeFile(t *testing.T) { - // we know, that directory contains 5000 files (0-4999) - // let's try to randomly change it - for i := 0; i < 100; i++ { +func reloadMakeFiles(t *testing.T) { + for i := uint(0); i < 100; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".txt")) + } + for i := uint(0); i < 100; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".abc")) + } + for i := uint(0); i < 100; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".def")) + } +} + +func reloadFilteredExt(t *testing.T) { + // change files with abc extension + for i := 0; i < 10; i++ { // rand sleep rSleep := rand.Int63n(1000) // nolint:gosec time.Sleep(time.Millisecond * time.Duration(rSleep)) rNum := rand.Int63n(int64(hugeNumberOfFiles)) // nolint:gosec - err := ioutil.WriteFile(filepath.Join(testDir, "file_"+strconv.Itoa(int(rNum))+".txt"), []byte("Hello, Gophers!"), 0755) // nolint:gosec + err := ioutil.WriteFile(filepath.Join(testDir, "file_"+strconv.Itoa(int(rNum))+".abc"), []byte("Hello, Gophers!"), 0755) // nolint:gosec + assert.NoError(t, err) + } + + // change files with def extension + for i := 0; i < 10; i++ { + // rand sleep + rSleep := rand.Int63n(1000) // nolint:gosec + time.Sleep(time.Millisecond * time.Duration(rSleep)) + rNum := rand.Int63n(int64(hugeNumberOfFiles)) // nolint:gosec + err := ioutil.WriteFile(filepath.Join(testDir, "file_"+strconv.Itoa(int(rNum))+".def"), []byte("Hello, Gophers!"), 0755) // nolint:gosec assert.NoError(t, err) } } -func reloadHugeNumberOfFiles(t *testing.T) { - for i := uint(0); i < hugeNumberOfFiles; i++ { +// Should be events only about creating files with txt ext +func TestReloadCopy3k(t *testing.T) { + cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel)) + assert.NoError(t, err) + + cfg := &config.Viper{ + Path: "configs/.rr-reload-3.yaml", + Prefix: "rr", + } + + // try to remove, skip error + assert.NoError(t, freeResources(testDir)) + assert.NoError(t, freeResources(testCopyToDir)) + assert.NoError(t, freeResources("dir1")) + err = os.Mkdir(testDir, 0755) + assert.NoError(t, err) + err = os.Mkdir(testCopyToDir, 0755) + assert.NoError(t, err) + err = os.Mkdir("dir1", 0755) + assert.NoError(t, err) + + defer func() { + assert.NoError(t, freeResources(testDir)) + assert.NoError(t, freeResources(testCopyToDir)) + assert.NoError(t, freeResources("dir1")) + }() + + controller := gomock.NewController(t) + mockLogger := mocks.NewMockLogger(controller) + // + mockLogger.EXPECT().Debug("http handler response received", "elapsed", gomock.Any(), "remote address", "127.0.0.1").Times(1) + mockLogger.EXPECT().Debug("file was created", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(50) + mockLogger.EXPECT().Debug("file was added to watcher", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(50) + mockLogger.EXPECT().Debug("file added to the list of removed files", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(50) + mockLogger.EXPECT().Debug("file was removed from watcher", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(50) + mockLogger.EXPECT().Debug("file was updated", "path", gomock.Any(), "name", gomock.Any(), "size", gomock.Any()).MinTimes(50) + mockLogger.EXPECT().Info("HTTP plugin got restart request. Restarting...").MinTimes(1) + mockLogger.EXPECT().Info("HTTP workers Pool successfully restarted").MinTimes(1) + mockLogger.EXPECT().Info("HTTP listeners successfully re-added").MinTimes(1) + mockLogger.EXPECT().Info("HTTP plugin successfully restarted").MinTimes(1) + + err = cont.RegisterAll( + cfg, + mockLogger, + &server.Plugin{}, + &httpPlugin.Plugin{}, + &reload.Plugin{}, + &resetter.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + tt := time.NewTimer(time.Second * 160) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-tt.C: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + // Scenario + // 1 + // Create 3k files with txt, abc, def extensions + // Copy files to the unit_tests_copy dir + // 2 + // Delete both dirs, recreate + // Create 3k files with txt, abc, def extensions + // Move files to the unit_tests_copy dir + // 3 + // Recursive + + t.Run("ReloadMake3kFiles", reloadMake3kFiles) + ttt := time.Now() + t.Run("ReloadCopyFiles", reloadCopyFiles) + if time.Since(ttt).Seconds() > 100 { + t.Fatal("spend too much time on copy") + } + + t.Run("ReloadRecursiveDirsSupport", copyFilesRecursive) + t.Run("RandomChangesInRecursiveDirs", randomChangesInRecursiveDirs) + t.Run("RemoveFilesSupport", removeFilesSupport) + t.Run("ReloadMoveSupport", reloadMoveSupport) + + reloadHTTPLiveAfterReset(t, "37388") + + wg.Wait() +} + +func reloadMoveSupport(t *testing.T) { + t.Run("MoveSupportCopy", copyFilesRecursive) + // move some files + for i := 0; i < 50; i++ { + // rand sleep + rSleep := rand.Int63n(1000) // nolint:gosec + time.Sleep(time.Millisecond * time.Duration(rSleep)) + rNum := rand.Int63n(int64(200)) // nolint:gosec + rDir := rand.Int63n(9) // nolint:gosec + rExt := rand.Int63n(3) // nolint:gosec + + ext := []string{ + ".txt", + ".abc", + ".def", + } + + // change files with def extension + dirs := []string{ + "dir1", + "dir1/dir2", + "dir1/dir2/dir3", + "dir1/dir2/dir3/dir4", + "dir1/dir2/dir3/dir4/dir5", + "dir1/dir2/dir3/dir4/dir5/dir6", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10", + } + + // move file + err := os.Rename(filepath.Join(dirs[rDir], "file_"+strconv.Itoa(int(rNum))+ext[rExt]), filepath.Join(dirs[rDir+1], "file_"+strconv.Itoa(int(rNum))+ext[rExt])) + assert.NoError(t, err) + } +} + +func removeFilesSupport(t *testing.T) { + // remove some files + for i := 0; i < 50; i++ { + // rand sleep + rSleep := rand.Int63n(1000) // nolint:gosec + time.Sleep(time.Millisecond * time.Duration(rSleep)) + rNum := rand.Int63n(int64(200)) // nolint:gosec + rDir := rand.Int63n(10) // nolint:gosec + rExt := rand.Int63n(3) // nolint:gosec + + ext := []string{ + ".txt", + ".abc", + ".def", + } + + // change files with def extension + dirs := []string{ + "dir1", + "dir1/dir2", + "dir1/dir2/dir3", + "dir1/dir2/dir3/dir4", + "dir1/dir2/dir3/dir4/dir5", + "dir1/dir2/dir3/dir4/dir5/dir6", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10", + } + err := os.Remove(filepath.Join(dirs[rDir], "file_"+strconv.Itoa(int(rNum))+ext[rExt])) + assert.NoError(t, err) + } +} + +func randomChangesInRecursiveDirs(t *testing.T) { + // change files with def extension + dirs := []string{ + "dir1", + "dir1/dir2", + "dir1/dir2/dir3", + "dir1/dir2/dir3/dir4", + "dir1/dir2/dir3/dir4/dir5", + "dir1/dir2/dir3/dir4/dir5/dir6", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9", + "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10", + } + + ext := []string{ + ".txt", + ".abc", + ".def", + } + + filenames := []string{ + "file_", // should be update + "foo_", // should be created + "bar_", // should be created + } + for i := 0; i < 50; i++ { + // rand sleep + rSleep := rand.Int63n(1000) // nolint:gosec + time.Sleep(time.Millisecond * time.Duration(rSleep)) + rNum := rand.Int63n(int64(200)) // nolint:gosec + rDir := rand.Int63n(10) // nolint:gosec + rExt := rand.Int63n(3) // nolint:gosec + rName := rand.Int63n(3) // nolint:gosec + + err := ioutil.WriteFile(filepath.Join(dirs[rDir], filenames[rName]+strconv.Itoa(int(rNum))+ext[rExt]), []byte("Hello, Gophers!"), 0755) // nolint:gosec + assert.NoError(t, err) + } +} + +func copyFilesRecursive(t *testing.T) { + err := copyDir(testDir, "dir1") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2/dir3") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2/dir3/dir4") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6/dir7") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9") + assert.NoError(t, err) + err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10") + assert.NoError(t, err) +} + +func reloadCopyFiles(t *testing.T) { + err := copyDir(testDir, testCopyToDir) + assert.NoError(t, err) + + assert.NoError(t, freeResources(testDir)) + assert.NoError(t, freeResources(testCopyToDir)) + err = os.Mkdir(testDir, 0755) + assert.NoError(t, err) + err = os.Mkdir(testCopyToDir, 0755) + assert.NoError(t, err) + + // recreate files + for i := uint(0); i < 200; i++ { assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".txt")) } + for i := uint(0); i < 200; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".abc")) + } + for i := uint(0); i < 200; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".def")) + } + + err = copyDir(testDir, testCopyToDir) + assert.NoError(t, err) +} + +func reloadMake3kFiles(t *testing.T) { + for i := uint(0); i < 100; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".txt")) + } + for i := uint(0); i < 100; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".abc")) + } + for i := uint(0); i < 100; i++ { + assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".def")) + } +} + +func TestReloadNoRecursion(t *testing.T) { + cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel)) + assert.NoError(t, err) + + cfg := &config.Viper{ + Path: "configs/.rr-reload-4.yaml", + Prefix: "rr", + } + + // try to remove, skip error + assert.NoError(t, freeResources(testDir)) + assert.NoError(t, freeResources(testCopyToDir)) + assert.NoError(t, freeResources("dir1")) + err = os.Mkdir(testDir, 0755) + assert.NoError(t, err) + + err = os.Mkdir("dir1", 0755) + assert.NoError(t, err) + + err = os.Mkdir(testCopyToDir, 0755) + assert.NoError(t, err) + + defer func() { + assert.NoError(t, freeResources(testDir)) + assert.NoError(t, freeResources(testCopyToDir)) + assert.NoError(t, freeResources("dir1")) + }() + + controller := gomock.NewController(t) + mockLogger := mocks.NewMockLogger(controller) + + // http server should not be restarted. all event from wrong file extensions should be skipped + mockLogger.EXPECT().Debug("http handler response received", "elapsed", gomock.Any(), "remote address", "127.0.0.1").Times(1) + + err = cont.RegisterAll( + cfg, + mockLogger, + &server.Plugin{}, + &httpPlugin.Plugin{}, + &reload.Plugin{}, + &resetter.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + tt := time.NewTimer(time.Second * 20) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-tt.C: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + t.Run("ReloadMakeFiles", reloadMakeFiles) // make files in the testDir + t.Run("ReloadCopyFilesRecursive", reloadCopyFiles) + + reloadHTTPLiveAfterReset(t, "22766") + + wg.Wait() +} + +// ======================================================================== + +func reloadHTTPLiveAfterReset(t *testing.T, port string) { + req, err := http.NewRequest("GET", "http://localhost:"+port, nil) + assert.NoError(t, err) + + r, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + assert.Equal(t, 200, r.StatusCode) + assert.Equal(t, "hello world", string(b)) + + err = r.Body.Close() + assert.NoError(t, err) } func freeResources(path string) error { @@ -270,9 +803,6 @@ func copyDir(src string, dst string) error { if err != nil && !os.IsNotExist(err) { return err } - if err == nil { - return errors.E(errors.Str("destination already exists")) - } err = os.MkdirAll(dst, si.Mode()) if err != nil { diff --git a/plugins/reload/watcher.go b/plugins/reload/watcher.go index cf1b840c..d1e1a4cd 100644 --- a/plugins/reload/watcher.go +++ b/plugins/reload/watcher.go @@ -133,17 +133,6 @@ func ConvertIgnored(ignored []string) (map[string]struct{}, error) { return ign, nil } -// GetAllFiles returns all files initialized for particular company -func (w *Watcher) GetAllFiles(serviceName string) []os.FileInfo { - var ret []os.FileInfo - - for _, v := range w.watcherConfigs[serviceName].Files { - ret = append(ret, v) - } - - return ret -} - // https://en.wikipedia.org/wiki/Inotify // SetMaxFileEvents sets max file notify events for Watcher // In case of file watch errors, this value can be increased system-wide @@ -326,7 +315,7 @@ func (w *Watcher) pollEvents(serviceName string, files map[string]os.FileInfo) { for pth := range w.watcherConfigs[serviceName].Files { if _, found := files[pth]; !found { removes[pth] = w.watcherConfigs[serviceName].Files[pth] - w.log.Debug("file was removed", "path", pth, "name", w.watcherConfigs[serviceName].Files[pth].Name(), "size", w.watcherConfigs[serviceName].Files[pth].Size()) + w.log.Debug("file added to the list of removed files", "path", pth, "name", w.watcherConfigs[serviceName].Files[pth].Name(), "size", w.watcherConfigs[serviceName].Files[pth].Size()) } } @@ -382,7 +371,7 @@ func (w *Watcher) pollEvents(serviceName string, files map[string]os.FileInfo) { // Send all the remaining create and remove events. for pth := range creates { w.watcherConfigs[serviceName].Files[pth] = creates[pth] - w.log.Debug("file was created", "path", pth, "name", creates[pth].Name(), "size", creates[pth].Size()) + w.log.Debug("file was added to watcher", "path", pth, "name", creates[pth].Name(), "size", creates[pth].Size()) w.Event <- Event{ Path: pth, @@ -396,7 +385,7 @@ func (w *Watcher) pollEvents(serviceName string, files map[string]os.FileInfo) { defer wg.Done() for pth := range removes { delete(w.watcherConfigs[serviceName].Files, pth) - w.log.Debug("file was removed", "path", pth, "name", removes[pth].Name(), "size", removes[pth].Size()) + w.log.Debug("file was removed from watcher", "path", pth, "name", removes[pth].Name(), "size", removes[pth].Size()) w.Event <- Event{ Path: pth, |