You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
7.7 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. package main
  2. import (
  3. "fmt"
  4. "github.com/codegangsta/cli"
  5. "io/ioutil"
  6. "net/url"
  7. "os"
  8. "regexp"
  9. "strings"
  10. "sync"
  11. acm "github.com/tendermint/tendermint/account"
  12. "github.com/tendermint/tendermint/binary"
  13. btypes "github.com/tendermint/tendermint/cmd/barak/types"
  14. . "github.com/tendermint/tendermint/common"
  15. cfg "github.com/tendermint/tendermint/config"
  16. )
  17. func remoteNick(remote string) string {
  18. u, err := url.Parse(remote)
  19. if err != nil {
  20. return regexp.MustCompile(`[[:^alnum:]]`).ReplaceAllString(remote, "_")
  21. } else {
  22. return regexp.MustCompile(`[[:^alnum:]]`).ReplaceAllString(u.Host, "_")
  23. }
  24. }
  25. var Config = struct {
  26. Remotes []string
  27. PrivKey acm.PrivKey
  28. }{}
  29. func main() {
  30. fmt.Printf("New Debora Process (PID: %d)\n", os.Getpid())
  31. // Apply bare tendermint/* configuration.
  32. cfg.ApplyConfig(cfg.MapConfig(map[string]interface{}{"log_level": "info"}))
  33. rootDir := os.Getenv("DEBROOT")
  34. if rootDir == "" {
  35. rootDir = os.Getenv("HOME") + "/.debora"
  36. }
  37. var (
  38. groupFlag = cli.StringFlag{
  39. Name: "group",
  40. Value: "default",
  41. Usage: "uses ~/.debora/<group>.cfg",
  42. }
  43. labelFlag = cli.StringFlag{
  44. Name: "label",
  45. Value: "_",
  46. Usage: "label of the process, or _ by default",
  47. }
  48. bgFlag = cli.BoolFlag{
  49. Name: "bg",
  50. Usage: "if set, runs as a background daemon",
  51. }
  52. inputFlag = cli.StringFlag{
  53. Name: "input",
  54. Value: "",
  55. Usage: "input to the program (e.g. stdin)",
  56. }
  57. )
  58. app := cli.NewApp()
  59. app.Name = "debora"
  60. app.Usage = "summons commands to barak"
  61. app.Version = "0.0.1"
  62. app.Email = "ethan@erisindustries.com,jae@tendermint.com"
  63. app.Flags = []cli.Flag{
  64. groupFlag,
  65. }
  66. app.Before = func(c *cli.Context) error {
  67. configFile := rootDir + "/" + c.String("group") + ".cfg"
  68. fmt.Printf("Using configuration from %v\n", configFile)
  69. ReadConfig(configFile)
  70. return nil
  71. }
  72. app.Commands = []cli.Command{
  73. cli.Command{
  74. Name: "status",
  75. Usage: "shows remote status",
  76. Action: cliGetStatus,
  77. },
  78. cli.Command{
  79. Name: "run",
  80. Usage: "run process",
  81. Action: cliStartProcess,
  82. Flags: []cli.Flag{
  83. labelFlag,
  84. bgFlag,
  85. inputFlag,
  86. },
  87. },
  88. cli.Command{
  89. Name: "stop",
  90. Usage: "stop process",
  91. Action: cliStopProcess,
  92. },
  93. cli.Command{
  94. Name: "list",
  95. Usage: "list processes",
  96. Action: cliListProcesses,
  97. },
  98. cli.Command{
  99. Name: "open",
  100. Usage: "open listener",
  101. Action: cliOpenListener,
  102. },
  103. cli.Command{
  104. Name: "close",
  105. Usage: "close listener",
  106. Action: cliCloseListener,
  107. },
  108. cli.Command{
  109. Name: "download",
  110. Usage: "download file <remote-path> <local-path-prefix>",
  111. Action: cliDownloadFile,
  112. },
  113. }
  114. app.Run(os.Args)
  115. }
  116. func ReadConfig(configFilePath string) {
  117. configJSONBytes, err := ioutil.ReadFile(configFilePath)
  118. if err != nil {
  119. Exit(Fmt("Failed to read config file %v. %v\n", configFilePath, err))
  120. }
  121. binary.ReadJSON(&Config, configJSONBytes, &err)
  122. if err != nil {
  123. Exit(Fmt("Failed to parse config. %v", err))
  124. }
  125. }
  126. func cliGetStatus(c *cli.Context) {
  127. args := c.Args()
  128. if len(args) != 0 {
  129. fmt.Println("BTW, status takes no arguments.")
  130. }
  131. wg := sync.WaitGroup{}
  132. for _, remote := range Config.Remotes {
  133. wg.Add(1)
  134. go func(remote string) {
  135. defer wg.Done()
  136. response, err := GetStatus(remote)
  137. if err != nil {
  138. fmt.Printf("%v failure. %v\n", remote, err)
  139. } else {
  140. fmt.Printf("%v success. %v\n", remote, response)
  141. }
  142. }(remote)
  143. }
  144. wg.Wait()
  145. }
  146. func cliStartProcess(c *cli.Context) {
  147. args := c.Args()
  148. if len(args) < 1 {
  149. Exit("Must specify <execPath> <args...>")
  150. }
  151. execPath := args[0]
  152. args = args[1:]
  153. command := btypes.CommandStartProcess{
  154. Wait: !c.Bool("bg"),
  155. Label: c.String("label"),
  156. ExecPath: execPath,
  157. Args: args,
  158. Input: c.String("input"),
  159. }
  160. wg := sync.WaitGroup{}
  161. for _, remote := range Config.Remotes {
  162. wg.Add(1)
  163. go func(remote string) {
  164. defer wg.Done()
  165. response, err := StartProcess(Config.PrivKey, remote, command)
  166. if err != nil {
  167. fmt.Printf("%v failure. %v\n", remote, err)
  168. } else {
  169. fmt.Printf("%v success.\n", remote)
  170. if response.Output != "" {
  171. fmt.Println("--------------------------------------------------------------------------------")
  172. fmt.Println(response.Output)
  173. fmt.Println("--------------------------------------------------------------------------------")
  174. } else {
  175. fmt.Println("(no output)")
  176. }
  177. }
  178. }(remote)
  179. }
  180. wg.Wait()
  181. }
  182. func cliStopProcess(c *cli.Context) {
  183. args := c.Args()
  184. if len(args) == 0 {
  185. Exit("Must specify label to stop")
  186. }
  187. label := args[0]
  188. command := btypes.CommandStopProcess{
  189. Label: label,
  190. Kill: true,
  191. }
  192. wg := sync.WaitGroup{}
  193. for _, remote := range Config.Remotes {
  194. wg.Add(1)
  195. go func(remote string) {
  196. defer wg.Done()
  197. response, err := StopProcess(Config.PrivKey, remote, command)
  198. if err != nil {
  199. fmt.Printf("%v failure. %v\n", remote, err)
  200. } else {
  201. fmt.Printf("%v success. %v\n", remote, response)
  202. }
  203. }(remote)
  204. }
  205. wg.Wait()
  206. }
  207. func cliListProcesses(c *cli.Context) {
  208. /*
  209. args := c.Args()
  210. if len(args) == 0 {
  211. log.Fatal("Must specify application name")
  212. }
  213. app := args[0]
  214. */
  215. command := btypes.CommandListProcesses{}
  216. wg := sync.WaitGroup{}
  217. for _, remote := range Config.Remotes {
  218. wg.Add(1)
  219. go func(remote string) {
  220. defer wg.Done()
  221. response, err := ListProcesses(Config.PrivKey, remote, command)
  222. if err != nil {
  223. fmt.Printf("%v failure. %v\n", Blue(remote), Red(err))
  224. } else {
  225. fmt.Printf("%v processes:\n", Blue(remote))
  226. for _, proc := range response.Processes {
  227. fmt.Printf(" \"%v\" => `%v %v` (%v)\n", Yellow(proc.Label), proc.ExecPath, strings.Join(proc.Args, ","), proc.Pid)
  228. fmt.Printf(" started at %v", proc.StartTime.String())
  229. if proc.EndTime.IsZero() {
  230. fmt.Printf(", running still\n")
  231. } else {
  232. endTimeStr := proc.EndTime.String()
  233. fmt.Printf(", stopped at %v\n", Yellow(endTimeStr))
  234. }
  235. fmt.Printf(" stdout/stderr goes to %v\n", proc.OutputPath)
  236. }
  237. }
  238. }(remote)
  239. }
  240. wg.Wait()
  241. }
  242. func cliOpenListener(c *cli.Context) {
  243. args := c.Args()
  244. if len(args) < 1 {
  245. Exit("Must specify <listenAddr e.g. [::]:46661>")
  246. }
  247. listenAddr := args[0]
  248. command := btypes.CommandOpenListener{
  249. Addr: listenAddr,
  250. }
  251. wg := sync.WaitGroup{}
  252. for _, remote := range Config.Remotes {
  253. wg.Add(1)
  254. go func(remote string) {
  255. defer wg.Done()
  256. response, err := OpenListener(Config.PrivKey, remote, command)
  257. if err != nil {
  258. fmt.Printf("%v failure. %v\n", remote, err)
  259. } else {
  260. fmt.Printf("%v opened %v.\n", remote, response.Addr)
  261. }
  262. }(remote)
  263. }
  264. wg.Wait()
  265. }
  266. func cliCloseListener(c *cli.Context) {
  267. args := c.Args()
  268. if len(args) == 0 {
  269. Exit("Must specify listenAddr to stop")
  270. }
  271. listenAddr := args[0]
  272. command := btypes.CommandCloseListener{
  273. Addr: listenAddr,
  274. }
  275. wg := sync.WaitGroup{}
  276. for _, remote := range Config.Remotes {
  277. wg.Add(1)
  278. go func(remote string) {
  279. defer wg.Done()
  280. response, err := CloseListener(Config.PrivKey, remote, command)
  281. if err != nil {
  282. fmt.Printf("%v failure. %v\n", remote, err)
  283. } else {
  284. fmt.Printf("%v success. %v\n", remote, response)
  285. }
  286. }(remote)
  287. }
  288. wg.Wait()
  289. }
  290. func cliDownloadFile(c *cli.Context) {
  291. args := c.Args()
  292. if len(args) != 2 {
  293. Exit("Must specify <remote-path> <local-path-prefix>")
  294. }
  295. remotePath := args[0]
  296. localPathPrefix := args[1]
  297. command := btypes.CommandServeFile{
  298. Path: remotePath,
  299. }
  300. wg := sync.WaitGroup{}
  301. for _, remote := range Config.Remotes {
  302. wg.Add(1)
  303. go func(remote string, localPath string) {
  304. defer wg.Done()
  305. n, err := DownloadFile(Config.PrivKey, remote, command, localPath)
  306. if err != nil {
  307. fmt.Printf("%v failure. %v\n", remote, err)
  308. } else {
  309. fmt.Printf("%v success. Wrote %v bytes to %v\n", remote, n, localPath)
  310. }
  311. }(remote, Fmt("%v_%v", localPathPrefix, remoteNick(remote)))
  312. }
  313. wg.Wait()
  314. }