package main import ( "fmt" "net/http" "os" "os/exec" "path" "strconv" "github.com/filecoin-project/lotus/lib/jsonrpc" "gopkg.in/urfave/cli.v2" ) const listenAddr = "127.0.0.1:2222" type runningNode struct { cmd *exec.Cmd meta nodeInfo mux *outmux stop func() } var onCmd = &cli.Command{ Name: "on", Usage: "run a command on a given node", Action: func(cctx *cli.Context) error { client, err := apiClient() if err != nil { return err } nd, err := strconv.ParseInt(cctx.Args().Get(0), 10, 32) if err != nil { return err } node := nodeById(client.Nodes(), int(nd)) var cmd *exec.Cmd if !node.Storage { cmd = exec.Command("./lotus", cctx.Args().Slice()[1:]...) cmd.Env = []string{ "LOTUS_PATH=" + node.Repo, } } else { cmd = exec.Command("./lotus-storage-miner") cmd.Env = []string{ "LOTUS_STORAGE_PATH=" + node.Repo, "LOTUS_PATH=" + node.FullNode, } } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() return err }, } var shCmd = &cli.Command{ Name: "sh", Usage: "spawn shell with node shell variables set", Action: func(cctx *cli.Context) error { client, err := apiClient() if err != nil { return err } nd, err := strconv.ParseInt(cctx.Args().Get(0), 10, 32) if err != nil { return err } node := nodeById(client.Nodes(), int(nd)) shcmd := exec.Command("/bin/bash") if !node.Storage { shcmd.Env = []string{ "LOTUS_PATH=" + node.Repo, } } else { shcmd.Env = []string{ "LOTUS_STORAGE_PATH=" + node.Repo, "LOTUS_PATH=" + node.FullNode, } } shcmd.Stdin = os.Stdin shcmd.Stdout = os.Stdout shcmd.Stderr = os.Stderr fmt.Printf("Entering shell for Node %d\n", nd) err = shcmd.Run() fmt.Printf("Closed pond shell\n") return err }, } func nodeById(nodes []nodeInfo, i int) nodeInfo { for _, n := range nodes { if n.ID == int32(i) { return n } } panic("no node with this id") } func logHandler(api *api) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, req *http.Request) { id, err := strconv.ParseInt(path.Base(req.URL.Path), 10, 32) if err != nil { panic(err) return } api.runningLk.Lock() n := api.running[int32(id)] api.runningLk.Unlock() n.mux.ServeHTTP(w, req) } } var runCmd = &cli.Command{ Name: "run", Usage: "run lotuspond daemon", Action: func(cctx *cli.Context) error { rpcServer := jsonrpc.NewServer() a := &api{running: map[int32]*runningNode{}} rpcServer.Register("Pond", a) http.Handle("/", http.FileServer(http.Dir("lotuspond/front/build"))) http.HandleFunc("/app/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "lotuspond/front/build/index.html") }) http.Handle("/rpc/v0", rpcServer) http.HandleFunc("/logs/", logHandler(a)) fmt.Printf("Listening on http://%s\n", listenAddr) return http.ListenAndServe(listenAddr, nil) }, } func main() { app := &cli.App{ Name: "pond", Commands: []*cli.Command{ runCmd, shCmd, onCmd, }, } if err := app.Run(os.Args); err != nil { panic(err) } }