From 416a05367d39b05611274e9087341ca98df10dea Mon Sep 17 00:00:00 2001 From: codeskyblue Date: Tue, 30 Aug 2016 16:54:09 +0800 Subject: [PATCH] finish sub commands --- .fsw.yml | 2 +- build.sh | 2 +- fsm.go | 2 +- gosuv.go | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++------ web.go | 51 ++++++++++++++++++++++++++++---- 5 files changed, 128 insertions(+), 17 deletions(-) diff --git a/.fsw.yml b/.fsw.yml index 0f98874..1c84aaa 100644 --- a/.fsw.yml +++ b/.fsw.yml @@ -10,7 +10,7 @@ triggers: cmd: sh ./build.sh shell: true delay: 100ms - signal: KILL + signal: TERM watch_paths: - . watch_depth: 0 diff --git a/build.sh b/build.sh index cf1ff6f..c0b690e 100755 --- a/build.sh +++ b/build.sh @@ -1,3 +1,3 @@ #!/bin/bash - -go build && ./gosuv serv +go build && ./gosuv start-server -f diff --git a/fsm.go b/fsm.go index b2b85e8..fff8432 100644 --- a/fsm.go +++ b/fsm.go @@ -181,7 +181,7 @@ func (p *Process) startCommand() { p.Stdout.Reset() p.Stderr.Reset() p.Output.Reset() - log.Println("start cmd:", p.Name, p.Command) + log.Printf("start cmd(%s): %s", p.Name, p.Command) p.cmd = p.buildCommand() p.SetState(Running) diff --git a/gosuv.go b/gosuv.go index 20de7a9..d280127 100644 --- a/gosuv.go +++ b/gosuv.go @@ -1,7 +1,9 @@ package main import ( + "encoding/json" "fmt" + "io/ioutil" "log" "net/http" "os" @@ -13,15 +15,63 @@ var ( Version string = "dev" ) -func actionServ(c *cli.Context) error { - fmt.Println("added serv: ", c.Args().First()) - - log.Fatal(http.ListenAndServe(":8000", nil)) +func actionStartServer(c *cli.Context) error { + if err := registerHTTPHandlers(); err != nil { + return err + } + addr := c.String("address") + if c.Bool("foreground") { + fmt.Println("added serv: ", addr) + log.Fatal(http.ListenAndServe(addr, nil)) + } else { + log.Fatal("Not implement daemon mode") + } return nil } func actionStatus(c *cli.Context) error { - log.Println("Status") + resp, err := http.Get("http://localhost:8000/api/status") + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + var ret JSONResponse + err = json.Unmarshal(body, &ret) + if err != nil { + log.Fatal(err) + } + fmt.Println(ret.Value) + return nil +} + +func actionShutdown(c *cli.Context) error { + resp, err := http.Get("http://localhost:8000/api/shutdown") + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + var ret JSONResponse + err = json.Unmarshal(body, &ret) + if err != nil { + log.Fatal(err) + } + fmt.Println(ret.Value) + return nil +} + +func actionConfigTest(c *cli.Context) error { + if err := registerHTTPHandlers(); err != nil { + log.Fatal(err) + } + log.Println("test is successful") return nil } @@ -32,9 +82,20 @@ func main() { app.Usage = "golang port of python-supervisor" app.Commands = []cli.Command{ { - Name: "serv", - Usage: "Should only called by itself", - Action: actionServ, + Name: "start-server", + Usage: "Start supervisor and run in background", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "foreground, f", + Usage: "start in foreground", + }, + cli.StringFlag{ + Name: "address, addr", + Usage: "listen address", + Value: ":8000", + }, + }, + Action: actionStartServer, }, { Name: "status", @@ -42,6 +103,17 @@ func main() { Usage: "Show program status", Action: actionStatus, }, + { + Name: "shutdown", + Usage: "Shutdown server", + Action: actionShutdown, + }, + { + Name: "conftest", + Aliases: []string{"t"}, + Usage: "Test if config file is valid", + Action: actionConfigTest, + }, } if err := app.Run(os.Args); err != nil { os.Exit(1) diff --git a/web.go b/web.go index 40c2b35..ab2723c 100644 --- a/web.go +++ b/web.go @@ -181,6 +181,17 @@ func (s *Supervisor) renderHTML(w http.ResponseWriter, name string, data interfa t.ExecuteTemplate(w, baseName, data) } +type JSONResponse struct { + Status int `json:"status"` + Value interface{} `json:"value"` +} + +func (s *Supervisor) renderJSON(w http.ResponseWriter, data JSONResponse) { + w.Header().Set("Content-Type", "application/json") + bytes, _ := json.Marshal(data) + w.Write(bytes) +} + func (s *Supervisor) hIndex(w http.ResponseWriter, r *http.Request) { s.renderHTML(w, "./res/index.html", nil) } @@ -189,6 +200,27 @@ func (s *Supervisor) hSetting(w http.ResponseWriter, r *http.Request) { s.renderHTML(w, "./res/setting.html", nil) } +func (s *Supervisor) hStatus(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + data, _ := json.Marshal(map[string]interface{}{ + "status": 0, + "value": "server is running", + }) + w.Write(data) +} + +func (s *Supervisor) hShutdown(w http.ResponseWriter, r *http.Request) { + s.Close() + s.renderJSON(w, JSONResponse{ + Status: 0, + Value: "gosuv has been shutdown", + }) + go func() { + time.Sleep(500 * time.Millisecond) + os.Exit(0) + }() +} + func (s *Supervisor) hGetProgram(w http.ResponseWriter, r *http.Request) { procs := make([]*Process, 0, len(s.pgs)) for _, pg := range s.pgs { @@ -342,23 +374,27 @@ func (s *Supervisor) wsLog(w http.ResponseWriter, r *http.Request) { }) } +func (s *Supervisor) Close() { + for _, proc := range s.procMap { + s.stopAndWait(proc.Name) + } + fmt.Println("Supervisor closed") +} + func (s *Supervisor) catchExitSignal() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) go func() { sig := <-c fmt.Printf("Got signal: %v, stopping all running process\n", sig) - for _, proc := range s.procMap { - proc.stopCommand() - } - fmt.Println("Finished. Exit with code 0") + s.Close() os.Exit(0) }() } var defaultConfigDir = filepath.Join(UserHomeDir(), ".gosuv") -func init() { +func registerHTTPHandlers() error { suv := &Supervisor{ ConfigDir: defaultConfigDir, pgMap: make(map[string]*Program, 0), @@ -367,13 +403,15 @@ func init() { eventB: NewBroadcastString(), } if err := suv.loadDB(); err != nil { - log.Fatal(err) + return err } suv.catchExitSignal() r := mux.NewRouter() r.HandleFunc("/", suv.hIndex) r.HandleFunc("/settings/{name}", suv.hSetting) + r.HandleFunc("/api/status", suv.hStatus) + r.HandleFunc("/api/shutdown", suv.hShutdown) r.HandleFunc("/api/programs", suv.hGetProgram).Methods("GET") r.HandleFunc("/api/programs", suv.hAddProgram).Methods("POST") r.HandleFunc("/api/programs/{name}/start", suv.hStartProgram).Methods("POST") @@ -384,4 +422,5 @@ func init() { fs := http.FileServer(http.Dir("res")) http.Handle("/", r) http.Handle("/res/", http.StripPrefix("/res/", fs)) + return nil }