tunasynctl.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "net/http"
  7. "os"
  8. "strings"
  9. "github.com/codegangsta/cli"
  10. "gopkg.in/op/go-logging.v1"
  11. tunasync "github.com/tuna/tunasync/internal"
  12. )
  13. const (
  14. listJobsPath = "/jobs"
  15. listWorkersPath = "/workers"
  16. cmdPath = "/cmd"
  17. )
  18. var logger = logging.MustGetLogger("tunasynctl-cmd")
  19. var baseURL string
  20. var client *http.Client
  21. func initializeWrapper(handler func(*cli.Context)) func(*cli.Context) {
  22. return func(c *cli.Context) {
  23. err := initialize(c)
  24. if err != nil {
  25. os.Exit(1)
  26. }
  27. handler(c)
  28. }
  29. }
  30. func initialize(c *cli.Context) error {
  31. // init logger
  32. tunasync.InitLogger(c.Bool("verbose"), c.Bool("verbose"), false)
  33. // parse manager server address
  34. baseURL = c.String("manager")
  35. if baseURL == "" {
  36. baseURL = "localhost"
  37. }
  38. managerPort := c.String("port")
  39. if managerPort != "" {
  40. baseURL += ":" + managerPort
  41. }
  42. if c.Bool("no-ssl") {
  43. baseURL = "http://" + baseURL
  44. } else {
  45. baseURL = "https://" + baseURL
  46. }
  47. logger.Info("Use manager address: %s", baseURL)
  48. // create HTTP client
  49. var err error
  50. client, err = tunasync.CreateHTTPClient(c.String("ca-cert"))
  51. if err != nil {
  52. err = fmt.Errorf("Error initializing HTTP client: %s", err.Error())
  53. logger.Error(err.Error())
  54. return err
  55. }
  56. return nil
  57. }
  58. func listWorkers(c *cli.Context) {
  59. var workers []tunasync.WorkerStatus
  60. _, err := tunasync.GetJSON(baseURL+listWorkersPath, &workers, client)
  61. if err != nil {
  62. logger.Error("Filed to correctly get informations from manager server: %s", err.Error())
  63. os.Exit(1)
  64. }
  65. b, err := json.MarshalIndent(workers, "", " ")
  66. if err != nil {
  67. logger.Error("Error printing out informations: %s", err.Error())
  68. }
  69. fmt.Print(string(b))
  70. }
  71. func listJobs(c *cli.Context) {
  72. // FIXME: there should be an API on manager server side that return MirrorStatus list to tunasynctl
  73. var jobs []tunasync.MirrorStatus
  74. if c.Bool("all") {
  75. _, err := tunasync.GetJSON(baseURL+listJobsPath, &jobs, client)
  76. if err != nil {
  77. logger.Error("Filed to correctly get information of all jobs from manager server: %s", err.Error())
  78. os.Exit(1)
  79. }
  80. } else {
  81. args := c.Args()
  82. if len(args) == 0 {
  83. logger.Error("Usage Error: jobs command need at least one arguments or \"--all\" flag.")
  84. os.Exit(1)
  85. }
  86. ans := make(chan []tunasync.MirrorStatus, len(args))
  87. for _, workerID := range args {
  88. go func(workerID string) {
  89. var workerJobs []tunasync.MirrorStatus
  90. _, err := tunasync.GetJSON(fmt.Sprintf("%s/workers/%s/jobs", baseURL, workerID), &workerJobs, client)
  91. if err != nil {
  92. logger.Error("Filed to correctly get jobs for worker %s: %s", workerID, err.Error())
  93. }
  94. ans <- workerJobs
  95. }(workerID)
  96. }
  97. for range args {
  98. jobs = append(jobs, <-ans...)
  99. }
  100. }
  101. b, err := json.MarshalIndent(jobs, "", " ")
  102. if err != nil {
  103. logger.Error("Error printing out informations: %s", err.Error())
  104. }
  105. fmt.Printf(string(b))
  106. }
  107. func cmdJob(cmd tunasync.CmdVerb) func(*cli.Context) {
  108. return func(c *cli.Context) {
  109. var mirrorID string
  110. var argsList []string
  111. if len(c.Args()) == 1 {
  112. mirrorID = c.Args()[0]
  113. } else if len(c.Args()) == 2 {
  114. mirrorID = c.Args()[0]
  115. for _, arg := range strings.Split(c.Args()[1], ",") {
  116. argsList = append(argsList, strings.TrimSpace(arg))
  117. }
  118. } else {
  119. logger.Error("Usage Error: cmd command receive just 1 required positional argument MIRROR and 1 optional ")
  120. os.Exit(1)
  121. }
  122. cmd := tunasync.ClientCmd{
  123. Cmd: cmd,
  124. MirrorID: mirrorID,
  125. WorkerID: c.String("worker"),
  126. Args: argsList,
  127. }
  128. resp, err := tunasync.PostJSON(baseURL+cmdPath, cmd, client)
  129. if err != nil {
  130. logger.Error("Failed to correctly send command: %s", err.Error)
  131. os.Exit(1)
  132. }
  133. defer resp.Body.Close()
  134. if resp.StatusCode != http.StatusOK {
  135. body, err := ioutil.ReadAll(resp.Body)
  136. if err != nil {
  137. logger.Error("Failed to parse response: %s", err.Error())
  138. }
  139. logger.Error("Failed to correctly send command: HTTP status code is not 200: %s", body)
  140. } else {
  141. logger.Info("Succesfully send command")
  142. }
  143. }
  144. }
  145. func main() {
  146. app := cli.NewApp()
  147. app.EnableBashCompletion = true
  148. app.Version = "0.1"
  149. commonFlags := []cli.Flag{
  150. cli.StringFlag{
  151. Name: "manager, m",
  152. Usage: "The manager server address",
  153. },
  154. cli.StringFlag{
  155. Name: "port, p",
  156. Usage: "The manager server port",
  157. },
  158. cli.StringFlag{
  159. Name: "ca-cert, c",
  160. Usage: "Trust CA cert `CERT`",
  161. },
  162. cli.BoolFlag{
  163. Name: "no-ssl",
  164. Usage: "Use http rather than https",
  165. },
  166. cli.BoolFlag{
  167. Name: "verbose, v",
  168. Usage: "Enable verbosely logging",
  169. },
  170. }
  171. cmdFlags := []cli.Flag{
  172. cli.StringFlag{
  173. Name: "worker, w",
  174. Usage: "Send the command to `WORKER`",
  175. },
  176. }
  177. app.Commands = []cli.Command{
  178. {
  179. Name: "list",
  180. Usage: "List jobs of workers",
  181. Flags: append(commonFlags,
  182. []cli.Flag{
  183. cli.BoolFlag{
  184. Name: "all, a",
  185. Usage: "List all jobs of all workers",
  186. },
  187. }...),
  188. Action: initializeWrapper(listJobs),
  189. },
  190. {
  191. Name: "workers",
  192. Usage: "List workers",
  193. Flags: commonFlags,
  194. Action: initializeWrapper(listWorkers),
  195. },
  196. {
  197. Name: "start",
  198. Usage: "Start a job",
  199. Flags: append(commonFlags, cmdFlags...),
  200. Action: initializeWrapper(cmdJob(tunasync.CmdStart)),
  201. },
  202. {
  203. Name: "stop",
  204. Usage: "Stop a job",
  205. Flags: append(commonFlags, cmdFlags...),
  206. Action: initializeWrapper(cmdJob(tunasync.CmdStop)),
  207. },
  208. {
  209. Name: "disable",
  210. Usage: "Disable a job",
  211. Flags: append(commonFlags, cmdFlags...),
  212. Action: initializeWrapper(cmdJob(tunasync.CmdDisable)),
  213. },
  214. {
  215. Name: "restart",
  216. Usage: "Restart a job",
  217. Flags: append(commonFlags, cmdFlags...),
  218. Action: initializeWrapper(cmdJob(tunasync.CmdRestart)),
  219. },
  220. {
  221. Name: "ping",
  222. Flags: append(commonFlags, cmdFlags...),
  223. Action: initializeWrapper(cmdJob(tunasync.CmdPing)),
  224. },
  225. }
  226. app.Run(os.Args)
  227. }