add SetUser support

master 0.0.2
codeskyblue 8 years ago
parent 1168486740
commit 8c48c7ebb1

2
Godeps/Godeps.json generated

@ -5,7 +5,7 @@
"Deps": [
{
"ImportPath": "github.com/codeskyblue/kexec",
"Rev": "098ccba5e5e7e676f3631983c5ea931a3592871f"
"Rev": "863094f94c7fb7c235764bf8f0f79cccea78c8eb"
},
{
"ImportPath": "github.com/equinox-io/equinox",

@ -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 <http://localhost:11313> to see the manager page.
Open web <http://localhost:11313> 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
* <https://github.com/ahmetalpbalkan/govvv>
```yml
- demo-program:
command: python app.py
directory: /opt/demo
webhook:
github:
secret: 123456
command: git pull origin master
```
## LICENSE
[MIT](LICENSE)

@ -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)

@ -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)
[MIT](LICENSE)

@ -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
}

@ -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
}

@ -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
}

Loading…
Cancel
Save