app.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package app
  2. import (
  3. "fmt"
  4. "os"
  5. "os/signal"
  6. "strconv"
  7. "strings"
  8. "syscall"
  9. "golang.org/x/net/context"
  10. "github.com/Sirupsen/logrus"
  11. "github.com/codegangsta/cli"
  12. "github.com/docker/libcompose/project"
  13. "github.com/docker/libcompose/project/options"
  14. )
  15. // ProjectAction is an adapter to allow the use of ordinary functions as libcompose actions.
  16. // Any function that has the appropriate signature can be register as an action on a codegansta/cli command.
  17. //
  18. // cli.Command{
  19. // Name: "ps",
  20. // Usage: "List containers",
  21. // Action: app.WithProject(factory, app.ProjectPs),
  22. // }
  23. type ProjectAction func(project project.APIProject, c *cli.Context) error
  24. // BeforeApp is an action that is executed before any cli command.
  25. func BeforeApp(c *cli.Context) error {
  26. if c.GlobalBool("verbose") {
  27. logrus.SetLevel(logrus.DebugLevel)
  28. }
  29. logrus.Warning("Note: This is an experimental alternate implementation of the Compose CLI (https://github.com/docker/compose)")
  30. return nil
  31. }
  32. // WithProject is a helper function to create a cli.Command action with a ProjectFactory.
  33. func WithProject(factory ProjectFactory, action ProjectAction) func(context *cli.Context) error {
  34. return func(context *cli.Context) error {
  35. p, err := factory.Create(context)
  36. if err != nil {
  37. logrus.Fatalf("Failed to read project: %v", err)
  38. }
  39. return action(p, context)
  40. }
  41. }
  42. // ProjectPs lists the containers.
  43. func ProjectPs(p project.APIProject, c *cli.Context) error {
  44. qFlag := c.Bool("q")
  45. allInfo, err := p.Ps(context.Background(), qFlag, c.Args()...)
  46. if err != nil {
  47. return cli.NewExitError(err.Error(), 1)
  48. }
  49. os.Stdout.WriteString(allInfo.String(!qFlag))
  50. return nil
  51. }
  52. // ProjectPort prints the public port for a port binding.
  53. func ProjectPort(p project.APIProject, c *cli.Context) error {
  54. if len(c.Args()) != 2 {
  55. return cli.NewExitError("Please pass arguments in the form: SERVICE PORT", 1)
  56. }
  57. index := c.Int("index")
  58. protocol := c.String("protocol")
  59. serviceName := c.Args()[0]
  60. privatePort := c.Args()[1]
  61. port, err := p.Port(context.Background(), index, protocol, serviceName, privatePort)
  62. if err != nil {
  63. return cli.NewExitError(err.Error(), 1)
  64. }
  65. fmt.Println(port)
  66. return nil
  67. }
  68. // ProjectStop stops all services.
  69. func ProjectStop(p project.APIProject, c *cli.Context) error {
  70. err := p.Stop(context.Background(), c.Int("timeout"), c.Args()...)
  71. if err != nil {
  72. return cli.NewExitError(err.Error(), 1)
  73. }
  74. return nil
  75. }
  76. // ProjectDown brings all services down (stops and clean containers).
  77. func ProjectDown(p project.APIProject, c *cli.Context) error {
  78. options := options.Down{
  79. RemoveVolume: c.Bool("volumes"),
  80. RemoveImages: options.ImageType(c.String("rmi")),
  81. RemoveOrphans: c.Bool("remove-orphans"),
  82. }
  83. err := p.Down(context.Background(), options, c.Args()...)
  84. if err != nil {
  85. return cli.NewExitError(err.Error(), 1)
  86. }
  87. return nil
  88. }
  89. // ProjectBuild builds or rebuilds services.
  90. func ProjectBuild(p project.APIProject, c *cli.Context) error {
  91. config := options.Build{
  92. NoCache: c.Bool("no-cache"),
  93. ForceRemove: c.Bool("force-rm"),
  94. Pull: c.Bool("pull"),
  95. }
  96. err := p.Build(context.Background(), config, c.Args()...)
  97. if err != nil {
  98. return cli.NewExitError(err.Error(), 1)
  99. }
  100. return nil
  101. }
  102. // ProjectCreate creates all services but do not start them.
  103. func ProjectCreate(p project.APIProject, c *cli.Context) error {
  104. options := options.Create{
  105. NoRecreate: c.Bool("no-recreate"),
  106. ForceRecreate: c.Bool("force-recreate"),
  107. NoBuild: c.Bool("no-build"),
  108. }
  109. err := p.Create(context.Background(), options, c.Args()...)
  110. if err != nil {
  111. return cli.NewExitError(err.Error(), 1)
  112. }
  113. return nil
  114. }
  115. // ProjectUp brings all services up.
  116. func ProjectUp(p project.APIProject, c *cli.Context) error {
  117. options := options.Up{
  118. Create: options.Create{
  119. NoRecreate: c.Bool("no-recreate"),
  120. ForceRecreate: c.Bool("force-recreate"),
  121. NoBuild: c.Bool("no-build"),
  122. },
  123. }
  124. ctx, cancelFun := context.WithCancel(context.Background())
  125. err := p.Up(ctx, options, c.Args()...)
  126. if err != nil {
  127. return cli.NewExitError(err.Error(), 1)
  128. }
  129. if !c.Bool("d") {
  130. signalChan := make(chan os.Signal, 1)
  131. cleanupDone := make(chan bool)
  132. signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
  133. errChan := make(chan error)
  134. go func() {
  135. errChan <- p.Log(ctx, true, c.Args()...)
  136. }()
  137. go func() {
  138. select {
  139. case <-signalChan:
  140. fmt.Printf("\nGracefully stopping...\n")
  141. cancelFun()
  142. ProjectStop(p, c)
  143. cleanupDone <- true
  144. case err := <-errChan:
  145. if err != nil {
  146. logrus.Fatal(err)
  147. }
  148. cleanupDone <- true
  149. }
  150. }()
  151. <-cleanupDone
  152. return nil
  153. }
  154. return nil
  155. }
  156. // ProjectRun runs a given command within a service's container.
  157. func ProjectRun(p project.APIProject, c *cli.Context) error {
  158. if len(c.Args()) == 1 {
  159. logrus.Fatal("No service specified")
  160. }
  161. serviceName := c.Args()[0]
  162. commandParts := c.Args()[1:]
  163. exitCode, err := p.Run(context.Background(), serviceName, commandParts)
  164. if err != nil {
  165. return cli.NewExitError(err.Error(), 1)
  166. }
  167. return cli.NewExitError("", exitCode)
  168. }
  169. // ProjectStart starts services.
  170. func ProjectStart(p project.APIProject, c *cli.Context) error {
  171. err := p.Start(context.Background(), c.Args()...)
  172. if err != nil {
  173. return cli.NewExitError(err.Error(), 1)
  174. }
  175. return nil
  176. }
  177. // ProjectRestart restarts services.
  178. func ProjectRestart(p project.APIProject, c *cli.Context) error {
  179. err := p.Restart(context.Background(), c.Int("timeout"), c.Args()...)
  180. if err != nil {
  181. return cli.NewExitError(err.Error(), 1)
  182. }
  183. return nil
  184. }
  185. // ProjectLog gets services logs.
  186. func ProjectLog(p project.APIProject, c *cli.Context) error {
  187. err := p.Log(context.Background(), c.Bool("follow"), c.Args()...)
  188. if err != nil {
  189. return cli.NewExitError(err.Error(), 1)
  190. }
  191. return nil
  192. }
  193. // ProjectPull pulls images for services.
  194. func ProjectPull(p project.APIProject, c *cli.Context) error {
  195. err := p.Pull(context.Background(), c.Args()...)
  196. if err != nil && !c.Bool("ignore-pull-failures") {
  197. return cli.NewExitError(err.Error(), 1)
  198. }
  199. return nil
  200. }
  201. // ProjectDelete deletes services.
  202. func ProjectDelete(p project.APIProject, c *cli.Context) error {
  203. options := options.Delete{
  204. RemoveVolume: c.Bool("v"),
  205. }
  206. if !c.Bool("force") {
  207. options.BeforeDeleteCallback = func(stoppedContainers []string) bool {
  208. fmt.Printf("Going to remove %v\nAre you sure? [yN]\n", strings.Join(stoppedContainers, ", "))
  209. var answer string
  210. _, err := fmt.Scanln(&answer)
  211. if err != nil {
  212. logrus.Error(err)
  213. return false
  214. }
  215. if answer != "y" && answer != "Y" {
  216. return false
  217. }
  218. return true
  219. }
  220. }
  221. err := p.Delete(context.Background(), options, c.Args()...)
  222. if err != nil {
  223. return cli.NewExitError(err.Error(), 1)
  224. }
  225. return nil
  226. }
  227. // ProjectKill forces stop service containers.
  228. func ProjectKill(p project.APIProject, c *cli.Context) error {
  229. err := p.Kill(context.Background(), c.String("signal"), c.Args()...)
  230. if err != nil {
  231. return cli.NewExitError(err.Error(), 1)
  232. }
  233. return nil
  234. }
  235. // ProjectPause pauses service containers.
  236. func ProjectPause(p project.APIProject, c *cli.Context) error {
  237. err := p.Pause(context.Background(), c.Args()...)
  238. if err != nil {
  239. return cli.NewExitError(err.Error(), 1)
  240. }
  241. return nil
  242. }
  243. // ProjectUnpause unpauses service containers.
  244. func ProjectUnpause(p project.APIProject, c *cli.Context) error {
  245. err := p.Unpause(context.Background(), c.Args()...)
  246. if err != nil {
  247. return cli.NewExitError(err.Error(), 1)
  248. }
  249. return nil
  250. }
  251. // ProjectScale scales services.
  252. func ProjectScale(p project.APIProject, c *cli.Context) error {
  253. servicesScale := map[string]int{}
  254. for _, arg := range c.Args() {
  255. kv := strings.SplitN(arg, "=", 2)
  256. if len(kv) != 2 {
  257. return cli.NewExitError(fmt.Sprintf("Invalid scale parameter: %s", arg), 2)
  258. }
  259. name := kv[0]
  260. count, err := strconv.Atoi(kv[1])
  261. if err != nil {
  262. return cli.NewExitError(fmt.Sprintf("Invalid scale parameter: %v", err), 2)
  263. }
  264. servicesScale[name] = count
  265. }
  266. err := p.Scale(context.Background(), c.Int("timeout"), servicesScale)
  267. if err != nil {
  268. return cli.NewExitError(err.Error(), 1)
  269. }
  270. return nil
  271. }