utils.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package container
  2. import (
  3. "fmt"
  4. "strconv"
  5. "golang.org/x/net/context"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/api/client"
  8. "github.com/docker/docker/api/client/system"
  9. clientapi "github.com/docker/engine-api/client"
  10. "github.com/docker/engine-api/types"
  11. "github.com/docker/engine-api/types/events"
  12. "github.com/docker/engine-api/types/filters"
  13. )
  14. func waitExitOrRemoved(dockerCli *client.DockerCli, ctx context.Context, containerID string, waitRemove bool) (chan int, error) {
  15. if len(containerID) == 0 {
  16. // containerID can never be empty
  17. panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
  18. }
  19. var exitCode int
  20. statusChan := make(chan int)
  21. exitChan := make(chan struct{})
  22. detachChan := make(chan struct{})
  23. destroyChan := make(chan struct{})
  24. eventsErr := make(chan error)
  25. // Start watch events
  26. eh := system.InitEventHandler()
  27. eh.Handle("die", func(e events.Message) {
  28. if len(e.Actor.Attributes) > 0 {
  29. for k, v := range e.Actor.Attributes {
  30. if k == "exitCode" {
  31. var err error
  32. if exitCode, err = strconv.Atoi(v); err != nil {
  33. logrus.Errorf("Can't convert %q to int: %v", v, err)
  34. }
  35. close(exitChan)
  36. break
  37. }
  38. }
  39. }
  40. })
  41. eh.Handle("detach", func(e events.Message) {
  42. exitCode = 0
  43. close(detachChan)
  44. })
  45. eh.Handle("destroy", func(e events.Message) {
  46. close(destroyChan)
  47. })
  48. eventChan := make(chan events.Message)
  49. go eh.Watch(eventChan)
  50. // Get events via Events API
  51. f := filters.NewArgs()
  52. f.Add("type", "container")
  53. f.Add("container", containerID)
  54. options := types.EventsOptions{
  55. Filters: f,
  56. }
  57. resBody, err := dockerCli.Client().Events(ctx, options)
  58. if err != nil {
  59. return nil, fmt.Errorf("can't get events from daemon: %v", err)
  60. }
  61. go func() {
  62. eventsErr <- system.DecodeEvents(resBody, func(event events.Message, err error) error {
  63. if err != nil {
  64. return fmt.Errorf("decode events error: %v", err)
  65. }
  66. eventChan <- event
  67. return nil
  68. })
  69. close(eventChan)
  70. }()
  71. go func() {
  72. var waitErr error
  73. if waitRemove {
  74. select {
  75. case <-destroyChan:
  76. // keep exitcode and return
  77. case <-detachChan:
  78. exitCode = 0
  79. case waitErr = <-eventsErr:
  80. exitCode = 125
  81. }
  82. } else {
  83. select {
  84. case <-exitChan:
  85. // keep exitcode and return
  86. case <-detachChan:
  87. exitCode = 0
  88. case waitErr = <-eventsErr:
  89. exitCode = 125
  90. }
  91. }
  92. if waitErr != nil {
  93. logrus.Errorf("%v", waitErr)
  94. }
  95. statusChan <- exitCode
  96. resBody.Close()
  97. }()
  98. return statusChan, nil
  99. }
  100. // getExitCode performs an inspect on the container. It returns
  101. // the running state and the exit code.
  102. func getExitCode(dockerCli *client.DockerCli, ctx context.Context, containerID string) (bool, int, error) {
  103. c, err := dockerCli.Client().ContainerInspect(ctx, containerID)
  104. if err != nil {
  105. // If we can't connect, then the daemon probably died.
  106. if err != clientapi.ErrConnectionFailed {
  107. return false, -1, err
  108. }
  109. return false, -1, nil
  110. }
  111. return c.State.Running, c.State.ExitCode, nil
  112. }