diff options
-rw-r--r-- | server.go | 37 | ||||
-rw-r--r-- | server_config.go | 77 | ||||
-rw-r--r-- | server_config_test.go | 42 | ||||
-rw-r--r-- | server_test.go | 84 | ||||
-rw-r--r-- | user.go | 31 | ||||
-rw-r--r-- | user_test.go | 76 |
6 files changed, 68 insertions, 279 deletions
@@ -4,7 +4,6 @@ import ( "sync" "os/exec" "fmt" - "errors" ) const ( @@ -26,6 +25,9 @@ type Server struct { // configures server, pool, cmd creation and factory. cfg *ServerConfig + // worker command creator + cmd func() *exec.Cmd + // observes pool events (can be attached to multiple pools at the same time) observer func(event int, ctx interface{}) @@ -35,9 +37,6 @@ type Server struct { // indicates that server was started started bool - // worker command creator - cmd func() *exec.Cmd - // creates and connects to workers factory Factory @@ -46,14 +45,17 @@ type Server struct { } // NewServer creates new router. Make sure to call configure before the usage. -func NewServer(cfg *ServerConfig, o func(event int, ctx interface{})) *Server { - return &Server{ - cfg: cfg, - observer: o, - } +func NewServer(cmd func() *exec.Cmd, cfg *ServerConfig) *Server { + return &Server{cmd: cmd, cfg: cfg} } -// Reconfigure re-configures underlying pool and destroys it's previous version if any. +// Observe attaches server event watcher. +func (srv *Server) Observe(o func(event int, ctx interface{})) { + srv.observer = o +} + +// Reconfigure re-configures underlying pool and destroys it's previous version if any. Reconfigure will ignore factory +// and relay settings. func (srv *Server) Reconfigure(cfg *ServerConfig) error { srv.mu.Lock() if !srv.started { @@ -62,23 +64,18 @@ func (srv *Server) Reconfigure(cfg *ServerConfig) error { } srv.mu.Unlock() - // we are not allowing factory or cmd changes while the server is running. - if srv.cfg.Differs(cfg) { - return errors.New("config change while running server (only pool config change is allowed)") - } - srv.mu.Lock() previous := srv.pool srv.mu.Unlock() - pool, err := NewPool(srv.cmd, srv.factory, *cfg.Pool) + pool, err := NewPool(srv.cmd, srv.factory, cfg.Pool) if err != nil { return err } srv.throw(EventNewPool, pool) srv.mu.Lock() - srv.cfg, srv.pool = cfg, pool + srv.cfg.Pool, srv.pool = cfg.Pool, pool srv.pool.Observe(srv.poolObserver) srv.mu.Unlock() @@ -102,15 +99,11 @@ func (srv *Server) Start() (err error) { srv.mu.Lock() defer srv.mu.Unlock() - if srv.cmd, err = srv.cfg.makeCommand(); err != nil { - return err - } - if srv.factory, err = srv.cfg.makeFactory(); err != nil { return err } - if srv.pool, err = NewPool(srv.cmd, srv.factory, *srv.cfg.Pool); err != nil { + if srv.pool, err = NewPool(srv.cmd, srv.factory, srv.cfg.Pool); err != nil { return err } diff --git a/server_config.go b/server_config.go index 7e8aa80e..14bd7619 100644 --- a/server_config.go +++ b/server_config.go @@ -5,10 +5,6 @@ import ( "net" "strings" "time" - "os/exec" - "syscall" - "os/user" - "strconv" ) const ( @@ -18,18 +14,6 @@ const ( // Server config combines factory, pool and cmd configurations. type ServerConfig struct { - // Command includes command strings with all the parameters, example: "php worker.php pipes". This config section - // // must not change on re-configuration. - Command string - - // User specifies what user to run command under, for Unix systems only. Support both UID and name options. Keep - // empty to use current user.This config section must not change on re-configuration. - User string - - // Group specifies what group to run command under, for Unix systems only. Support GID or name options. Keep empty - // to use current user.This config section must not change on re-configuration. - Group string - // 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. @@ -41,74 +25,15 @@ 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 + Pool Config } // Differs returns true if configuration has changed but ignores pool changes. func (cfg *ServerConfig) Differs(new *ServerConfig) bool { - // command configuration has changed - if cfg.Command != new.Command || cfg.User != new.User || cfg.Group != new.Group { - return true - } - // factory configuration has changed return cfg.Relay != new.Relay || cfg.RelayTimeout != new.RelayTimeout } -// makeCommands returns new command provider based on configured options. -func (cfg *ServerConfig) makeCommand() (func() *exec.Cmd, error) { - var ( - err error - u *user.User - g *user.Group - crd *syscall.Credential - cmd = strings.Split(cfg.Command, " ") - ) - - if cfg.User != "" { - if u, err = resolveUser(cfg.User); err != nil { - return nil, err - } - } - - if cfg.Group != "" { - if g, err = resolveGroup(cfg.Group); err != nil { - return nil, err - } - } - - if u != nil || g != nil { - crd = &syscall.Credential{} - - if u != nil { - uid, err := strconv.ParseUint(u.Uid, 10, 32) - if err != nil { - return nil, err - } - - crd.Uid = uint32(uid) - } - - if g != nil { - gid, err := strconv.ParseUint(g.Gid, 10, 32) - if err != nil { - return nil, err - } - - crd.Gid = uint32(gid) - } - } - - return func() *exec.Cmd { - cmd := exec.Command(cmd[0], cmd[1:]...) - if crd != nil { - cmd.SysProcAttr = &syscall.SysProcAttr{Credential: crd} - } - - return cmd - }, nil -} - // makeFactory creates and connects new factory instance based on given parameters. func (cfg *ServerConfig) makeFactory() (Factory, error) { if cfg.Relay == "pipes" || cfg.Relay == "pipe" { diff --git a/server_config_test.go b/server_config_test.go index 42d2fec4..e3ef6ec0 100644 --- a/server_config_test.go +++ b/server_config_test.go @@ -3,9 +3,7 @@ package roadrunner import ( "testing" "github.com/stretchr/testify/assert" - "os/user" "runtime" - "strconv" ) func Test_ServerConfig_PipeFactory(t *testing.T) { @@ -50,6 +48,10 @@ func Test_ServerConfig_SocketFactory(t *testing.T) { } func Test_ServerConfig_UnixSocketFactory(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("not supported on " + runtime.GOOS) + } + cfg := &ServerConfig{Relay: "unix://unix.sock"} f, err := cfg.makeFactory() defer f.Close() @@ -61,41 +63,13 @@ func Test_ServerConfig_UnixSocketFactory(t *testing.T) { } func Test_ServerConfig_ErrorFactory(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("not supported on " + runtime.GOOS) + } + cfg := &ServerConfig{Relay: "uni:unix.sock"} f, err := cfg.makeFactory() assert.Nil(t, f) assert.Error(t, err) assert.Equal(t, "invalid relay DSN (pipes, tcp://:6001, unix://rr.sock)", err.Error()) } - -func Test_ServerConfig_Cmd(t *testing.T) { - cfg := &ServerConfig{ - Command: "php php-src/tests/client.php pipes", - } - - cmd, err := cfg.makeCommand() - assert.NoError(t, err) - assert.NotNil(t, cmd) -} - -func Test_ServerConfig_Cmd_Credentials(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("not supported on " + runtime.GOOS) - } - - u, err := user.Current() - assert.NoError(t, err) - - cfg := &ServerConfig{ - Command: "php php-src/tests/client.php pipes", - User: u.Username, - Group: u.Gid, - } - - cmd, err := cfg.makeCommand() - assert.NoError(t, err) - assert.NotNil(t, cmd) - - assert.Equal(t, u.Uid, strconv.Itoa(int(cmd().SysProcAttr.Credential.Uid))) - assert.Equal(t, u.Gid, strconv.Itoa(int(cmd().SysProcAttr.Credential.Gid))) -} diff --git a/server_test.go b/server_test.go index cfc75265..6cc07c4d 100644 --- a/server_test.go +++ b/server_test.go @@ -5,18 +5,20 @@ import ( "github.com/stretchr/testify/assert" "runtime" "time" + "os/exec" ) func TestServer_PipesEcho(t *testing.T) { - srv := NewServer(&ServerConfig{ - Command: "php php-src/tests/client.php echo pipes", - Relay: "pipes", - Pool: &Config{ - NumWorkers: uint64(runtime.NumCPU()), - AllocateTimeout: time.Second, - DestroyTimeout: time.Second, - }, - }, nil) + srv := NewServer( + func() *exec.Cmd { return exec.Command("php", "php-src/tests/client.php", "echo", "pipes") }, + &ServerConfig{ + Relay: "pipes", + Pool: Config{ + NumWorkers: uint64(runtime.NumCPU()), + AllocateTimeout: time.Second, + DestroyTimeout: time.Second, + }, + }) defer srv.Stop() assert.NoError(t, srv.Start()) @@ -32,16 +34,17 @@ func TestServer_PipesEcho(t *testing.T) { } func TestServer_SocketEcho(t *testing.T) { - srv := NewServer(&ServerConfig{ - Command: "php php-src/tests/client.php echo tcp", - Relay: "tcp://:9007", - RelayTimeout: 10 * time.Second, - Pool: &Config{ - NumWorkers: uint64(runtime.NumCPU()), - AllocateTimeout: time.Second, - DestroyTimeout: time.Second, - }, - }, nil) + srv := NewServer( + func() *exec.Cmd { return exec.Command("php", "php-src/tests/client.php", "echo", "tcp") }, + &ServerConfig{ + Relay: "tcp://:9007", + RelayTimeout: 10 * time.Second, + Pool: Config{ + NumWorkers: uint64(runtime.NumCPU()), + AllocateTimeout: time.Second, + DestroyTimeout: time.Second, + }, + }) defer srv.Stop() assert.NoError(t, srv.Start()) @@ -57,24 +60,24 @@ func TestServer_SocketEcho(t *testing.T) { } func TestServer_Reconfigure(t *testing.T) { - srv := NewServer(&ServerConfig{ - Command: "php php-src/tests/client.php echo pipes", - Relay: "pipes", - Pool: &Config{ - NumWorkers: 1, - AllocateTimeout: time.Second, - DestroyTimeout: time.Second, - }, - }, nil) + srv := NewServer( + func() *exec.Cmd { return exec.Command("php", "php-src/tests/client.php", "echo", "pipes") }, + &ServerConfig{ + Relay: "pipes", + Pool: Config{ + NumWorkers: 1, + AllocateTimeout: time.Second, + DestroyTimeout: time.Second, + }, + }) defer srv.Stop() assert.NoError(t, srv.Start()) assert.Len(t, srv.Workers(), 1) err := srv.Reconfigure(&ServerConfig{ - Command: "php php-src/tests/client.php echo pipes", - Relay: "pipes", - Pool: &Config{ + Relay: "pipes", + Pool: Config{ NumWorkers: 2, AllocateTimeout: time.Second, DestroyTimeout: time.Second, @@ -86,15 +89,16 @@ func TestServer_Reconfigure(t *testing.T) { } func TestServer_Reset(t *testing.T) { - srv := NewServer(&ServerConfig{ - Command: "php php-src/tests/client.php echo pipes", - Relay: "pipes", - Pool: &Config{ - NumWorkers: 1, - AllocateTimeout: time.Second, - DestroyTimeout: time.Second, - }, - }, nil) + srv := NewServer( + func() *exec.Cmd { return exec.Command("php", "php-src/tests/client.php", "echo", "pipes") }, + &ServerConfig{ + Relay: "pipes", + Pool: Config{ + NumWorkers: 1, + AllocateTimeout: time.Second, + DestroyTimeout: time.Second, + }, + }) defer srv.Stop() assert.NoError(t, srv.Start()) diff --git a/user.go b/user.go deleted file mode 100644 index 1d47b5fb..00000000 --- a/user.go +++ /dev/null @@ -1,31 +0,0 @@ -package roadrunner - -import ( - "os/user" - "fmt" -) - -// resolveUser attempt to find system user by it's name or uid. -func resolveUser(u string) (usr *user.User, err error) { - usr, err = user.LookupId(u) - if usr != nil { - return usr, nil - } - - return user.Lookup(u) -} - -// resolveUser attempt to find system group by it's name or uid. -func resolveGroup(g string) (grp *user.Group, err error) { - grp, err = user.LookupGroupId(g) - if grp != nil && grp.Name != "nogroup" { - return grp, nil - } - - grp, err = user.LookupGroup(g) - if grp != nil && grp.Name != "nogroup" { - return grp, nil - } - - return nil, fmt.Errorf("no such group %s", g) -} diff --git a/user_test.go b/user_test.go deleted file mode 100644 index 1b945e3c..00000000 --- a/user_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package roadrunner - -import ( - "testing" - "runtime" - "github.com/stretchr/testify/assert" - "os/user" -) - -func Test_ResolveUser_Error(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("not supported on " + runtime.GOOS) - } - - u, err := resolveUser("-1") - assert.Nil(t, u) - assert.Error(t, err) - - u, err = resolveUser("random-user") - assert.Nil(t, u) - assert.Error(t, err) -} - -func Test_ResolveUser(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("not supported on " + runtime.GOOS) - } - - current, err := user.Current() - assert.NotNil(t, current) - assert.NoError(t, err) - - u, err := resolveUser(current.Uid) - assert.NoError(t, err) - assert.NotNil(t, u) - assert.Equal(t, current.Uid, u.Uid) - - u, err = resolveUser(current.Username) - assert.NoError(t, err) - assert.NotNil(t, u) - assert.Equal(t, current.Uid, u.Uid) -} - -func Test_ResolveGroup_Error(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("not supported on " + runtime.GOOS) - } - - g, err := resolveGroup("-1") - assert.Nil(t, g) - assert.Error(t, err) - - g, err = resolveGroup("random-group") - assert.Nil(t, g) - assert.Error(t, err) -} - -func Test_ResolveGroup(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("not supported on " + runtime.GOOS) - } - - current, err := user.Current() - assert.NotNil(t, current) - assert.NoError(t, err) - - g, err := resolveGroup(current.Gid) - assert.NoError(t, err) - assert.NotNil(t, g) - assert.Equal(t, current.Gid, g.Gid) - - g2, err := resolveGroup(g.Name) - assert.NoError(t, err) - assert.NotNil(t, g2) - assert.Equal(t, g2.Gid, g.Gid) -} |