event_test.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package system // import "github.com/docker/docker/integration/system"
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "io"
  7. "net/http"
  8. "net/url"
  9. "strconv"
  10. "testing"
  11. "time"
  12. "github.com/docker/docker/api/types"
  13. "github.com/docker/docker/api/types/events"
  14. "github.com/docker/docker/api/types/filters"
  15. "github.com/docker/docker/api/types/mount"
  16. "github.com/docker/docker/api/types/strslice"
  17. "github.com/docker/docker/api/types/versions"
  18. "github.com/docker/docker/api/types/volume"
  19. "github.com/docker/docker/integration/internal/container"
  20. "github.com/docker/docker/pkg/jsonmessage"
  21. "github.com/docker/docker/testutil/request"
  22. req "github.com/docker/docker/testutil/request"
  23. "gotest.tools/v3/assert"
  24. is "gotest.tools/v3/assert/cmp"
  25. "gotest.tools/v3/skip"
  26. )
  27. func TestEventsExecDie(t *testing.T) {
  28. skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.36"), "broken in earlier versions")
  29. skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME. Suspect may need to wait until container is running before exec")
  30. defer setupTest(t)()
  31. ctx := context.Background()
  32. client := testEnv.APIClient()
  33. cID := container.Run(ctx, t, client)
  34. id, err := client.ContainerExecCreate(ctx, cID,
  35. types.ExecConfig{
  36. Cmd: strslice.StrSlice([]string{"echo", "hello"}),
  37. },
  38. )
  39. assert.NilError(t, err)
  40. msg, errs := client.Events(ctx, types.EventsOptions{
  41. Filters: filters.NewArgs(
  42. filters.Arg("container", cID),
  43. filters.Arg("event", "exec_die"),
  44. ),
  45. })
  46. err = client.ContainerExecStart(ctx, id.ID,
  47. types.ExecStartCheck{
  48. Detach: true,
  49. Tty: false,
  50. },
  51. )
  52. assert.NilError(t, err)
  53. select {
  54. case m := <-msg:
  55. assert.Equal(t, m.Type, "container")
  56. assert.Equal(t, m.Actor.ID, cID)
  57. assert.Equal(t, m.Action, "exec_die")
  58. assert.Equal(t, m.Actor.Attributes["execID"], id.ID)
  59. assert.Equal(t, m.Actor.Attributes["exitCode"], "0")
  60. case err = <-errs:
  61. assert.NilError(t, err)
  62. case <-time.After(time.Second * 3):
  63. t.Fatal("timeout hit")
  64. }
  65. }
  66. // Test case for #18888: Events messages have been switched from generic
  67. // `JSONMessage` to `events.Message` types. The switch does not break the
  68. // backward compatibility so old `JSONMessage` could still be used.
  69. // This test verifies that backward compatibility maintains.
  70. func TestEventsBackwardsCompatible(t *testing.T) {
  71. skip.If(t, testEnv.DaemonInfo.OSType == "windows", "Windows doesn't support back-compat messages")
  72. defer setupTest(t)()
  73. ctx := context.Background()
  74. client := testEnv.APIClient()
  75. since := request.DaemonTime(ctx, t, client, testEnv)
  76. ts := strconv.FormatInt(since.Unix(), 10)
  77. cID := container.Create(ctx, t, client)
  78. // In case there is no events, the API should have responded immediately (not blocking),
  79. // The test here makes sure the response time is less than 3 sec.
  80. expectedTime := time.Now().Add(3 * time.Second)
  81. emptyResp, emptyBody, err := req.Get("/events")
  82. assert.NilError(t, err)
  83. defer emptyBody.Close()
  84. assert.Check(t, is.DeepEqual(http.StatusOK, emptyResp.StatusCode))
  85. assert.Check(t, time.Now().Before(expectedTime), "timeout waiting for events api to respond, should have responded immediately")
  86. // We also test to make sure the `events.Message` is compatible with `JSONMessage`
  87. q := url.Values{}
  88. q.Set("since", ts)
  89. _, body, err := req.Get("/events?" + q.Encode())
  90. assert.NilError(t, err)
  91. defer body.Close()
  92. dec := json.NewDecoder(body)
  93. var containerCreateEvent *jsonmessage.JSONMessage
  94. for {
  95. var event jsonmessage.JSONMessage
  96. if err := dec.Decode(&event); err != nil {
  97. if err == io.EOF {
  98. break
  99. }
  100. assert.NilError(t, err)
  101. }
  102. if event.Status == "create" && event.ID == cID {
  103. containerCreateEvent = &event
  104. break
  105. }
  106. }
  107. assert.Check(t, containerCreateEvent != nil)
  108. assert.Check(t, is.Equal("create", containerCreateEvent.Status))
  109. assert.Check(t, is.Equal(cID, containerCreateEvent.ID))
  110. assert.Check(t, is.Equal("busybox", containerCreateEvent.From))
  111. }
  112. // TestEventsVolumeCreate verifies that volume create events are only fired
  113. // once: when creating the volume, and not when attaching to a container.
  114. func TestEventsVolumeCreate(t *testing.T) {
  115. skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: Windows doesn't trigger the events? Could be a race")
  116. defer setupTest(t)()
  117. ctx, cancel := context.WithCancel(context.Background())
  118. defer cancel()
  119. client := testEnv.APIClient()
  120. since := request.DaemonUnixTime(ctx, t, client, testEnv)
  121. volName := t.Name()
  122. getEvents := func(messages <-chan events.Message, errs <-chan error) ([]events.Message, error) {
  123. var evts []events.Message
  124. for {
  125. select {
  126. case m := <-messages:
  127. evts = append(evts, m)
  128. case err := <-errs:
  129. if err == io.EOF {
  130. return evts, nil
  131. }
  132. return nil, err
  133. case <-time.After(time.Second * 3):
  134. return nil, errors.New("timeout hit")
  135. }
  136. }
  137. }
  138. _, err := client.VolumeCreate(ctx, volume.CreateOptions{Name: volName})
  139. assert.NilError(t, err)
  140. filter := filters.NewArgs(
  141. filters.Arg("type", "volume"),
  142. filters.Arg("event", "create"),
  143. filters.Arg("volume", volName),
  144. )
  145. messages, errs := client.Events(ctx, types.EventsOptions{
  146. Since: since,
  147. Until: request.DaemonUnixTime(ctx, t, client, testEnv),
  148. Filters: filter,
  149. })
  150. volEvents, err := getEvents(messages, errs)
  151. assert.NilError(t, err)
  152. assert.Equal(t, len(volEvents), 1, "expected volume create event when creating a volume")
  153. container.Create(ctx, t, client, container.WithMount(mount.Mount{
  154. Type: mount.TypeVolume,
  155. Source: volName,
  156. Target: "/tmp/foo",
  157. }))
  158. messages, errs = client.Events(ctx, types.EventsOptions{
  159. Since: since,
  160. Until: request.DaemonUnixTime(ctx, t, client, testEnv),
  161. Filters: filter,
  162. })
  163. volEvents, err = getEvents(messages, errs)
  164. assert.NilError(t, err)
  165. assert.Equal(t, len(volEvents), 1, "expected volume create event to be fired only once")
  166. }