summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Titov <[email protected]>2019-12-23 12:04:12 +0300
committerGitHub <[email protected]>2019-12-23 12:04:12 +0300
commit7f694966730f6dac09d0d0ea3bf51276b8e4dfe4 (patch)
tree55d584785e87aef8ee15f5ab5f01c22d50754397
parentfadf373c1fe5e51bfaeb9e5ac3fe4ee748620a44 (diff)
parent028ff585f8f8a42f4796afdb932f97eee6eb8f4c (diff)
Merge pull request #204 from spiral/feature/hotreload
[wip] Hot-reloading capabilities - review wanted
-rw-r--r--server_config.go40
-rw-r--r--service/container.go13
-rw-r--r--service/container_test.go14
-rw-r--r--service/http/service.go7
-rw-r--r--service/metrics/rpc_test.go5
-rw-r--r--service/metrics/service.go2
6 files changed, 76 insertions, 5 deletions
diff --git a/server_config.go b/server_config.go
index 35965962..5fb4a014 100644
--- a/server_config.go
+++ b/server_config.go
@@ -8,15 +8,22 @@ import (
"os"
"os/exec"
"strings"
+ "sync"
"syscall"
"time"
)
+// CommandProducer can produce commands.
+type CommandProducer func(cfg *ServerConfig) func() *exec.Cmd
+
// ServerConfig config combines factory, pool and cmd configurations.
type ServerConfig struct {
// Command includes command strings with all the parameters, example: "php worker.php pipes".
Command string
+ // CommandProducer overwrites
+ CommandProducer CommandProducer
+
// Relay defines connection method and factory to be used to connect to workers:
// "pipes", "tcp://:6001", "unix://rr.sock"
// This config section must not change on re-configuration.
@@ -31,7 +38,8 @@ type ServerConfig struct {
Pool *Config
// values defines set of values to be passed to the command context.
- env []string
+ mu sync.Mutex
+ env map[string]string
}
// InitDefaults sets missing values to their default values.
@@ -68,18 +76,42 @@ func (cfg *ServerConfig) Differs(new *ServerConfig) bool {
// 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))
+ cfg.mu.Lock()
+ defer cfg.mu.Unlock()
+
+ if cfg.env == nil {
+ cfg.env = make(map[string]string)
+ }
+
+ cfg.env[k] = v
+}
+
+// GetEnv must return list of env variables.
+func (cfg *ServerConfig) GetEnv() (env []string) {
+ env = append(os.Environ(), fmt.Sprintf("RR_RELAY=%s", cfg.Relay))
+ for k, v := range cfg.env {
+ env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(k), v))
+ }
+
+ return
}
// makeCommands returns new command provider based on configured options.
func (cfg *ServerConfig) makeCommand() func() *exec.Cmd {
+ cfg.mu.Lock()
+ defer cfg.mu.Unlock()
+
+ if cfg.CommandProducer != nil {
+ return cfg.CommandProducer(cfg)
+ }
+
var cmd = strings.Split(cfg.Command, " ")
return func() *exec.Cmd {
cmd := exec.Command(cmd[0], cmd[1:]...)
osutil.IsolateProcess(cmd)
- cmd.Env = append(os.Environ(), fmt.Sprintf("RR_RELAY=%s", cfg.Relay))
- cmd.Env = append(cmd.Env, cfg.env...)
+ cmd.Env = cfg.GetEnv()
+
return cmd
}
}
diff --git a/service/container.go b/service/container.go
index a21b49b4..0be4f853 100644
--- a/service/container.go
+++ b/service/container.go
@@ -46,6 +46,9 @@ type Container interface {
// Close all active services.
Stop()
+
+ // List service names.
+ List() []string
}
// Config provides ability to slice configuration sections and unmarshal configuration data into
@@ -212,6 +215,16 @@ func (c *container) Stop() {
}
}
+// List all service names.
+func (c *container) List() []string {
+ names := make([]string, 0, len(c.services))
+ for _, e := range c.services {
+ names = append(names, e.name)
+ }
+
+ return names
+}
+
// calls Init method with automatically resolved arguments.
func (c *container) initService(s interface{}, segment Config) (bool, error) {
r := reflect.TypeOf(s)
diff --git a/service/container_test.go b/service/container_test.go
index 8fcaede2..4628986a 100644
--- a/service/container_test.go
+++ b/service/container_test.go
@@ -129,6 +129,20 @@ func TestContainer_Has(t *testing.T) {
assert.False(t, c.Has("another"))
}
+func TestContainer_List(t *testing.T) {
+ logger, hook := test.NewNullLogger()
+ logger.SetLevel(logrus.DebugLevel)
+
+ c := NewContainer(logger)
+ c.Register("test", &testService{})
+
+ assert.Equal(t, 0, len(hook.Entries))
+ assert.Equal(t, 1, len(c.List()))
+
+ assert.True(t, c.Has("test"))
+ assert.False(t, c.Has("another"))
+}
+
func TestContainer_Get(t *testing.T) {
logger, hook := test.NewNullLogger()
logger.SetLevel(logrus.DebugLevel)
diff --git a/service/http/service.go b/service/http/service.go
index 58038acb..945a12c4 100644
--- a/service/http/service.go
+++ b/service/http/service.go
@@ -31,6 +31,7 @@ type middleware func(f http.HandlerFunc) http.HandlerFunc
// Service manages rr, http servers.
type Service struct {
cfg *Config
+ cprod roadrunner.CommandProducer
env env.Environment
lsns []func(event int, ctx interface{})
mdwr []middleware
@@ -48,6 +49,11 @@ func (s *Service) Attach(w roadrunner.Controller) {
s.controller = w
}
+// ProduceCommands changes the default command generator method
+func (s *Service) ProduceCommands(producer roadrunner.CommandProducer) {
+ s.cprod = producer
+}
+
// AddMiddleware adds new net/http mdwr.
func (s *Service) AddMiddleware(m middleware) {
s.mdwr = append(s.mdwr, m)
@@ -87,6 +93,7 @@ func (s *Service) Serve() error {
}
}
+ s.cfg.Workers.CommandProducer = s.cprod
s.cfg.Workers.SetEnv("RR_HTTP", "true")
s.rr = roadrunner.NewServer(s.cfg.Workers)
diff --git a/service/metrics/rpc_test.go b/service/metrics/rpc_test.go
index 6d061f1d..136f031c 100644
--- a/service/metrics/rpc_test.go
+++ b/service/metrics/rpc_test.go
@@ -43,10 +43,13 @@ func setup(t *testing.T, metric string, portNum string) (*rpc2.Client, service.C
assert.True(t, s.(*Service).Enabled())
go func() { c.Serve() }()
- time.Sleep(time.Millisecond * 100)
+ time.Sleep(time.Millisecond * 200)
client, err := rs.Client()
assert.NoError(t, err)
+ if err != nil {
+ panic(err)
+ }
return client, c
}
diff --git a/service/metrics/service.go b/service/metrics/service.go
index 4916b3e0..b581f041 100644
--- a/service/metrics/service.go
+++ b/service/metrics/service.go
@@ -1,5 +1,7 @@
package metrics
+// todo: declare metric at runtime
+
import (
"context"
"github.com/prometheus/client_golang/prometheus"