diff options
author | Valery Piashchynski <[email protected]> | 2020-12-15 17:59:20 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2020-12-15 17:59:20 +0300 |
commit | 089a202aa716f3510402ff8baf47a3b9bfaefcb8 (patch) | |
tree | c0d889ab896804da239dd3e1bd16de0bdd70e379 /plugins | |
parent | 21b51367e27f5a1b166459a115e4655d07a5d832 (diff) |
Update reloader to support new container
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/reload/config.go | 28 | ||||
-rw-r--r-- | plugins/reload/plugin.go (renamed from plugins/reload/service.go) | 45 | ||||
-rw-r--r-- | plugins/reload/service_test.go | 1 | ||||
-rw-r--r-- | plugins/reload/tests/config_test.go (renamed from plugins/reload/config_test.go) | 26 | ||||
-rw-r--r-- | plugins/reload/tests/configs/.rr-reload.yaml | 41 | ||||
-rw-r--r-- | plugins/reload/tests/plugin_test.go | 1 | ||||
-rw-r--r-- | plugins/reload/tests/reload_plugin_test.go | 84 | ||||
-rw-r--r-- | plugins/reload/tests/watcher_test.go (renamed from plugins/reload/watcher_test.go) | 240 | ||||
-rw-r--r-- | plugins/reload/watcher.go | 114 | ||||
-rw-r--r-- | plugins/resetter/plugin.go | 33 | ||||
-rw-r--r-- | plugins/resetter/rpc.go | 4 |
11 files changed, 379 insertions, 238 deletions
diff --git a/plugins/reload/config.go b/plugins/reload/config.go index efc71972..9ca2c0dc 100644 --- a/plugins/reload/config.go +++ b/plugins/reload/config.go @@ -1,10 +1,9 @@ package reload import ( - "errors" - "github.com/spiral/roadrunner" - "github.com/spiral/roadrunner/service" "time" + + "github.com/spiral/errors" ) // Config is a Reload configuration point. @@ -34,38 +33,25 @@ type ServiceConfig struct { // Ignore is set of files which would not be watched Ignore []string - - // service is a link to service to restart - service *roadrunner.Controllable -} - -// Hydrate must populate Config values using given Config source. Must return error if Config is not valid. -func (c *Config) Hydrate(cfg service.Config) error { - if err := cfg.Unmarshal(c); err != nil { - return err - } - - return nil } // InitDefaults sets missing values to their default values. -func (c *Config) InitDefaults() error { +func InitDefaults(c *Config) { c.Interval = time.Second c.Patterns = []string{".php"} - - return nil } // Valid validates the configuration. func (c *Config) Valid() error { + const op = errors.Op("config validation [reload plugin]") if c.Interval < time.Second { - return errors.New("too short interval") + return errors.E(op, errors.Str("too short interval")) } if c.Services == nil { - return errors.New("should add at least 1 service") + return errors.E(op, errors.Str("should add at least 1 service")) } else if len(c.Services) == 0 { - return errors.New("service initialized, however, no config added") + return errors.E(op, errors.Str("service initialized, however, no config added")) } return nil diff --git a/plugins/reload/service.go b/plugins/reload/plugin.go index b2c320f9..bad85b59 100644 --- a/plugins/reload/service.go +++ b/plugins/reload/plugin.go @@ -7,23 +7,27 @@ import ( "github.com/spiral/errors" "github.com/spiral/roadrunner/v2/interfaces/log" + "github.com/spiral/roadrunner/v2/interfaces/resetter" "github.com/spiral/roadrunner/v2/plugins/config" ) // PluginName contains default plugin name. const PluginName string = "reload" -type Service struct { +type Plugin struct { cfg *Config log log.Logger watcher *Watcher services map[string]interface{} + res resetter.Resetter stopc chan struct{} } // Init controller service -func (s *Service) Init(cfg config.Configurer, log log.Logger) error { +func (s *Plugin) Init(cfg config.Configurer, log log.Logger, res resetter.Resetter) error { const op = errors.Op("reload plugin init") + s.cfg = &Config{} + InitDefaults(s.cfg) err := cfg.UnmarshalKey(PluginName, &s.cfg) if err != nil { // disable plugin in case of error @@ -31,34 +35,32 @@ func (s *Service) Init(cfg config.Configurer, log log.Logger) error { } s.log = log + s.res = res s.stopc = make(chan struct{}) s.services = make(map[string]interface{}) var configs []WatcherConfig for serviceName, serviceConfig := range s.cfg.Services { - if s.cfg.Services[serviceName].service == nil { - continue - } ignored, err := ConvertIgnored(serviceConfig.Ignore) if err != nil { return errors.E(op, err) } configs = append(configs, WatcherConfig{ - serviceName: serviceName, - recursive: serviceConfig.Recursive, - directories: serviceConfig.Dirs, - filterHooks: func(filename string, patterns []string) error { + ServiceName: serviceName, + Recursive: serviceConfig.Recursive, + Directories: serviceConfig.Dirs, + FilterHooks: func(filename string, patterns []string) error { for i := 0; i < len(patterns); i++ { if strings.Contains(filename, patterns[i]) { return nil } } - return ErrorSkip + return errors.E(op, errors.Skip, err) }, - files: make(map[string]os.FileInfo), - ignored: ignored, - filePatterns: append(serviceConfig.Patterns, s.cfg.Patterns...), + Files: make(map[string]os.FileInfo), + Ignored: ignored, + FilePatterns: append(serviceConfig.Patterns, s.cfg.Patterns...), }) } @@ -70,7 +72,7 @@ func (s *Service) Init(cfg config.Configurer, log log.Logger) error { return nil } -func (s *Service) Serve() chan error { +func (s *Plugin) Serve() chan error { const op = errors.Op("reload plugin serve") errCh := make(chan error, 1) if s.cfg.Interval < time.Second { @@ -126,13 +128,12 @@ func (s *Service) Serve() chan error { ticker = time.NewTicker(s.cfg.Interval) case <-ticker.C: if len(updated) > 0 { - for k, v := range updated { - sv := *v.service - err := sv.Server().Reset() + for name := range updated { + err := s.res.ResetByName(name) if err != nil { - s.log.Error(err) + errCh <- errors.E(op, err) + return } - s.log.Debugf("[%s] found %v file(s) changes, reloading", k, len(updated)) } // zero map updated = make(map[string]ServiceConfig, 100) @@ -153,7 +154,11 @@ func (s *Service) Serve() chan error { return errCh } -func (s *Service) Stop() { +func (s *Plugin) Stop() { s.watcher.Stop() s.stopc <- struct{}{} } + +func (s *Plugin) Name() string { + return PluginName +} diff --git a/plugins/reload/service_test.go b/plugins/reload/service_test.go deleted file mode 100644 index 7cad4a5d..00000000 --- a/plugins/reload/service_test.go +++ /dev/null @@ -1 +0,0 @@ -package reload diff --git a/plugins/reload/config_test.go b/plugins/reload/tests/config_test.go index 600975d3..5bb64b6b 100644 --- a/plugins/reload/config_test.go +++ b/plugins/reload/tests/config_test.go @@ -1,22 +1,23 @@ -package reload +package tests import ( - "github.com/stretchr/testify/assert" "testing" "time" + + "github.com/spiral/roadrunner/v2/plugins/reload" + "github.com/stretchr/testify/assert" ) func Test_Config_Valid(t *testing.T) { - services := make(map[string]ServiceConfig) - services["test"] = ServiceConfig{ + services := make(map[string]reload.ServiceConfig) + services["test"] = reload.ServiceConfig{ Recursive: false, Patterns: nil, Dirs: nil, Ignore: nil, - service: nil, } - cfg := &Config{ + cfg := &reload.Config{ Interval: time.Second, Patterns: nil, Services: services, @@ -25,8 +26,8 @@ func Test_Config_Valid(t *testing.T) { } func Test_Fake_ServiceConfig(t *testing.T) { - services := make(map[string]ServiceConfig) - cfg := &Config{ + services := make(map[string]reload.ServiceConfig) + cfg := &reload.Config{ Interval: time.Microsecond, Patterns: nil, Services: services, @@ -35,17 +36,16 @@ func Test_Fake_ServiceConfig(t *testing.T) { } func Test_Interval(t *testing.T) { - services := make(map[string]ServiceConfig) - services["test"] = ServiceConfig{ + services := make(map[string]reload.ServiceConfig) + services["test"] = reload.ServiceConfig{ Enabled: false, Recursive: false, Patterns: nil, Dirs: nil, Ignore: nil, - service: nil, } - cfg := &Config{ + cfg := &reload.Config{ Interval: time.Millisecond, // should crash here Patterns: nil, Services: services, @@ -54,7 +54,7 @@ func Test_Interval(t *testing.T) { } func Test_NoServiceConfig(t *testing.T) { - cfg := &Config{ + cfg := &reload.Config{ Interval: time.Second, Patterns: nil, Services: nil, diff --git a/plugins/reload/tests/configs/.rr-reload.yaml b/plugins/reload/tests/configs/.rr-reload.yaml index e69de29b..e1149cd2 100644 --- a/plugins/reload/tests/configs/.rr-reload.yaml +++ b/plugins/reload/tests/configs/.rr-reload.yaml @@ -0,0 +1,41 @@ +server: + command: php ../../../tests/http/client.php echo pipes + user: '' + group: '' + env: + RR_HTTP: 'true' + relay: pipes + relayTimeout: 20s +http: + debug: true + address: '127.0.0.1:15395' + 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: 1s + patterns: + - .go + services: + http: + dirs: + - '' + recursive: true diff --git a/plugins/reload/tests/plugin_test.go b/plugins/reload/tests/plugin_test.go new file mode 100644 index 00000000..ca8701d2 --- /dev/null +++ b/plugins/reload/tests/plugin_test.go @@ -0,0 +1 @@ +package tests diff --git a/plugins/reload/tests/reload_plugin_test.go b/plugins/reload/tests/reload_plugin_test.go new file mode 100644 index 00000000..376df9c8 --- /dev/null +++ b/plugins/reload/tests/reload_plugin_test.go @@ -0,0 +1,84 @@ +package tests + +import ( + "os" + "os/signal" + "sync" + "syscall" + "testing" + "time" + + "github.com/spiral/endure" + "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" + "github.com/stretchr/testify/assert" +) + +func TestReloadInit(t *testing.T) { + cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel)) + assert.NoError(t, err) + + cfg := &config.Viper{ + Path: "configs/.rr-reload.yaml", + Prefix: "rr", + } + + err = cont.RegisterAll( + cfg, + &logger.ZapLogger{}, + &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 * 5) + + 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 + } + } + }() + + wg.Wait() +} diff --git a/plugins/reload/watcher_test.go b/plugins/reload/tests/watcher_test.go index 9683d2de..6a4521a4 100644 --- a/plugins/reload/watcher_test.go +++ b/plugins/reload/tests/watcher_test.go @@ -1,4 +1,4 @@ -package reload +package tests import ( "fmt" @@ -10,6 +10,9 @@ import ( "strings" "testing" "time" + + "github.com/spiral/errors" + "github.com/spiral/roadrunner/v2/plugins/reload" ) var testServiceName = "test" @@ -27,23 +30,23 @@ func Test_Correct_Watcher_Init(t *testing.T) { if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file.txt"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file.txt"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - wc := WatcherConfig{ - serviceName: testServiceName, - recursive: false, - directories: []string{tempDir}, - filterHooks: nil, - files: make(map[string]os.FileInfo), - ignored: nil, - filePatterns: nil, + wc := reload.WatcherConfig{ + ServiceName: testServiceName, + Recursive: false, + Directories: []string{tempDir}, + FilterHooks: nil, + Files: make(map[string]os.FileInfo), + Ignored: nil, + FilePatterns: nil, } - w, err := NewWatcher([]WatcherConfig{wc}) + w, err := reload.NewWatcher([]reload.WatcherConfig{wc}) if err != nil { t.Fatal(err) } @@ -72,35 +75,35 @@ func Test_Get_FileEvent(t *testing.T) { if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file1.txt"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file1.txt"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file2.txt"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file2.txt"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file3.txt"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file3.txt"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - wc := WatcherConfig{ - serviceName: testServiceName, - recursive: false, - directories: []string{tempDir}, - filterHooks: nil, - files: make(map[string]os.FileInfo), - ignored: nil, - filePatterns: []string{"aaa", "txt"}, + wc := reload.WatcherConfig{ + ServiceName: testServiceName, + Recursive: false, + Directories: []string{tempDir}, + FilterHooks: nil, + Files: make(map[string]os.FileInfo), + Ignored: nil, + FilePatterns: []string{"aaa", "txt"}, } - w, err := NewWatcher([]WatcherConfig{wc}) + w, err := reload.NewWatcher([]reload.WatcherConfig{wc}) if err != nil { t.Fatal(err) } @@ -110,12 +113,12 @@ func Test_Get_FileEvent(t *testing.T) { t.Fatal("incorrect directories len") } - go limitTime(time.Second * 10, t.Name(), c) + go limitTime(time.Second*10, t.Name(), c) go func() { go func() { time.Sleep(time.Second) - err2 := ioutil.WriteFile(filepath.Join(tempDir, "file2.txt"), + err2 := ioutil.WriteFile(filepath.Join(tempDir, "file2.txt"), //nolint:gosec []byte{1, 1, 1}, 0755) if err2 != nil { panic(err2) @@ -125,7 +128,7 @@ func Test_Get_FileEvent(t *testing.T) { go func() { for e := range w.Event { - if e.path != "file2.txt" { + if e.Path != "file2.txt" { panic("didn't handle event when write file2") } w.Stop() @@ -158,41 +161,41 @@ func Test_FileExtensionFilter(t *testing.T) { if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file1.aaa"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file1.aaa"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file2.bbb"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file2.bbb"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file3.txt"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file3.txt"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - wc := WatcherConfig{ - serviceName: testServiceName, - recursive: false, - directories: []string{tempDir}, - filterHooks: func(filename string, patterns []string) error { + wc := reload.WatcherConfig{ + ServiceName: testServiceName, + Recursive: false, + Directories: []string{tempDir}, + FilterHooks: func(filename string, patterns []string) error { for i := 0; i < len(patterns); i++ { if strings.Contains(filename, patterns[i]) { return nil } } - return ErrorSkip + return errors.E(errors.Skip) }, - files: make(map[string]os.FileInfo), - ignored: nil, - filePatterns: []string{"aaa", "bbb"}, + Files: make(map[string]os.FileInfo), + Ignored: nil, + FilePatterns: []string{"aaa", "bbb"}, } - w, err := NewWatcher([]WatcherConfig{wc}) + w, err := reload.NewWatcher([]reload.WatcherConfig{wc}) if err != nil { t.Fatal(err) } @@ -203,11 +206,11 @@ func Test_FileExtensionFilter(t *testing.T) { t.Fatalf("incorrect directories len, len is: %d", dirLen) } - go limitTime(time.Second * 5, t.Name(), c) + go limitTime(time.Second*5, t.Name(), c) go func() { go func() { - err2 := ioutil.WriteFile(filepath.Join(tempDir, "file3.txt"), + err2 := ioutil.WriteFile(filepath.Join(tempDir, "file3.txt"), //nolint:gosec []byte{1, 1, 1}, 0755) if err2 != nil { panic(err2) @@ -218,7 +221,7 @@ func Test_FileExtensionFilter(t *testing.T) { go func() { for e := range w.Event { - fmt.Println(e.info.Name()) + fmt.Println(e.Info.Name()) panic("handled event from filtered file") } }() @@ -251,47 +254,47 @@ func Test_Recursive_Support(t *testing.T) { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file1.aaa"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file1.aaa"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file2.bbb"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file2.bbb"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(nestedDir, "file3.txt"), + err = ioutil.WriteFile(filepath.Join(nestedDir, "file3.txt"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), + err = ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - wc := WatcherConfig{ - serviceName: testServiceName, - recursive: true, - directories: []string{tempDir}, - filterHooks: func(filename string, patterns []string) error { + wc := reload.WatcherConfig{ + ServiceName: testServiceName, + Recursive: true, + Directories: []string{tempDir}, + FilterHooks: func(filename string, patterns []string) error { for i := 0; i < len(patterns); i++ { if strings.Contains(filename, patterns[i]) { return nil } } - return ErrorSkip + return errors.E(errors.Skip) }, - files: make(map[string]os.FileInfo), - ignored: nil, - filePatterns: []string{"aaa", "bbb"}, + Files: make(map[string]os.FileInfo), + Ignored: nil, + FilePatterns: []string{"aaa", "bbb"}, } - w, err := NewWatcher([]WatcherConfig{wc}) + w, err := reload.NewWatcher([]reload.WatcherConfig{wc}) if err != nil { t.Fatal(err) } @@ -306,14 +309,14 @@ func Test_Recursive_Support(t *testing.T) { // time sleep is used here because StartPolling is blocking operation time.Sleep(time.Second * 5) // change file in nested directory - err = ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), + err = ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), //nolint:gosec []byte{1, 1, 1}, 0755) if err != nil { panic(err) } go func() { for e := range w.Event { - if e.info.Name() != "file4.aaa" { + if e.Info.Name() != "file4.aaa" { panic("wrong handled event from watcher in nested dir") } w.Stop() @@ -331,24 +334,24 @@ func Test_Wrong_Dir(t *testing.T) { // no such file or directory wrongDir := "askdjfhaksdlfksdf" - wc := WatcherConfig{ - serviceName: testServiceName, - recursive: true, - directories: []string{wrongDir}, - filterHooks: func(filename string, patterns []string) error { + wc := reload.WatcherConfig{ + ServiceName: testServiceName, + Recursive: true, + Directories: []string{wrongDir}, + FilterHooks: func(filename string, patterns []string) error { for i := 0; i < len(patterns); i++ { if strings.Contains(filename, patterns[i]) { return nil } } - return ErrorSkip + return errors.E(errors.Skip) }, - files: make(map[string]os.FileInfo), - ignored: nil, - filePatterns: []string{"aaa", "bbb"}, + Files: make(map[string]os.FileInfo), + Ignored: nil, + FilePatterns: []string{"aaa", "bbb"}, } - _, err := NewWatcher([]WatcherConfig{wc}) + _, err := reload.NewWatcher([]reload.WatcherConfig{wc}) if err == nil { t.Fatal(err) } @@ -373,51 +376,51 @@ func Test_Filter_Directory(t *testing.T) { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file1.aaa"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file1.aaa"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file2.bbb"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file2.bbb"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(nestedDir, "file3.txt"), + err = ioutil.WriteFile(filepath.Join(nestedDir, "file3.txt"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), + err = ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - ignored, err := ConvertIgnored([]string{nestedDir}) + ignored, err := reload.ConvertIgnored([]string{nestedDir}) if err != nil { t.Fatal(err) } - wc := WatcherConfig{ - serviceName: testServiceName, - recursive: true, - directories: []string{tempDir}, - filterHooks: func(filename string, patterns []string) error { + wc := reload.WatcherConfig{ + ServiceName: testServiceName, + Recursive: true, + Directories: []string{tempDir}, + FilterHooks: func(filename string, patterns []string) error { for i := 0; i < len(patterns); i++ { if strings.Contains(filename, patterns[i]) { return nil } } - return ErrorSkip + return errors.E(errors.Skip) }, - files: make(map[string]os.FileInfo), - ignored: ignored, - filePatterns: []string{"aaa", "bbb", "txt"}, + Files: make(map[string]os.FileInfo), + Ignored: ignored, + FilePatterns: []string{"aaa", "bbb", "txt"}, } - w, err := NewWatcher([]WatcherConfig{wc}) + w, err := reload.NewWatcher([]reload.WatcherConfig{wc}) if err != nil { t.Fatal(err) } @@ -430,7 +433,7 @@ func Test_Filter_Directory(t *testing.T) { go func() { go func() { - err2 := ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), + err2 := ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), //nolint:gosec []byte{1, 1, 1}, 0755) if err2 != nil { panic(err2) @@ -439,7 +442,7 @@ func Test_Filter_Directory(t *testing.T) { go func() { for e := range w.Event { - fmt.Println("file: " + e.info.Name()) + fmt.Println("file: " + e.Info.Name()) panic("handled event from watcher in nested dir") } }() @@ -447,7 +450,6 @@ func Test_Filter_Directory(t *testing.T) { // time sleep is used here because StartPolling is blocking operation time.Sleep(time.Second * 5) w.Stop() - }() err = w.StartPolling(time.Second) @@ -475,52 +477,52 @@ func Test_Copy_Directory(t *testing.T) { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file1.aaa"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file1.aaa"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "file2.bbb"), + err = ioutil.WriteFile(filepath.Join(tempDir, "file2.bbb"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(nestedDir, "file3.txt"), + err = ioutil.WriteFile(filepath.Join(nestedDir, "file3.txt"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), + err = ioutil.WriteFile(filepath.Join(nestedDir, "file4.aaa"), //nolint:gosec []byte{}, 0755) if err != nil { t.Fatal(err) } - ignored, err := ConvertIgnored([]string{nestedDir}) + ignored, err := reload.ConvertIgnored([]string{nestedDir}) if err != nil { t.Fatal(err) } - wc := WatcherConfig{ - serviceName: testServiceName, - recursive: true, - directories: []string{tempDir}, - filterHooks: func(filename string, patterns []string) error { + wc := reload.WatcherConfig{ + ServiceName: testServiceName, + Recursive: true, + Directories: []string{tempDir}, + FilterHooks: func(filename string, patterns []string) error { for i := 0; i < len(patterns); i++ { if strings.Contains(filename, patterns[i]) { return nil } } - return ErrorSkip + return errors.E(errors.Skip) }, - files: make(map[string]os.FileInfo), - ignored: ignored, - filePatterns: []string{"aaa", "bbb", "txt"}, + Files: make(map[string]os.FileInfo), + Ignored: ignored, + FilePatterns: []string{"aaa", "bbb", "txt"}, } - w, err := NewWatcher([]WatcherConfig{wc}) + w, err := reload.NewWatcher([]reload.WatcherConfig{wc}) if err != nil { t.Fatal(err) } @@ -574,16 +576,18 @@ func limitTime(d time.Duration, name string, free chan struct{}) { }() } -func copyFile(src, dst string) (err error) { +func copyFile(src, dst string) error { in, err := os.Open(src) if err != nil { - return + return err } - defer in.Close() + defer func() { + _ = in.Close() + }() out, err := os.Create(dst) if err != nil { - return + return err } defer func() { if e := out.Close(); e != nil { @@ -593,27 +597,26 @@ func copyFile(src, dst string) (err error) { _, err = io.Copy(out, in) if err != nil { - return + return err } err = out.Sync() if err != nil { - return + return err } si, err := os.Stat(src) if err != nil { - return + return err } err = os.Chmod(dst, si.Mode()) if err != nil { - return + return err } - - return + return nil } -func copyDir(src string, dst string) (err error) { +func copyDir(src string, dst string) error { src = filepath.Clean(src) dst = filepath.Clean(dst) @@ -627,7 +630,7 @@ func copyDir(src string, dst string) (err error) { _, err = os.Stat(dst) if err != nil && !os.IsNotExist(err) { - return + return err } if err == nil { return fmt.Errorf("destination already exists") @@ -635,12 +638,12 @@ func copyDir(src string, dst string) (err error) { err = os.MkdirAll(dst, si.Mode()) if err != nil { - return + return err } entries, err := ioutil.ReadDir(src) if err != nil { - return + return err } for _, entry := range entries { @@ -650,7 +653,7 @@ func copyDir(src string, dst string) (err error) { if entry.IsDir() { err = copyDir(srcPath, dstPath) if err != nil { - return + return err } } else { // Skip symlinks. @@ -660,12 +663,11 @@ func copyDir(src string, dst string) (err error) { err = copyFile(srcPath, dstPath) if err != nil { - return + return err } } } - - return + return nil } func freeResources(path string) error { diff --git a/plugins/reload/watcher.go b/plugins/reload/watcher.go index 721495c3..ba0b8e82 100644 --- a/plugins/reload/watcher.go +++ b/plugins/reload/watcher.go @@ -1,16 +1,14 @@ package reload import ( - "errors" "io/ioutil" "os" "path/filepath" "sync" "time" -) -var ErrorSkip = errors.New("file is skipped") -var NoWalkerConfig = errors.New("should add at least one walker config, when reload is set to true") + "github.com/spiral/errors" +) // SimpleHook is used to filter by simple criteria, CONTAINS type SimpleHook func(filename string, pattern []string) error @@ -19,33 +17,33 @@ type SimpleHook func(filename string, pattern []string) error // changes occur. It includes the os.FileInfo of the changed file or // directory and the type of event that's occurred and the full path of the file. type Event struct { - path string - info os.FileInfo + Path string + Info os.FileInfo service string // type of service, http, grpc, etc... } type WatcherConfig struct { // service name - serviceName string + ServiceName string - // recursive or just add by singe directory - recursive bool + // Recursive or just add by singe directory + Recursive bool - // directories used per-service - directories []string + // Directories used per-service + Directories []string // simple hook, just CONTAINS - filterHooks func(filename string, pattern []string) error + FilterHooks func(filename string, pattern []string) error - // path to file with files - files map[string]os.FileInfo + // path to file with Files + Files map[string]os.FileInfo - // ignored directories, used map for O(1) amortized get - ignored map[string]struct{} + // Ignored Directories, used map for O(1) amortized get + Ignored map[string]struct{} - // filePatterns to ignore - filePatterns []string + // FilePatterns to ignore + FilePatterns []string } type Watcher struct { @@ -53,7 +51,7 @@ type Watcher struct { Event chan Event close chan struct{} - //============================= + // ============================= mu *sync.Mutex // indicates is walker started or not @@ -81,7 +79,7 @@ func NewWatcher(configs []WatcherConfig, options ...Options) (*Watcher, error) { // add watcherConfigs by service names for _, v := range configs { - w.watcherConfigs[v.serviceName] = v + w.watcherConfigs[v.ServiceName] = v } // apply options @@ -105,7 +103,7 @@ func (w *Watcher) initFs() error { } // workaround. in golang you can't assign to map in struct field tmp := w.watcherConfigs[srvName] - tmp.files = fileList + tmp.Files = fileList w.watcherConfigs[srvName] = tmp } return nil @@ -127,14 +125,13 @@ 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 { + for _, v := range w.watcherConfigs[serviceName].Files { ret = append(ret, v) } @@ -146,12 +143,12 @@ func (w *Watcher) GetAllFiles(serviceName string) []os.FileInfo { // In case of file watch errors, this value can be increased system-wide // For linux: set --> fs.inotify.max_user_watches = 600000 (under /etc/<choose_name_here>.conf) // Add apply: sudo sysctl -p --system -//func SetMaxFileEvents(events int) Options { +// func SetMaxFileEvents(events int) Options { // return func(watcher *Watcher) { // watcher.maxFileWatchEvents = events // } // -//} +// } // pass map from outside func (w *Watcher) retrieveFilesSingle(serviceName, path string) (map[string]os.FileInfo, error) { @@ -178,14 +175,14 @@ func (w *Watcher) retrieveFilesSingle(serviceName, path string) (map[string]os.F outer: for i := 0; i < len(fileInfoList); i++ { // if file in ignored --> continue - if _, ignored := w.watcherConfigs[serviceName].ignored[path]; ignored { + if _, ignored := w.watcherConfigs[serviceName].Ignored[path]; ignored { continue } // if filename does not contain pattern --> ignore that file - if w.watcherConfigs[serviceName].filePatterns != nil && w.watcherConfigs[serviceName].filterHooks != nil { - err = w.watcherConfigs[serviceName].filterHooks(fileInfoList[i].Name(), w.watcherConfigs[serviceName].filePatterns) - if err == ErrorSkip { + if w.watcherConfigs[serviceName].FilePatterns != nil && w.watcherConfigs[serviceName].FilterHooks != nil { + err = w.watcherConfigs[serviceName].FilterHooks(fileInfoList[i].Name(), w.watcherConfigs[serviceName].FilePatterns) + if errors.Is(errors.Skip, err) { continue outer } } @@ -198,9 +195,10 @@ outer: func (w *Watcher) StartPolling(duration time.Duration) error { w.mu.Lock() + const op = errors.Op("start polling") if w.started { w.mu.Unlock() - return errors.New("already started") + return errors.E(op, errors.Str("already started")) } w.started = true @@ -226,12 +224,11 @@ func (w *Watcher) waitEvent(d time.Duration) error { for serviceName, config := range w.watcherConfigs { go func(sn string, c WatcherConfig) { fileList, _ := w.retrieveFileList(sn, c) - w.pollEvents(c.serviceName, fileList) + w.pollEvents(c.ServiceName, fileList) }(serviceName, config) } } } - } // retrieveFileList get file list for service @@ -239,9 +236,9 @@ func (w *Watcher) retrieveFileList(serviceName string, config WatcherConfig) (ma w.mu.Lock() defer w.mu.Unlock() fileList := make(map[string]os.FileInfo) - if config.recursive { + if config.Recursive { // walk through directories recursively - for _, dir := range config.directories { + for _, dir := range config.Directories { // full path is workdir/relative_path fullPath, err := filepath.Abs(dir) if err != nil { @@ -259,7 +256,7 @@ func (w *Watcher) retrieveFileList(serviceName string, config WatcherConfig) (ma return fileList, nil } - for _, dir := range config.directories { + for _, dir := range config.Directories { // full path is workdir/relative_path fullPath, err := filepath.Abs(dir) if err != nil { @@ -290,7 +287,7 @@ func (w *Watcher) retrieveFilesRecursive(serviceName, root string) (map[string]o // If path is ignored and it's a directory, skip the directory. If it's // ignored and it's a single file, skip the file. - _, ignored := w.watcherConfigs[serviceName].ignored[path] + _, ignored := w.watcherConfigs[serviceName].Ignored[path] if ignored { if info.IsDir() { // if it's dir, ignore whole @@ -300,8 +297,8 @@ func (w *Watcher) retrieveFilesRecursive(serviceName, root string) (map[string]o } // if filename does not contain pattern --> ignore that file - err = w.watcherConfigs[serviceName].filterHooks(info.Name(), w.watcherConfigs[serviceName].filePatterns) - if err == ErrorSkip { + err = w.watcherConfigs[serviceName].FilterHooks(info.Name(), w.watcherConfigs[serviceName].FilePatterns) + if errors.Is(errors.Skip, err) { return nil } @@ -320,7 +317,7 @@ func (w *Watcher) pollEvents(serviceName string, files map[string]os.FileInfo) { removes := make(map[string]os.FileInfo) // Check for removed files. - for pth, info := range w.watcherConfigs[serviceName].files { + for pth, info := range w.watcherConfigs[serviceName].Files { if _, found := files[pth]; !found { removes[pth] = info } @@ -331,65 +328,64 @@ func (w *Watcher) pollEvents(serviceName string, files map[string]os.FileInfo) { if info.IsDir() { continue } - oldInfo, found := w.watcherConfigs[serviceName].files[pth] + oldInfo, found := w.watcherConfigs[serviceName].Files[pth] if !found { // A file was created. creates[pth] = info continue } if oldInfo.ModTime() != info.ModTime() { - w.watcherConfigs[serviceName].files[pth] = info + w.watcherConfigs[serviceName].Files[pth] = info w.Event <- Event{ - path: pth, - info: info, + Path: pth, + Info: info, service: serviceName, } } if oldInfo.Mode() != info.Mode() { - w.watcherConfigs[serviceName].files[pth] = info + w.watcherConfigs[serviceName].Files[pth] = info w.Event <- Event{ - path: pth, - info: info, + Path: pth, + Info: info, service: serviceName, } } } - //Check for renames and moves. + // Check for renames and moves. for path1, info1 := range removes { for path2, info2 := range creates { if sameFile(info1, info2) { e := Event{ - path: path2, - info: info2, + Path: path2, + Info: info2, service: serviceName, } // remove initial path - delete(w.watcherConfigs[serviceName].files, path1) + delete(w.watcherConfigs[serviceName].Files, path1) // update with new - w.watcherConfigs[serviceName].files[path2] = info2 - + w.watcherConfigs[serviceName].Files[path2] = info2 w.Event <- e } } } - //Send all the remaining create and remove events. + // Send all the remaining create and remove events. for pth, info := range creates { - w.watcherConfigs[serviceName].files[pth] = info + w.watcherConfigs[serviceName].Files[pth] = info w.Event <- Event{ - path: pth, - info: info, + Path: pth, + Info: info, service: serviceName, } } for pth, info := range removes { - delete(w.watcherConfigs[serviceName].files, pth) + delete(w.watcherConfigs[serviceName].Files, pth) w.Event <- Event{ - path: pth, - info: info, + Path: pth, + Info: info, service: serviceName, } } diff --git a/plugins/resetter/plugin.go b/plugins/resetter/plugin.go index 99e02aef..8dc5cc31 100644 --- a/plugins/resetter/plugin.go +++ b/plugins/resetter/plugin.go @@ -10,12 +10,39 @@ import ( const PluginName = "resetter" type Plugin struct { - registry map[string]resetter.Resetter + registry map[string]resetter.Resettable log log.Logger } +func (p *Plugin) ResetAll() error { + const op = errors.Op("reset all") + for name := range p.registry { + err := p.registry[name].Reset() + if err != nil { + return errors.E(op, err) + } + } + return nil +} + +func (p *Plugin) ResetByName(plugin string) error { + const op = errors.Op("reset by name") + if plugin, ok := p.registry[plugin]; ok { + return plugin.Reset() + } + return errors.E(op, errors.Errorf("can't find plugin: %s", plugin)) +} + +func (p *Plugin) GetAll() []string { + all := make([]string, 0, len(p.registry)) + for name := range p.registry { + all = append(all, name) + } + return all +} + func (p *Plugin) Init(log log.Logger) error { - p.registry = make(map[string]resetter.Resetter) + p.registry = make(map[string]resetter.Resettable) p.log = log return nil } @@ -31,7 +58,7 @@ func (p *Plugin) Reset(name string) error { } // RegisterTarget resettable service. -func (p *Plugin) RegisterTarget(name endure.Named, r resetter.Resetter) error { +func (p *Plugin) RegisterTarget(name endure.Named, r resetter.Resettable) error { p.registry[name.Name()] = r return nil } diff --git a/plugins/resetter/rpc.go b/plugins/resetter/rpc.go index ecc51bb3..344c6681 100644 --- a/plugins/resetter/rpc.go +++ b/plugins/resetter/rpc.go @@ -7,7 +7,7 @@ type rpc struct { log log.Logger } -// List all resettable services. +// List all resettable plugins. func (rpc *rpc) List(_ bool, list *[]string) error { rpc.log.Debug("started List method") *list = make([]string, 0) @@ -21,7 +21,7 @@ func (rpc *rpc) List(_ bool, list *[]string) error { return nil } -// Reset named service. +// Reset named plugin. func (rpc *rpc) Reset(service string, done *bool) error { rpc.log.Debug("started Reset method for the service", "service", service) defer rpc.log.Debug("finished Reset method for the service", "service", service) |