From 8c48c7ebb172fdfead82e36d6c3eeef8d8c0e460 Mon Sep 17 00:00:00 2001 From: codeskyblue Date: Thu, 8 Sep 2016 13:55:37 +0800 Subject: [PATCH] add SetUser support --- Godeps/Godeps.json | 2 +- README.md | 61 +++++++++++-------- fsm.go | 9 ++- vendor/github.com/codeskyblue/kexec/README.md | 58 ++++++++++++------ vendor/github.com/codeskyblue/kexec/kexec.go | 11 ++-- .../codeskyblue/kexec/kexec_posix.go | 27 +++++++- .../codeskyblue/kexec/kexec_windows.go | 11 +++- 7 files changed, 124 insertions(+), 55 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 1633a40..90c9efe 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -5,7 +5,7 @@ "Deps": [ { "ImportPath": "github.com/codeskyblue/kexec", - "Rev": "098ccba5e5e7e676f3631983c5ea931a3592871f" + "Rev": "863094f94c7fb7c235764bf8f0f79cccea78c8eb" }, { "ImportPath": "github.com/equinox-io/equinox", diff --git a/README.md b/README.md index c9bbeb2..2819ae0 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,27 @@ # gosuv [![Build Status](https://travis-ci.org/codeskyblue/gosuv.svg)](https://travis-ci.org/codeskyblue/gosuv) -## Should not use in production now (current is in alpha) +## Should not use in production now (current is in beta) Process management writtern by golang, inspired by python-supervisor -Features +## So why write another supervisor? +I have been using python-supervisor for many years and there are something uncomfortable feelings. + +1. Log can't contains ANSI color chars +1. The configuration file can add on the web, often forgot some settings. +1. `supervisorctl reload` will cause supervisord restarted +1. Hard to set status change to fatal notifications. +1. No process performance monitor page. +1. Program starts with no common environ, eg, missing HOME and USER variable +1. Kill process default is not group kill which make sub process still running. +1. More... will added when I think of it. + +## Features -* [x] Realtime log view * [x] Web control page - * [x] Start, Stop, Tail, Reload + * [x] Start, Stop, Tail, Reload + * [x] Realtime log * [x] Add program support * [ ] Edit support * [ ] Delete support @@ -49,8 +61,8 @@ go-bindata-assetfs -tags bindata res/... go build -tags bindata ``` -## Usage -Start server in the background +## Quick start +After you installed gosuv, the first thing is to start server. ```sh gosuv start-server @@ -63,7 +75,8 @@ $ gosuv status Server is running ``` -Open web to see the manager page. +Open web to see the manager page. And follow the gif to add a program to gosuv. + ![gosuv web](docs/gosuv.gif) @@ -90,6 +103,8 @@ client: Logs can be found in `$HOME/.gosuv/log/` +Edit config file(default located in `$HOME/.gosuv/programs.yml`) and run `gosuv reload` will take effect immediately. + ## Design HTTP is follow the RESTFul guide. @@ -110,7 +125,7 @@ Only 4 states. [ref](http://supervisord.org/subprocess.html#process-states) ![states](docs/states.png) -## Notification (todo) +## Notification Configuration example ```yaml @@ -126,26 +141,20 @@ Configuration example Now only support [pushover](https://pushover.net/api), and only status change to fatal will get notified. -# Plugin Design (todo) -Current plugins: - -- [tailf](https://github.com/codeskyblue/gosuv-tailf) - -All command plugin will store in `$HOME/.gosuv/cmdplugin`, gosuv will treat this plugin as a subcommand. +## Integrate with github (todo) +This is feature that will helps update your deployment environment once your updated in the github. -for example: +This part is set in the `programs.yml`, take look the example - $HOME/.gosuv/cmdplugin/ --. - |- showpid/ - |- run - -There is a directory `showpid` - -When run `gosuv showpid`, file `run` will be called. - - -## Use libs -* +```yml +- demo-program: + command: python app.py + directory: /opt/demo + webhook: + github: + secret: 123456 + command: git pull origin master +``` ## LICENSE [MIT](LICENSE) diff --git a/fsm.go b/fsm.go index 8629adf..f3f1cb5 100644 --- a/fsm.go +++ b/fsm.go @@ -107,6 +107,7 @@ type Program struct { StartAuto bool `yaml:"start_auto" json:"startAuto"` StartRetries int `yaml:"start_retries" json:"startRetries"` StartSeconds int `yaml:"start_seconds" json:"startSeconds"` + User string `yaml:"user,omitempty" json:"user"` Notifications struct { Pushover struct { ApiKey string `yaml:"api_key"` @@ -165,10 +166,16 @@ type Process struct { Status string `json:"status"` } +// FIXME(ssx): maybe need to return error func (p *Process) buildCommand() *kexec.KCommand { - cmd := kexec.CommandString(p.Command) // Not tested here, I think it should work + cmd := kexec.CommandString(p.Command) // cmd := kexec.Command(p.Command[0], p.Command[1:]...) cmd.Dir = p.Dir + if p.User != "" { + if err := cmd.SetUser(p.User); err != nil { + log.Warnf("cmd:%s chusr to %s failed", p.Name, p.User) + } + } logDir := filepath.Join(defaultConfigDir, "log", sanitize.Name(p.Name)) if !IsDir(logDir) { os.MkdirAll(logDir, 0755) diff --git a/vendor/github.com/codeskyblue/kexec/README.md b/vendor/github.com/codeskyblue/kexec/README.md index 84da704..b73b7f1 100644 --- a/vendor/github.com/codeskyblue/kexec/README.md +++ b/vendor/github.com/codeskyblue/kexec/README.md @@ -14,32 +14,54 @@ This lib has been used in [fswatch](https://github.com/codeskyblue/fswatch). example1: - package main +```go +package main - import "github.com/codeskyblue/kexec" +import "github.com/codeskyblue/kexec" - func main(){ - p := kexec.Command("python", "flask_main.py") - p.Start() - p.Terminate(syscall.SIGINT) - } +func main(){ + p := kexec.Command("python", "flask_main.py") + p.Start() + p.Terminate(syscall.SIGINT) +} +``` example2: see more [examples](examples) - package main - - import "github.com/codeskyblue/kexec" +```go +package main + +import ( + "github.com/codeskyblue/kexec" +) + +func main() { + // In unix will call: bash -c "python flask_main.py" + // In windows will call: cmd /c "python flask_main.py" + p := kexec.CommandString("python flask_main.py") + p.Stdout = os.Stdout + p.Stderr = os.Stderr + p.Start() + p.Terminate(syscall.SIGKILL) +} +``` + +example3: + +```go +package main + +import "github.com/codeskyblue/kexec" - func main() { - // In unix will call: bash -c "python flask_main.py" - // In windows will call: cmd /c "python flask_main.py" - p := kexec.CommandString("python flask_main.py") - p.Start() - p.Terminate(syscall.SIGKILL) - } +func main() { + p := kexec.Command("whoami") + p.SetUser("codeskyblue") // Only works on darwin and linux + p.Run() +} +``` ## PS This lib also support you call `Wait()` twice, which is not support by `os/exec` ## LICENSE -[MIT](LICENSE) \ No newline at end of file +[MIT](LICENSE) diff --git a/vendor/github.com/codeskyblue/kexec/kexec.go b/vendor/github.com/codeskyblue/kexec/kexec.go index cc39bda..4183a27 100644 --- a/vendor/github.com/codeskyblue/kexec/kexec.go +++ b/vendor/github.com/codeskyblue/kexec/kexec.go @@ -9,7 +9,7 @@ import ( type KCommand struct { *exec.Cmd - errChs []chan error + errCs []chan error err error finished bool once sync.Once @@ -23,19 +23,20 @@ func (c *KCommand) Run() error { return c.Wait() } +// This Wait wraps exec.Wait, but support multi call func (k *KCommand) Wait() error { if k.Process == nil { return errors.New("exec: not started") } k.once.Do(func() { - if k.errChs == nil { - k.errChs = make([]chan error, 0) + if k.errCs == nil { + k.errCs = make([]chan error, 0) } go func() { k.err = k.Cmd.Wait() k.mu.Lock() k.finished = true - for _, errC := range k.errChs { + for _, errC := range k.errCs { errC <- k.err } k.mu.Unlock() @@ -47,7 +48,7 @@ func (k *KCommand) Wait() error { return k.err } errC := make(chan error, 1) - k.errChs = append(k.errChs, errC) + k.errCs = append(k.errCs, errC) k.mu.Unlock() return <-errC } diff --git a/vendor/github.com/codeskyblue/kexec/kexec_posix.go b/vendor/github.com/codeskyblue/kexec/kexec_posix.go index e205aeb..394d539 100644 --- a/vendor/github.com/codeskyblue/kexec/kexec_posix.go +++ b/vendor/github.com/codeskyblue/kexec/kexec_posix.go @@ -5,6 +5,8 @@ package kexec import ( "os" "os/exec" + "os/user" + "strconv" "syscall" ) @@ -24,8 +26,8 @@ func Command(name string, arg ...string) *KCommand { func CommandString(command string) *KCommand { cmd := exec.Command("/bin/bash", "-c", command) setupCmd(cmd) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + //cmd.Stdout = os.Stdout + //cmd.Stderr = os.Stderr return &KCommand{ Cmd: cmd, } @@ -43,3 +45,24 @@ func (p *KCommand) Terminate(sig os.Signal) (err error) { } return err } + +// Ref: http://stackoverflow.com/questions/21705950/running-external-commands-through-os-exec-under-another-user +func (k *KCommand) SetUser(name string) (err error) { + u, err := user.Lookup(name) + if err != nil { + return err + } + uid, err := strconv.Atoi(u.Uid) + if err != nil { + return err + } + gid, err := strconv.Atoi(u.Gid) + if err != nil { + return err + } + if k.SysProcAttr == nil { + k.SysProcAttr = &syscall.SysProcAttr{} + } + k.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + return nil +} diff --git a/vendor/github.com/codeskyblue/kexec/kexec_windows.go b/vendor/github.com/codeskyblue/kexec/kexec_windows.go index 5be90a8..4515a3e 100644 --- a/vendor/github.com/codeskyblue/kexec/kexec_windows.go +++ b/vendor/github.com/codeskyblue/kexec/kexec_windows.go @@ -1,6 +1,7 @@ package kexec import ( + "log" "os" "os/exec" "strconv" @@ -14,8 +15,8 @@ func Command(name string, arg ...string) *KCommand { func CommandString(command string) *KCommand { cmd := exec.Command("cmd", "/c", command) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + //cmd.Stdout = os.Stdout + //cmd.Stderr = os.Stderr return &KCommand{ Cmd: cmd, } @@ -31,3 +32,9 @@ func (p *KCommand) Terminate(sig os.Signal) (err error) { c.Stderr = os.Stderr return c.Run() } + +// SetUser not support on windws +func (k *KCommand) SetUser(name string) (err error) { + log.Printf("Can not set user(%s) on windows", name) + return nil +}