summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValery Piashchynski <[email protected]>2020-02-17 19:13:15 +0300
committerValery Piashchynski <[email protected]>2020-02-17 19:13:15 +0300
commit50b46c8d3c0e1f13623e2cd7cbb1302ae66ed308 (patch)
tree71f1ac71fe3e5bf024b199136584e601016ac0c7
parent25ef7646e2149b6e35945eb4ce50c19db2ef8e27 (diff)
Add reload service
Start to implement Watcher
-rw-r--r--cmd/rr/main.go2
-rw-r--r--service/reload/config.go41
-rw-r--r--service/reload/config_test.go1
-rw-r--r--service/reload/service.go40
-rw-r--r--service/reload/service_test.go1
-rw-r--r--service/reload/watcher.go235
6 files changed, 320 insertions, 0 deletions
diff --git a/cmd/rr/main.go b/cmd/rr/main.go
index ef393426..46431e49 100644
--- a/cmd/rr/main.go
+++ b/cmd/rr/main.go
@@ -24,6 +24,7 @@ package main
import (
rr "github.com/spiral/roadrunner/cmd/rr/cmd"
+ "github.com/spiral/roadrunner/service/reload"
// services (plugins)
"github.com/spiral/roadrunner/service/env"
@@ -51,6 +52,7 @@ func main() {
rr.Container.Register(limit.ID, &limit.Service{})
rr.Container.Register(health.ID, &health.Service{})
rr.Container.Register(gzip.ID, &gzip.Service{})
+ rr.Container.Register(reload.ID, &reload.Service{})
// you can register additional commands using cmd.CLI
rr.Execute()
diff --git a/service/reload/config.go b/service/reload/config.go
new file mode 100644
index 00000000..338c6eba
--- /dev/null
+++ b/service/reload/config.go
@@ -0,0 +1,41 @@
+package reload
+
+import "github.com/spiral/roadrunner/service"
+
+// Config is a Reload configuration point.
+type Config struct {
+ // Enable or disable Reload extension, default disable.
+ Enabled bool
+
+ // Watch is general pattern of files to watch. It will be applied to every directory in project
+ Watch []string
+
+ // Services is set of services which would be reloaded in case of FS changes
+ Services map[string]ServiceConfig
+}
+
+type ServiceConfig struct {
+ // Watch is per-service specific files to watch
+ Watch []string
+ // Dirs is per-service specific dirs which will be combined with Watch
+ Dirs []string
+ // Ignore is set of files which would not be watched
+ Ignore []string
+}
+
+
+// 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 {
+ //c.Interval = time.Second
+
+ return nil
+}
+
diff --git a/service/reload/config_test.go b/service/reload/config_test.go
new file mode 100644
index 00000000..7cad4a5d
--- /dev/null
+++ b/service/reload/config_test.go
@@ -0,0 +1 @@
+package reload
diff --git a/service/reload/service.go b/service/reload/service.go
new file mode 100644
index 00000000..db10b6f4
--- /dev/null
+++ b/service/reload/service.go
@@ -0,0 +1,40 @@
+package reload
+
+import "github.com/spiral/roadrunner/service"
+
+// ID contains default service name.
+const ID = "reload"
+
+type Service struct {
+ reloadConfig *Config
+}
+
+// Init controller service
+func (s *Service) Init(cfg *Config, c service.Container) (bool, error) {
+ // mount Services to designated services
+ //for id, watcher := range cfg.Controllers(s.throw) {
+ // svc, _ := c.Get(id)
+ // if ctrl, ok := svc.(controllable); ok {
+ // ctrl.Attach(watcher)
+ // }
+ //}
+
+ s.reloadConfig = cfg
+
+ return true, nil
+}
+
+func (s *Service) Serve() error {
+ w, err := NewWatcher(s.reloadConfig, SetMaxFileEvents(100))
+ if err != nil {
+ return err
+ }
+
+ _ = w
+
+ return nil
+}
+
+func (s *Service) Stop() {
+
+} \ No newline at end of file
diff --git a/service/reload/service_test.go b/service/reload/service_test.go
new file mode 100644
index 00000000..7cad4a5d
--- /dev/null
+++ b/service/reload/service_test.go
@@ -0,0 +1 @@
+package reload
diff --git a/service/reload/watcher.go b/service/reload/watcher.go
new file mode 100644
index 00000000..e81ce56f
--- /dev/null
+++ b/service/reload/watcher.go
@@ -0,0 +1,235 @@
+package reload
+
+import (
+ "errors"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sync"
+ "time"
+)
+
+// Config is a Reload configuration point.
+//type Config struct {
+// Enable or disable Reload extension, default disable.
+//Enabled bool
+//
+// Watch is general pattern of files to watch. It will be applied to every directory in project
+//Watch []string
+//
+// Services is set of services which would be reloaded in case of FS changes
+//Services map[string]ServiceConfig
+//}
+//
+//type ServiceConfig struct {
+// Watch is per-service specific files to watch
+//Watch []string
+// Dirs is per-service specific dirs which will be combined with Watch
+//Dirs []string
+// Ignore is set of files which would not be watched
+//Ignore []string
+//}
+
+// An Op is a type that is used to describe what type
+// of event has occurred during the watching process.
+type Op uint32
+
+// Ops
+const (
+ Create Op = iota
+ Write
+ Remove
+ Rename
+ Chmod
+ Move
+)
+
+var ops = map[Op]string{
+ Create: "CREATE",
+ Write: "WRITE",
+ Remove: "REMOVE",
+ Rename: "RENAME",
+ Chmod: "CHMOD",
+ Move: "MOVE",
+}
+
+var (
+ // ErrDurationTooShort occurs when calling the watcher's Start
+ // method with a duration that's less than 1 nanosecond.
+ ErrDurationTooShort = errors.New("error: duration is less than 1ns")
+
+ // ErrWatcherRunning occurs when trying to call the watcher's
+ // Start method and the polling cycle is still already running
+ // from previously calling Start and not yet calling Close.
+ ErrWatcherRunning = errors.New("error: watcher is already running")
+
+ // ErrWatchedFileDeleted is an error that occurs when a file or folder that was
+ // being watched has been deleted.
+ ErrWatchedFileDeleted = errors.New("error: watched file or folder deleted")
+
+ // ErrSkip is less of an error, but more of a way for path hooks to skip a file or
+ // directory.
+ ErrSkip = errors.New("error: skipping file")
+)
+
+// FilterFileHookFunc is a function that is called to filter files during listings.
+// If a file is ok to be listed, nil is returned otherwise ErrSkip is returned.
+type FilterFileHookFunc func(info os.FileInfo, fullPath string) error
+
+// RegexFilterHook is a function that accepts or rejects a file
+// for listing based on whether it's filename or full path matches
+// a regular expression.
+func RegexFilterHook(r *regexp.Regexp, useFullPath bool) FilterFileHookFunc {
+ return func(info os.FileInfo, fullPath string) error {
+ str := info.Name()
+
+ if useFullPath {
+ str = fullPath
+ }
+
+ // Match
+ if r.MatchString(str) {
+ return nil
+ }
+
+ // No match.
+ return ErrSkip
+ }
+}
+
+// An Event describes an event that is received when files or directory
+// 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 {
+ Op
+ Path string
+ OldPath string
+ os.FileInfo
+}
+
+type Watcher struct {
+ Event chan Event
+ errors chan error
+ wg *sync.WaitGroup
+
+ filterHooks []FilterFileHookFunc
+
+ workingDir string
+ maxFileWatchEvents int
+ ops map[Op]struct{} // Op filtering.
+ files map[string]string //files by service, http, grpc, etc..
+ ignored map[string]string //ignored files or directories
+}
+
+// Options is used to set Watcher Options
+type Options func(*Watcher)
+
+func NewWatcher(options ...Options) (*Watcher, error) {
+ dir, err := os.Getwd()
+ if err != nil {
+ return nil, err
+ }
+
+ w := &Watcher{
+ workingDir: dir,
+ }
+
+ for _, option := range options {
+ option(w)
+ }
+
+ // dir --> /home/valery/Projects/opensource/roadrunner
+ return w, nil
+}
+
+// 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
+// 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 {
+ return func(watcher *Watcher) {
+ watcher.maxFileWatchEvents = events
+ }
+
+}
+
+// Add
+// name will be
+func (w *Watcher) Add(name string) error {
+ name, err := filepath.Abs(name)
+ if err != nil {
+
+ }
+
+ // Ignored files
+ // map is to have O(1) when search for file
+ _, ignored := w.ignored[name]
+ if ignored {
+ return nil
+ }
+
+ // small optimization for smallvector
+ fileList := make(map[string]os.FileInfo, 10)
+ err = w.addDirectoryContent(" ", fileList)
+ if err != nil {
+ return err
+ }
+
+
+}
+
+func (w *Watcher) addDirectoryContent(name string, filelist map[string]os.FileInfo) error {
+ fileInfo, err := os.Stat(name)
+ if err != nil {
+ return err
+ }
+
+ filelist[name] = fileInfo
+
+ // if it's not a dir, return
+ if !fileInfo.IsDir() {
+ return nil
+ }
+
+
+
+
+}
+
+func (w *Watcher) search(map[string]os.FileInfo) error {
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+