diff options
author | Wolfy-J <[email protected]> | 2018-07-26 16:32:27 +0300 |
---|---|---|
committer | Wolfy-J <[email protected]> | 2018-07-26 16:32:27 +0300 |
commit | bcef5b36bb50b2fecd4db4ca8e01640347300bea (patch) | |
tree | b6db6954063dd3288a80f24aa714e8799664d909 | |
parent | 0f8e2bab6888f1b27ed2bd1b91ac6b2677f03450 (diff) |
- added support for custom env provider
- new config section "env" to share env variables with php process
- container can resolve interfaces now
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | cmd/rr/.rr.yaml | 4 | ||||
-rw-r--r-- | cmd/rr/main.go | 8 | ||||
-rw-r--r-- | server_config.go | 14 | ||||
-rw-r--r-- | service/container.go | 57 | ||||
-rw-r--r-- | service/env/config.go | 17 | ||||
-rw-r--r-- | service/env/provider.go | 8 | ||||
-rw-r--r-- | service/env/service.go | 21 | ||||
-rw-r--r-- | service/http/attributes/attributes.go | 8 | ||||
-rw-r--r-- | service/http/service.go | 27 |
12 files changed, 136 insertions, 35 deletions
diff --git a/.travis.yml b/.travis.yml index 4f7741f6..8fd4f66d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ install: script: - go test -race -v -coverprofile=lib.txt -covermode=atomic - go test ./service -race -v -coverprofile=service.txt -covermode=atomic + - go test ./service/env -race -v -coverprofile=env.txt -covermode=atomic - go test ./service/rpc -race -v -coverprofile=rpc.txt -covermode=atomic - go test ./service/http -race -v -coverprofile=http.txt -covermode=atomic - go test ./service/static -race -v -coverprofile=static.txt -covermode=atomic @@ -32,6 +33,7 @@ script: after_success: - bash <(curl -s https://codecov.io/bash) -f lib.txt - bash <(curl -s https://codecov.io/bash) -f service.txt + - bash <(curl -s https://codecov.io/bash) -f env.txt - bash <(curl -s https://codecov.io/bash) -f rpc.txt - bash <(curl -s https://codecov.io/bash) -f http.txt - bash <(curl -s https://codecov.io/bash) -f static.txt
\ No newline at end of file @@ -11,6 +11,7 @@ uninstall: test: go test -v -race -cover go test -v -race -cover ./service + go test -v -race -cover ./service/env go test -v -race -cover ./service/rpc go test -v -race -cover ./service/http go test -v -race -cover ./service/static @@ -55,6 +55,10 @@ Using RoadRunner: In order to use RoadRunner you only have to place a `.rr.yaml` config file in the root of your PHP project: ```yaml +# defines environment variables for all underlying php processes +env: + key: value + # rpc bus allows php application and external clients to talk to rr services. rpc: # enable rpc server diff --git a/cmd/rr/.rr.yaml b/cmd/rr/.rr.yaml index 5ea6b345..401a42da 100644 --- a/cmd/rr/.rr.yaml +++ b/cmd/rr/.rr.yaml @@ -1,3 +1,7 @@ +# defines environment variables for all underlying php processes +env: + key: value + # rpc bus allows php application and external clients to talk to rr services. rpc: # enable rpc server diff --git a/cmd/rr/main.go b/cmd/rr/main.go index 2a16b195..2abb6321 100644 --- a/cmd/rr/main.go +++ b/cmd/rr/main.go @@ -23,8 +23,6 @@ package main import ( - "github.com/sirupsen/logrus" - rr "github.com/spiral/roadrunner/cmd/rr/cmd" // services (plugins) @@ -33,15 +31,19 @@ import ( "github.com/spiral/roadrunner/service/static" // additional command handlers + "github.com/sirupsen/logrus" _ "github.com/spiral/roadrunner/cmd/rr/http" + "github.com/spiral/roadrunner/service/env" ) func main() { + rr.Logger.Formatter = &logrus.TextFormatter{ForceColors: true} + + rr.Container.Register(env.ID, &env.Service{}) rr.Container.Register(rpc.ID, &rpc.Service{}) rr.Container.Register(http.ID, &http.Service{}) rr.Container.Register(static.ID, &static.Service{}) // you can register additional commands using cmd.CLI - rr.Logger.Formatter = &logrus.TextFormatter{ForceColors: true} rr.Execute() } diff --git a/server_config.go b/server_config.go index b927c8c6..df9b4cee 100644 --- a/server_config.go +++ b/server_config.go @@ -2,6 +2,7 @@ package roadrunner import ( "errors" + "fmt" "net" "os/exec" "strings" @@ -26,6 +27,9 @@ type ServerConfig struct { // Pool defines worker pool configuration, number of workers, timeouts and etc. This config section might change // while server is running. Pool *Config + + // Env defines set of values to be passed to the command context. + env []string } // Differs returns true if configuration has changed but ignores pool or cmd changes. @@ -33,11 +37,19 @@ func (cfg *ServerConfig) Differs(new *ServerConfig) bool { return cfg.Relay != new.Relay || cfg.RelayTimeout != new.RelayTimeout } +// SetEnv sets new environment variable. Value is automatically uppercase-d. +func (cfg *ServerConfig) SetEnv(k, v string) { + cfg.env = append(cfg.env, fmt.Sprintf("%s=%s", strings.ToUpper(k), v)) +} + // makeCommands returns new command provider based on configured options. func (cfg *ServerConfig) makeCommand() func() *exec.Cmd { var cmd = strings.Split(cfg.Command, " ") return func() *exec.Cmd { - return exec.Command(cmd[0], cmd[1:]...) + cmd := exec.Command(cmd[0], cmd[1:]...) + cmd.Env = cfg.env + + return cmd } } diff --git a/service/container.go b/service/container.go index 436d2e5f..3450a18c 100644 --- a/service/container.go +++ b/service/container.go @@ -8,7 +8,7 @@ import ( "sync" ) -var noConfig = fmt.Errorf("no config has been provided") +var errNoConfig = fmt.Errorf("no config has been provided") // InitMethod contains name of the method to be automatically invoked while service initialization. Must return // (bool, error). Container can be requested as well. Config can be requested in a form @@ -132,7 +132,7 @@ func (c *container) Init(cfg Config) error { // inject service dependencies if ok, err := c.initService(e.svc, cfg.Get(e.name)); err != nil { // soft error (skipping) - if err == noConfig { + if err == errNoConfig { c.log.Warningf("[%s]: no config has been provided", e.name) continue } @@ -253,7 +253,7 @@ func (c *container) resolveValues(s interface{}, m reflect.Method, cfg Config) ( case v.Implements(reflect.TypeOf((*HydrateConfig)(nil)).Elem()): // injectable config if cfg == nil { - return nil, noConfig + return nil, errNoConfig } sc := reflect.New(v.Elem()) @@ -265,27 +265,18 @@ func (c *container) resolveValues(s interface{}, m reflect.Method, cfg Config) ( case v.Implements(reflect.TypeOf((*Config)(nil)).Elem()): // generic config section if cfg == nil { - return nil, noConfig + return nil, errNoConfig } values = append(values, reflect.ValueOf(cfg)) default: // dependency on other service (resolution to nil if service can't be found) - found := false - for _, e := range c.services { - if !e.hasStatus(StatusOK) || !v.ConvertibleTo(reflect.ValueOf(e.svc).Type()) { - continue - } - - found = true - values = append(values, reflect.ValueOf(e.svc)) - break + value, err := c.resolveValue(v) + if err != nil { + return nil, err } - if !found { - // placeholder (make sure to check inside the method) - values = append(values, reflect.New(v).Elem()) - } + values = append(values, value) } } @@ -308,3 +299,35 @@ func (c *container) verifySignature(m reflect.Method) error { return nil } + +func (c *container) resolveValue(v reflect.Type) (reflect.Value, error) { + value := reflect.Value{} + for _, e := range c.services { + if !e.hasStatus(StatusOK) { + continue + } + + if v.Kind() == reflect.Interface && reflect.TypeOf(e.svc).Implements(v) { + if value.IsValid() { + return value, fmt.Errorf("disambiguous dependency `%s`", v) + } + + value = reflect.ValueOf(e.svc) + } + + if v.ConvertibleTo(reflect.ValueOf(e.svc).Type()) { + if value.IsValid() { + return value, fmt.Errorf("disambiguous dependency `%s`", v) + } + + value = reflect.ValueOf(e.svc) + } + } + + if !value.IsValid() { + // placeholder (make sure to check inside the method) + value = reflect.New(v).Elem() + } + + return value, nil +} diff --git a/service/env/config.go b/service/env/config.go new file mode 100644 index 00000000..06342f98 --- /dev/null +++ b/service/env/config.go @@ -0,0 +1,17 @@ +package env + +import ( + "github.com/spiral/roadrunner/service" +) + +// Config defines set of env values for RR workers. +type Config struct { + // Values to set as worker _ENV. + Values map[string]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 { + c.Values = map[string]string{"RR": "YES"} + return cfg.Unmarshal(&c.Values) +} diff --git a/service/env/provider.go b/service/env/provider.go new file mode 100644 index 00000000..75a1e31b --- /dev/null +++ b/service/env/provider.go @@ -0,0 +1,8 @@ +package env + +// Provider aggregates list of environment variables. This interface can be used in custom implementation to drive +// values from external sources. +type Provider interface { + // GetEnv must return list of env variables. + GetEnv() map[string]string +} diff --git a/service/env/service.go b/service/env/service.go new file mode 100644 index 00000000..95e99093 --- /dev/null +++ b/service/env/service.go @@ -0,0 +1,21 @@ +package env + +// ID contains default svc name. +const ID = "env" + +// Service provides ability to map _ENV values from config file. +type Service struct { + cfg *Config +} + +// Init must return configure svc and return true if svc hasStatus enabled. Must return error in case of +// misconfiguration. Services must not be used without proper configuration pushed first. +func (s *Service) Init(cfg *Config) (bool, error) { + s.cfg = cfg + return true, nil +} + +// GetEnv must return list of env variables. +func (s *Service) GetEnv() map[string]string { + return s.cfg.Values +} diff --git a/service/http/attributes/attributes.go b/service/http/attributes/attributes.go index 94d0e9c1..77d6ea69 100644 --- a/service/http/attributes/attributes.go +++ b/service/http/attributes/attributes.go @@ -6,7 +6,9 @@ import ( "net/http" ) -const contextKey = "psr:attributes" +type attrKey int + +const contextKey attrKey = iota type attrs map[string]interface{} @@ -41,7 +43,7 @@ func All(r *http.Request) map[string]interface{} { return v.(attrs) } -// get gets the value from request context. It replaces any existing +// Get gets the value from request context. It replaces any existing // values. func Get(r *http.Request, key string) interface{} { v := r.Context().Value(contextKey) @@ -52,7 +54,7 @@ func Get(r *http.Request, key string) interface{} { return v.(attrs).get(key) } -// set sets the key to value. It replaces any existing +// Set sets the key to value. It replaces any existing // values. Context specific. func Set(r *http.Request, key string, value interface{}) error { v := r.Context().Value(contextKey) diff --git a/service/http/service.go b/service/http/service.go index f7fdf2ab..a8f99669 100644 --- a/service/http/service.go +++ b/service/http/service.go @@ -3,6 +3,7 @@ package http import ( "context" "github.com/spiral/roadrunner" + "github.com/spiral/roadrunner/service/env" "github.com/spiral/roadrunner/service/http/attributes" "github.com/spiral/roadrunner/service/rpc" "net/http" @@ -18,10 +19,10 @@ type middleware func(f http.HandlerFunc) http.HandlerFunc // Service manages rr, http servers. type Service struct { - cfg *Config - lsns []func(event int, ctx interface{}) - mdws []middleware - + cfg *Config + env env.Provider + lsns []func(event int, ctx interface{}) + mdws []middleware mu sync.Mutex rr *roadrunner.Server stopping int32 @@ -41,12 +42,13 @@ func (s *Service) AddListener(l func(event int, ctx interface{})) { // Init must return configure svc and return true if svc hasStatus enabled. Must return error in case of // misconfiguration. Services must not be used without proper configuration pushed first. -func (s *Service) Init(cfg *Config, r *rpc.Service) (bool, error) { +func (s *Service) Init(cfg *Config, r *rpc.Service, e env.Provider) (bool, error) { if !cfg.Enable { return false, nil } s.cfg = cfg + s.env = e if r != nil { r.Register(ID, &rpcServer{s}) } @@ -57,6 +59,13 @@ func (s *Service) Init(cfg *Config, r *rpc.Service) (bool, error) { // Serve serves the svc. func (s *Service) Serve() error { s.mu.Lock() + + if s.env != nil { + for k, v := range s.env.GetEnv() { + s.cfg.Workers.SetEnv(k, v) + } + } + rr := roadrunner.NewServer(s.cfg.Workers) s.rr = rr @@ -116,11 +125,7 @@ func (s *Service) listener(event int, ctx interface{}) { } if event == roadrunner.EventServerFailure { - if atomic.LoadInt32(&s.stopping) != 0 { - // attempting rr server restart - if err := s.rr.Start(); err != nil { - s.Stop() - } - } + // underlying rr server is dead + s.Stop() } } |