summaryrefslogtreecommitdiff
path: root/integration/system/event_test.go
blob: 4351c9bd29917962de62d685bdfd7a47e567342f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package system // import "github.com/docker/docker/integration/system"

import (
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"testing"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/events"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/api/types/strslice"
	"github.com/docker/docker/api/types/versions"
	"github.com/docker/docker/api/types/volume"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/pkg/jsonmessage"
	"github.com/docker/docker/testutil/request"
	req "github.com/docker/docker/testutil/request"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
	"gotest.tools/v3/skip"
)

func TestEventsExecDie(t *testing.T) {
	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.36"), "broken in earlier versions")
	skip.If(t, testEnv.OSType == "windows", "FIXME. Suspect may need to wait until container is running before exec")
	defer setupTest(t)()
	ctx := context.Background()
	client := testEnv.APIClient()

	cID := container.Run(ctx, t, client)

	id, err := client.ContainerExecCreate(ctx, cID,
		types.ExecConfig{
			Cmd: strslice.StrSlice([]string{"echo", "hello"}),
		},
	)
	assert.NilError(t, err)

	msg, errs := client.Events(ctx, types.EventsOptions{
		Filters: filters.NewArgs(
			filters.Arg("container", cID),
			filters.Arg("event", "exec_die"),
		),
	})

	err = client.ContainerExecStart(ctx, id.ID,
		types.ExecStartCheck{
			Detach: true,
			Tty:    false,
		},
	)
	assert.NilError(t, err)

	select {
	case m := <-msg:
		assert.Equal(t, m.Type, "container")
		assert.Equal(t, m.Actor.ID, cID)
		assert.Equal(t, m.Action, "exec_die")
		assert.Equal(t, m.Actor.Attributes["execID"], id.ID)
		assert.Equal(t, m.Actor.Attributes["exitCode"], "0")
	case err = <-errs:
		assert.NilError(t, err)
	case <-time.After(time.Second * 3):
		t.Fatal("timeout hit")
	}
}

// Test case for #18888: Events messages have been switched from generic
// `JSONMessage` to `events.Message` types. The switch does not break the
// backward compatibility so old `JSONMessage` could still be used.
// This test verifies that backward compatibility maintains.
func TestEventsBackwardsCompatible(t *testing.T) {
	skip.If(t, testEnv.OSType == "windows", "Windows doesn't support back-compat messages")
	defer setupTest(t)()
	ctx := context.Background()
	client := testEnv.APIClient()

	since := request.DaemonTime(ctx, t, client, testEnv)
	ts := strconv.FormatInt(since.Unix(), 10)

	cID := container.Create(ctx, t, client)

	// In case there is no events, the API should have responded immediately (not blocking),
	// The test here makes sure the response time is less than 3 sec.
	expectedTime := time.Now().Add(3 * time.Second)
	emptyResp, emptyBody, err := req.Get("/events")
	assert.NilError(t, err)
	defer emptyBody.Close()
	assert.Check(t, is.DeepEqual(http.StatusOK, emptyResp.StatusCode))
	assert.Check(t, time.Now().Before(expectedTime), "timeout waiting for events api to respond, should have responded immediately")

	// We also test to make sure the `events.Message` is compatible with `JSONMessage`
	q := url.Values{}
	q.Set("since", ts)
	_, body, err := req.Get("/events?" + q.Encode())
	assert.NilError(t, err)
	defer body.Close()

	dec := json.NewDecoder(body)
	var containerCreateEvent *jsonmessage.JSONMessage
	for {
		var event jsonmessage.JSONMessage
		if err := dec.Decode(&event); err != nil {
			if err == io.EOF {
				break
			}
			assert.NilError(t, err)
		}
		if event.Status == "create" && event.ID == cID {
			containerCreateEvent = &event
			break
		}
	}

	assert.Check(t, containerCreateEvent != nil)
	assert.Check(t, is.Equal("create", containerCreateEvent.Status))
	assert.Check(t, is.Equal(cID, containerCreateEvent.ID))
	assert.Check(t, is.Equal("busybox", containerCreateEvent.From))
}

// TestEventsVolumeCreate verifies that volume create events are only fired
// once: when creating the volume, and not when attaching to a container.
func TestEventsVolumeCreate(t *testing.T) {
	skip.If(t, testEnv.OSType == "windows", "FIXME: Windows doesn't trigger the events? Could be a race")

	defer setupTest(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	client := testEnv.APIClient()

	since := request.DaemonUnixTime(ctx, t, client, testEnv)
	volName := t.Name()
	getEvents := func(messages <-chan events.Message, errs <-chan error) ([]events.Message, error) {
		var evts []events.Message

		for {
			select {
			case m := <-messages:
				evts = append(evts, m)
			case err := <-errs:
				if err == io.EOF {
					return evts, nil
				}
				return nil, err
			case <-time.After(time.Second * 3):
				return nil, errors.New("timeout hit")
			}
		}
	}

	_, err := client.VolumeCreate(ctx, volume.CreateOptions{Name: volName})
	assert.NilError(t, err)

	filter := filters.NewArgs(
		filters.Arg("type", "volume"),
		filters.Arg("event", "create"),
		filters.Arg("volume", volName),
	)
	messages, errs := client.Events(ctx, types.EventsOptions{
		Since:   since,
		Until:   request.DaemonUnixTime(ctx, t, client, testEnv),
		Filters: filter,
	})

	volEvents, err := getEvents(messages, errs)
	assert.NilError(t, err)
	assert.Equal(t, len(volEvents), 1, "expected volume create event when creating a volume")

	container.Create(ctx, t, client, container.WithMount(mount.Mount{
		Type:   mount.TypeVolume,
		Source: volName,
		Target: "/tmp/foo",
	}))

	messages, errs = client.Events(ctx, types.EventsOptions{
		Since:   since,
		Until:   request.DaemonUnixTime(ctx, t, client, testEnv),
		Filters: filter,
	})

	volEvents, err = getEvents(messages, errs)
	assert.NilError(t, err)
	assert.Equal(t, len(volEvents), 1, "expected volume create event to be fired only once")
}