forgot chanages ...

master
shengxiang 9 years ago
parent 98dcff54bb
commit 0b1c5e6b7a

@ -6,6 +6,7 @@ import (
"net/http"
"os"
"os/exec"
"path/filepath"
"strconv"
"time"
@ -64,7 +65,24 @@ func ServAction(ctx *cli.Context) {
}
func StatusAction(ctx *cli.Context) {
println("status todo")
programs := make([]*Program, 0)
res, err := goreq.Request{
Method: "GET",
Uri: buildURI(ctx, "/api/programs"),
}.Do()
if err != nil {
log.Fatal(err)
}
if res.StatusCode != http.StatusOK {
log.Fatal(res.Body.ToString())
}
if err = res.Body.FromJsonTo(&programs); err != nil {
log.Fatal(err)
}
fmt.Printf("%10s\t%s\n", "NAME", "STATUS")
for _, p := range programs {
fmt.Printf("%10s\t%s\n", p.Info.Name, p.Status)
}
}
func AddAction(ctx *cli.Context) {
@ -170,9 +188,10 @@ func init() {
Action: wrapAction(VersionAction),
},
{
Name: "status",
Usage: "show program status",
Action: StatusAction,
Name: "status",
Aliases: []string{"st"},
Usage: "show program status",
Action: StatusAction,
},
{
Name: "add",
@ -207,13 +226,14 @@ func init() {
}
}
const (
GOSUV_HOME = "$HOME/.gosuv"
GOSUV_CONFIG = "gosuv.json"
var (
GOSUV_HOME = os.ExpandEnv("$HOME/.gosuv")
GOSUV_CONFIG = filepath.Join(GOSUV_HOME, "gosuv.json")
GOSUV_VERSION = "0.0.1"
)
func main() {
MkdirIfNoExists(GOSUV_HOME)
app.HideHelp = false
app.RunAndExitOnError()
}

@ -0,0 +1,142 @@
package main
import (
"errors"
"io"
"os"
"os/exec"
"path/filepath"
"sync"
"github.com/codeskyblue/kproc"
"github.com/qiniu/log"
)
func GoFunc(f func() error) chan error {
ch := make(chan error)
go func() {
ch <- f()
}()
return ch
}
const (
ST_PENDING = "pending"
ST_RUNNING = "running"
ST_STOPPED = "stopped"
ST_FATAL = "fatal"
)
type Program struct {
*kproc.Process `json:"-"`
Status string `json:"state"`
Sig chan os.Signal `json:"-"`
Info *ProgramInfo `json:"info"`
}
func NewProgram(cmd *exec.Cmd, info *ProgramInfo) *Program {
return &Program{
Process: kproc.ProcCommand(cmd),
Status: ST_PENDING,
Sig: make(chan os.Signal),
Info: info,
}
}
func (p *Program) createLog() (*os.File, error) {
logDir := os.ExpandEnv("$HOME/.gosuv/logs")
os.MkdirAll(logDir, 0755) // just do it, err ignore it
logFile := filepath.Join(logDir, p.Info.Name+".output.log")
return os.Create(logFile)
}
func (p *Program) Run() error {
if err := p.Start(); err != nil {
p.Status = ST_FATAL
return err
}
return p.Wait()
}
func (p *Program) Start() error {
logFd, err := p.createLog()
if err != nil {
return err
}
p.Cmd.Stdout = logFd
p.Cmd.Stderr = logFd
return p.Cmd.Start()
}
// wait func finish, also accept signal
func (p *Program) Wait() (err error) {
log.Println("Wait program to finish")
p.Status = ST_RUNNING
defer func() {
if out, ok := p.Cmd.Stdout.(io.Closer); ok {
out.Close()
}
if err != nil {
log.Warnf("program finish: %v", err)
p.Status = ST_FATAL
} else {
p.Status = ST_STOPPED
}
}()
ch := GoFunc(p.Cmd.Wait)
for {
select {
case err = <-ch:
return err
case sig := <-p.Sig:
p.Terminate(sig)
}
}
}
type ProgramInfo struct {
Name string `json:"name"`
Command []string `json:"command"`
Dir string `json:"dir"`
Environ []string `json:"environ"`
}
var programTable *ProgramTable
func init() {
programTable = &ProgramTable{
table: make(map[string]*Program, 10),
ch: make(chan string),
}
}
type ProgramTable struct {
table map[string]*Program
ch chan string
mu sync.Mutex
}
var (
ErrProgramDuplicate = errors.New("program duplicate")
)
func (pt *ProgramTable) AddProgram(p *Program) error {
pt.mu.Lock()
defer pt.mu.Unlock()
name := p.Info.Name
if _, exists := pt.table[name]; exists {
return ErrProgramDuplicate
}
pt.table[name] = p
return nil
}
func (pt *ProgramTable) Programs() []*Program {
pt.mu.Lock()
defer pt.mu.Unlock()
ps := make([]*Program, 0, len(pt.table))
for _, p := range pt.table {
ps = append(ps, p)
}
return ps
}

@ -6,10 +6,8 @@ import (
"net/http"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/codeskyblue/kproc"
"github.com/gorilla/mux"
"github.com/qiniu/log"
)
@ -19,17 +17,6 @@ type JSONResponse struct {
Message string `json:"message"`
}
type ProgramInfo struct {
Name string `json:"name"`
Command []string `json:"command"`
Dir string `json:"dir"`
Environ []string `json:"environ"`
}
var programTable struct {
table map[string]*Program
}
func renderJSON(w http.ResponseWriter, v interface{}) {
w.Header().Add("Content-Type", "json")
json.NewEncoder(w).Encode(v)
@ -42,6 +29,22 @@ func versionHandler(w http.ResponseWriter, r *http.Request) {
})
}
func buildProgram(pinfo *ProgramInfo) (*Program, error) {
// init cmd
cmd := exec.Command(pinfo.Command[0], pinfo.Command[1:]...)
cmd.Dir = pinfo.Dir
cmd.Env = append(os.Environ(), pinfo.Environ...)
program := NewProgram(cmd, pinfo)
// set output
return program, nil
}
func statusHandler(w http.ResponseWriter, r *http.Request) {
prms := programTable.Programs()
renderJSON(w, prms)
}
func addHandler(w http.ResponseWriter, r *http.Request) {
pinfo := new(ProgramInfo)
err := json.NewDecoder(r.Body).Decode(pinfo)
@ -51,86 +54,31 @@ func addHandler(w http.ResponseWriter, r *http.Request) {
}
log.Printf("add: %#v", pinfo)
// init cmd
cmd := exec.Command(pinfo.Command[0], pinfo.Command[1:]...)
cmd.Dir = pinfo.Dir
cmd.Env = append(os.Environ(), pinfo.Environ...)
program := NewProgram(cmd, pinfo)
// set output
logFd, err := program.createLog()
program, err := buildProgram(pinfo)
if err != nil {
http.Error(w, err.Error(), 503)
http.Error(w, err.Error(), 502)
return
}
cmd.Stdout = logFd
cmd.Stderr = logFd
if err = program.Start(); err != nil {
if err = programTable.AddProgram(program); err != nil {
http.Error(w, err.Error(), 503)
return
}
program.Status = ST_RUNNING
// wait func finish
go func() {
finish := false
ch := GoFunc(program.Wait)
for !finish {
select {
case err := <-ch:
if err != nil {
log.Warnf("program finish: %v", err)
}
finish = true
case sig := <-program.Sig:
program.Terminate(sig)
}
go program.Run()
/*
if err = program.Start(); err != nil {
http.Error(w, err.Error(), 503)
return
}
}()
go program.Wait()
*/
renderJSON(w, &JSONResponse{
Code: 200,
Message: "program add success",
})
}
func GoFunc(f func() error) chan error {
ch := make(chan error)
go func() {
ch <- f()
}()
return ch
}
const (
ST_PENDING = "pending"
ST_RUNNING = "running"
ST_STOPPED = "stopped"
)
type Program struct {
*kproc.Process
Status string `json:"state"`
Sig chan os.Signal
info *ProgramInfo
}
func NewProgram(cmd *exec.Cmd, info *ProgramInfo) *Program {
return &Program{
Process: kproc.ProcCommand(cmd),
Status: ST_PENDING,
Sig: make(chan os.Signal),
info: info,
}
}
func (p *Program) createLog() (*os.File, error) {
logDir := os.ExpandEnv("$HOME/.gosuv/logs")
os.MkdirAll(logDir, 0755) // just do it, err ignore it
logFile := filepath.Join(logDir, p.info.Name+".output.log")
return os.Create(logFile)
}
func shutdownHandler(w http.ResponseWriter, r *http.Request) {
go func() {
time.Sleep(50 * time.Millisecond)
@ -147,5 +95,6 @@ func ServeAddr(host string, port int) error {
r.HandleFunc("/api/version", versionHandler)
r.Methods("POST").Path("/api/shutdown").HandlerFunc(shutdownHandler)
r.Methods("POST").Path("/api/programs").HandlerFunc(addHandler)
r.Methods("GET").Path("/api/programs").HandlerFunc(statusHandler)
return http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), r)
}

Loading…
Cancel
Save