Browse code

Get events until a time in the past.

This change allow to filter events that happened in the past
without waiting for future events. Example:

docker events --since -1h --until -30m

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2016/04/12 03:52:34
Showing 12 changed files
... ...
@@ -1,6 +1,8 @@
1 1
 package system
2 2
 
3 3
 import (
4
+	"time"
5
+
4 6
 	"github.com/docker/engine-api/types"
5 7
 	"github.com/docker/engine-api/types/events"
6 8
 	"github.com/docker/engine-api/types/filters"
... ...
@@ -12,7 +14,7 @@ import (
12 12
 type Backend interface {
13 13
 	SystemInfo() (*types.Info, error)
14 14
 	SystemVersion() types.Version
15
-	SubscribeToEvents(since, sinceNano int64, ef filters.Args) ([]events.Message, chan interface{})
15
+	SubscribeToEvents(since, until time.Time, ef filters.Args) ([]events.Message, chan interface{})
16 16
 	UnsubscribeFromEvents(chan interface{})
17 17
 	AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error)
18 18
 }
... ...
@@ -2,12 +2,14 @@ package system
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"fmt"
5 6
 	"net/http"
6 7
 	"time"
7 8
 
8 9
 	"github.com/Sirupsen/logrus"
9 10
 	"github.com/docker/docker/api"
10 11
 	"github.com/docker/docker/api/server/httputils"
12
+	"github.com/docker/docker/errors"
11 13
 	"github.com/docker/docker/pkg/ioutils"
12 14
 	"github.com/docker/engine-api/types"
13 15
 	"github.com/docker/engine-api/types/events"
... ...
@@ -46,19 +48,33 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
46 46
 	if err := httputils.ParseForm(r); err != nil {
47 47
 		return err
48 48
 	}
49
-	since, sinceNano, err := timetypes.ParseTimestamps(r.Form.Get("since"), -1)
49
+
50
+	since, err := eventTime(r.Form.Get("since"))
50 51
 	if err != nil {
51 52
 		return err
52 53
 	}
53
-	until, untilNano, err := timetypes.ParseTimestamps(r.Form.Get("until"), -1)
54
+	until, err := eventTime(r.Form.Get("until"))
54 55
 	if err != nil {
55 56
 		return err
56 57
 	}
57 58
 
58
-	var timeout <-chan time.Time
59
-	if until > 0 || untilNano > 0 {
60
-		dur := time.Unix(until, untilNano).Sub(time.Now())
61
-		timeout = time.NewTimer(dur).C
59
+	var (
60
+		timeout        <-chan time.Time
61
+		onlyPastEvents bool
62
+	)
63
+	if !until.IsZero() {
64
+		if until.Before(since) {
65
+			return errors.NewBadRequestError(fmt.Errorf("`since` time (%s) cannot be after `until` time (%s)", r.Form.Get("since"), r.Form.Get("until")))
66
+		}
67
+
68
+		now := time.Now()
69
+
70
+		onlyPastEvents = until.Before(now)
71
+
72
+		if !onlyPastEvents {
73
+			dur := until.Sub(now)
74
+			timeout = time.NewTimer(dur).C
75
+		}
62 76
 	}
63 77
 
64 78
 	ef, err := filters.FromParam(r.Form.Get("filters"))
... ...
@@ -73,7 +89,7 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
73 73
 
74 74
 	enc := json.NewEncoder(output)
75 75
 
76
-	buffered, l := s.backend.SubscribeToEvents(since, sinceNano, ef)
76
+	buffered, l := s.backend.SubscribeToEvents(since, until, ef)
77 77
 	defer s.backend.UnsubscribeFromEvents(l)
78 78
 
79 79
 	for _, ev := range buffered {
... ...
@@ -82,6 +98,10 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
82 82
 		}
83 83
 	}
84 84
 
85
+	if onlyPastEvents {
86
+		return nil
87
+	}
88
+
85 89
 	for {
86 90
 		select {
87 91
 		case ev := <-l:
... ...
@@ -118,3 +138,14 @@ func (s *systemRouter) postAuth(ctx context.Context, w http.ResponseWriter, r *h
118 118
 		IdentityToken: token,
119 119
 	})
120 120
 }
121
+
122
+func eventTime(formTime string) (time.Time, error) {
123
+	t, tNano, err := timetypes.ParseTimestamps(formTime, -1)
124
+	if err != nil {
125
+		return time.Time{}, err
126
+	}
127
+	if t == -1 {
128
+		return time.Time{}, nil
129
+	}
130
+	return time.Unix(t, tNano), nil
131
+}
... ...
@@ -29,8 +29,6 @@ import (
29 29
 	"github.com/docker/docker/errors"
30 30
 	"github.com/docker/engine-api/types"
31 31
 	containertypes "github.com/docker/engine-api/types/container"
32
-	eventtypes "github.com/docker/engine-api/types/events"
33
-	"github.com/docker/engine-api/types/filters"
34 32
 	networktypes "github.com/docker/engine-api/types/network"
35 33
 	registrytypes "github.com/docker/engine-api/types/registry"
36 34
 	"github.com/docker/engine-api/types/strslice"
... ...
@@ -547,18 +545,6 @@ func (daemon *Daemon) GetByName(name string) (*container.Container, error) {
547 547
 	return e, nil
548 548
 }
549 549
 
550
-// SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
551
-func (daemon *Daemon) SubscribeToEvents(since, sinceNano int64, filter filters.Args) ([]eventtypes.Message, chan interface{}) {
552
-	ef := events.NewFilter(filter)
553
-	return daemon.EventsService.SubscribeTopic(since, sinceNano, ef)
554
-}
555
-
556
-// UnsubscribeFromEvents stops the event subscription for a client by closing the
557
-// channel where the daemon sends events to.
558
-func (daemon *Daemon) UnsubscribeFromEvents(listener chan interface{}) {
559
-	daemon.EventsService.Evict(listener)
560
-}
561
-
562 550
 // GetLabels for a container or image id
563 551
 func (daemon *Daemon) GetLabels(id string) map[string]string {
564 552
 	// TODO: TestCase
... ...
@@ -2,9 +2,12 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"strings"
5
+	"time"
5 6
 
6 7
 	"github.com/docker/docker/container"
8
+	daemonevents "github.com/docker/docker/daemon/events"
7 9
 	"github.com/docker/engine-api/types/events"
10
+	"github.com/docker/engine-api/types/filters"
8 11
 	"github.com/docker/libnetwork"
9 12
 )
10 13
 
... ...
@@ -77,6 +80,18 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, actio
77 77
 	daemon.EventsService.Log(action, events.NetworkEventType, actor)
78 78
 }
79 79
 
80
+// SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
81
+func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
82
+	ef := daemonevents.NewFilter(filter)
83
+	return daemon.EventsService.SubscribeTopic(since, until, ef)
84
+}
85
+
86
+// UnsubscribeFromEvents stops the event subscription for a client by closing the
87
+// channel where the daemon sends events to.
88
+func (daemon *Daemon) UnsubscribeFromEvents(listener chan interface{}) {
89
+	daemon.EventsService.Evict(listener)
90
+}
91
+
80 92
 // copyAttributes guarantees that labels are not mutated by event triggers.
81 93
 func copyAttributes(attributes, labels map[string]string) {
82 94
 	if labels == nil {
... ...
@@ -48,7 +48,7 @@ func (e *Events) Subscribe() ([]eventtypes.Message, chan interface{}, func()) {
48 48
 // SubscribeTopic adds new listener to events, returns slice of 64 stored
49 49
 // last events, a channel in which you can expect new events (in form
50 50
 // of interface{}, so you need type assertion).
51
-func (e *Events) SubscribeTopic(since, sinceNano int64, ef *Filter) ([]eventtypes.Message, chan interface{}) {
51
+func (e *Events) SubscribeTopic(since, until time.Time, ef *Filter) ([]eventtypes.Message, chan interface{}) {
52 52
 	e.mu.Lock()
53 53
 
54 54
 	var topic func(m interface{}) bool
... ...
@@ -56,7 +56,7 @@ func (e *Events) SubscribeTopic(since, sinceNano int64, ef *Filter) ([]eventtype
56 56
 		topic = func(m interface{}) bool { return ef.Include(m.(eventtypes.Message)) }
57 57
 	}
58 58
 
59
-	buffered := e.loadBufferedEvents(since, sinceNano, topic)
59
+	buffered := e.loadBufferedEvents(since, until, topic)
60 60
 
61 61
 	var ch chan interface{}
62 62
 	if topic != nil {
... ...
@@ -116,24 +116,36 @@ func (e *Events) SubscribersCount() int {
116 116
 }
117 117
 
118 118
 // loadBufferedEvents iterates over the cached events in the buffer
119
-// and returns those that were emitted before a specific date.
120
-// The date is splitted in two values:
121
-//   - the `since` argument is a date timestamp without nanoseconds, or -1 to return an empty slice.
122
-//   - the `sinceNano` argument is the nanoseconds offset from the timestamp.
123
-// It uses `time.Unix(seconds, nanoseconds)` to generate a valid date with those two first arguments.
119
+// and returns those that were emitted between two specific dates.
120
+// It uses `time.Unix(seconds, nanoseconds)` to generate valid dates with those arguments.
124 121
 // It filters those buffered messages with a topic function if it's not nil, otherwise it adds all messages.
125
-func (e *Events) loadBufferedEvents(since, sinceNano int64, topic func(interface{}) bool) []eventtypes.Message {
122
+func (e *Events) loadBufferedEvents(since, until time.Time, topic func(interface{}) bool) []eventtypes.Message {
126 123
 	var buffered []eventtypes.Message
127
-	if since == -1 {
124
+	if since.IsZero() && until.IsZero() {
128 125
 		return buffered
129 126
 	}
130 127
 
131
-	sinceNanoUnix := time.Unix(since, sinceNano).UnixNano()
128
+	var sinceNanoUnix int64
129
+	if !since.IsZero() {
130
+		sinceNanoUnix = since.UnixNano()
131
+	}
132
+
133
+	var untilNanoUnix int64
134
+	if !until.IsZero() {
135
+		untilNanoUnix = until.UnixNano()
136
+	}
137
+
132 138
 	for i := len(e.events) - 1; i >= 0; i-- {
133 139
 		ev := e.events[i]
140
+
134 141
 		if ev.TimeNano < sinceNanoUnix {
135 142
 			break
136 143
 		}
144
+
145
+		if untilNanoUnix > 0 && ev.TimeNano > untilNanoUnix {
146
+			continue
147
+		}
148
+
137 149
 		if topic == nil || topic(ev) {
138 150
 			buffered = append([]eventtypes.Message{ev}, buffered...)
139 151
 		}
... ...
@@ -165,7 +165,7 @@ func TestLoadBufferedEvents(t *testing.T) {
165 165
 	if err != nil {
166 166
 		t.Fatal(err)
167 167
 	}
168
-	since, sinceNano, err := timetypes.ParseTimestamps(f, -1)
168
+	s, sNano, err := timetypes.ParseTimestamps(f, -1)
169 169
 	if err != nil {
170 170
 		t.Fatal(err)
171 171
 	}
... ...
@@ -183,14 +183,93 @@ func TestLoadBufferedEvents(t *testing.T) {
183 183
 		t.Fatal(err)
184 184
 	}
185 185
 
186
-	buffered := []events.Message{*m1, *m2, *m3}
186
+	events := &Events{
187
+		events: []events.Message{*m1, *m2, *m3},
188
+	}
189
+
190
+	since := time.Unix(s, sNano)
191
+	until := time.Time{}
192
+
193
+	out := events.loadBufferedEvents(since, until, nil)
194
+	if len(out) != 1 {
195
+		t.Fatalf("expected 1 message, got %d: %v", len(out), out)
196
+	}
197
+}
198
+
199
+func TestLoadBufferedEventsOnlyFromPast(t *testing.T) {
200
+	now := time.Now()
201
+	f, err := timetypes.GetTimestamp("2016-03-07T17:28:03.090000000+02:00", now)
202
+	if err != nil {
203
+		t.Fatal(err)
204
+	}
205
+	s, sNano, err := timetypes.ParseTimestamps(f, 0)
206
+	if err != nil {
207
+		t.Fatal(err)
208
+	}
209
+
210
+	f, err = timetypes.GetTimestamp("2016-03-07T17:28:03.100000000+02:00", now)
211
+	if err != nil {
212
+		t.Fatal(err)
213
+	}
214
+	u, uNano, err := timetypes.ParseTimestamps(f, 0)
215
+	if err != nil {
216
+		t.Fatal(err)
217
+	}
218
+
219
+	m1, err := eventstestutils.Scan("2016-03-07T17:28:03.022433271+02:00 container die 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover)")
220
+	if err != nil {
221
+		t.Fatal(err)
222
+	}
223
+	m2, err := eventstestutils.Scan("2016-03-07T17:28:03.091719377+02:00 network disconnect 19c5ed41acb798f26b751e0035cd7821741ab79e2bbd59a66b5fd8abf954eaa0 (type=bridge, container=0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079, name=bridge)")
224
+	if err != nil {
225
+		t.Fatal(err)
226
+	}
227
+	m3, err := eventstestutils.Scan("2016-03-07T17:28:03.129014751+02:00 container destroy 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover)")
228
+	if err != nil {
229
+		t.Fatal(err)
230
+	}
187 231
 
188 232
 	events := &Events{
189
-		events: buffered,
233
+		events: []events.Message{*m1, *m2, *m3},
190 234
 	}
191 235
 
192
-	out := events.loadBufferedEvents(since, sinceNano, nil)
236
+	since := time.Unix(s, sNano)
237
+	until := time.Unix(u, uNano)
238
+
239
+	out := events.loadBufferedEvents(since, until, nil)
193 240
 	if len(out) != 1 {
194 241
 		t.Fatalf("expected 1 message, got %d: %v", len(out), out)
195 242
 	}
243
+
244
+	if out[0].Type != "network" {
245
+		t.Fatalf("expected network event, got %s", out[0].Type)
246
+	}
247
+}
248
+
249
+// #13753
250
+func TestIngoreBufferedWhenNoTimes(t *testing.T) {
251
+	m1, err := eventstestutils.Scan("2016-03-07T17:28:03.022433271+02:00 container die 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover)")
252
+	if err != nil {
253
+		t.Fatal(err)
254
+	}
255
+	m2, err := eventstestutils.Scan("2016-03-07T17:28:03.091719377+02:00 network disconnect 19c5ed41acb798f26b751e0035cd7821741ab79e2bbd59a66b5fd8abf954eaa0 (type=bridge, container=0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079, name=bridge)")
256
+	if err != nil {
257
+		t.Fatal(err)
258
+	}
259
+	m3, err := eventstestutils.Scan("2016-03-07T17:28:03.129014751+02:00 container destroy 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover)")
260
+	if err != nil {
261
+		t.Fatal(err)
262
+	}
263
+
264
+	events := &Events{
265
+		events: []events.Message{*m1, *m2, *m3},
266
+	}
267
+
268
+	since := time.Time{}
269
+	until := time.Time{}
270
+
271
+	out := events.loadBufferedEvents(since, until, nil)
272
+	if len(out) != 0 {
273
+		t.Fatalf("expected 0 buffered events, got %q", out)
274
+	}
196 275
 }
... ...
@@ -6450,7 +6450,7 @@ func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) {
6450 6450
 }
6451 6451
 
6452 6452
 func (s *DockerSuite) TestBuildTagEvent(c *check.C) {
6453
-	since := daemonTime(c).Unix()
6453
+	since := daemonUnixTime(c)
6454 6454
 
6455 6455
 	dockerFile := `FROM busybox
6456 6456
 	RUN echo events
... ...
@@ -6458,7 +6458,8 @@ func (s *DockerSuite) TestBuildTagEvent(c *check.C) {
6458 6458
 	_, err := buildImage("test", dockerFile, false)
6459 6459
 	c.Assert(err, check.IsNil)
6460 6460
 
6461
-	out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "type=image")
6461
+	until := daemonUnixTime(c)
6462
+	out, _ := dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "type=image")
6462 6463
 	events := strings.Split(strings.TrimSpace(out), "\n")
6463 6464
 	actions := eventActionsByIDAndType(c, events, "test:latest", "image")
6464 6465
 	var foundTag bool
... ...
@@ -7,7 +7,6 @@ import (
7 7
 	"net/http"
8 8
 	"os"
9 9
 	"os/exec"
10
-	"strconv"
11 10
 	"strings"
12 11
 	"sync"
13 12
 	"time"
... ...
@@ -50,7 +49,6 @@ func (s *DockerSuite) TestEventsTimestampFormats(c *check.C) {
50 50
 		c.Assert(containerEvents[3], checker.Equals, "die", check.Commentf(out))
51 51
 		c.Assert(containerEvents[4], checker.Equals, "destroy", check.Commentf(out))
52 52
 	}
53
-
54 53
 }
55 54
 
56 55
 func (s *DockerSuite) TestEventsUntag(c *check.C) {
... ...
@@ -77,7 +75,7 @@ func (s *DockerSuite) TestEventsContainerFailStartDie(c *check.C) {
77 77
 	_, _, err := dockerCmdWithError("run", "--name", "testeventdie", "busybox", "blerg")
78 78
 	c.Assert(err, checker.NotNil, check.Commentf("Container run with command blerg should have failed, but it did not"))
79 79
 
80
-	out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
80
+	out, _ := dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c))
81 81
 	events := strings.Split(strings.TrimSpace(out), "\n")
82 82
 
83 83
 	nEvents := len(events)
... ...
@@ -128,17 +126,16 @@ func (s *DockerSuite) TestEventsLimit(c *check.C) {
128 128
 		c.Assert(err, checker.IsNil, check.Commentf("%q failed with error", strings.Join(args, " ")))
129 129
 	}
130 130
 
131
-	out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
131
+	out, _ := dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c))
132 132
 	events := strings.Split(out, "\n")
133 133
 	nEvents := len(events) - 1
134 134
 	c.Assert(nEvents, checker.Equals, 64, check.Commentf("events should be limited to 64, but received %d", nEvents))
135 135
 }
136 136
 
137 137
 func (s *DockerSuite) TestEventsContainerEvents(c *check.C) {
138
-	containerID, _ := dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true")
139
-	containerID = strings.TrimSpace(containerID)
138
+	dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true")
140 139
 
141
-	out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
140
+	out, _ := dockerCmd(c, "events", "--until", daemonUnixTime(c))
142 141
 	events := strings.Split(out, "\n")
143 142
 	events = events[:len(events)-1]
144 143
 
... ...
@@ -155,11 +152,10 @@ func (s *DockerSuite) TestEventsContainerEvents(c *check.C) {
155 155
 }
156 156
 
157 157
 func (s *DockerSuite) TestEventsContainerEventsAttrSort(c *check.C) {
158
-	since := daemonTime(c).Unix()
159
-	containerID, _ := dockerCmd(c, "run", "-d", "--name", "container-events-test", "busybox", "true")
160
-	containerID = strings.TrimSpace(containerID)
158
+	since := daemonUnixTime(c)
159
+	dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true")
161 160
 
162
-	out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
161
+	out, _ := dockerCmd(c, "events", "--filter", "container=container-events-test", "--since", since, "--until", daemonUnixTime(c))
163 162
 	events := strings.Split(out, "\n")
164 163
 
165 164
 	nEvents := len(events)
... ...
@@ -167,9 +163,6 @@ func (s *DockerSuite) TestEventsContainerEventsAttrSort(c *check.C) {
167 167
 	matchedEvents := 0
168 168
 	for _, event := range events {
169 169
 		matches := eventstestutils.ScanMap(event)
170
-		if matches["id"] != containerID {
171
-			continue
172
-		}
173 170
 		if matches["eventType"] == "container" && matches["action"] == "create" {
174 171
 			matchedEvents++
175 172
 			c.Assert(out, checker.Contains, "(image=busybox, name=container-events-test)", check.Commentf("Event attributes not sorted"))
... ...
@@ -178,14 +171,14 @@ func (s *DockerSuite) TestEventsContainerEventsAttrSort(c *check.C) {
178 178
 			c.Assert(out, checker.Contains, "(image=busybox, name=container-events-test)", check.Commentf("Event attributes not sorted"))
179 179
 		}
180 180
 	}
181
-	c.Assert(matchedEvents, checker.Equals, 2)
181
+	c.Assert(matchedEvents, checker.Equals, 2, check.Commentf("missing events for container container-events-test:\n%s", out))
182 182
 }
183 183
 
184 184
 func (s *DockerSuite) TestEventsContainerEventsSinceUnixEpoch(c *check.C) {
185 185
 	dockerCmd(c, "run", "--rm", "--name", "since-epoch-test", "busybox", "true")
186 186
 	timeBeginning := time.Unix(0, 0).Format(time.RFC3339Nano)
187 187
 	timeBeginning = strings.Replace(timeBeginning, "Z", ".000000000Z", -1)
188
-	out, _ := dockerCmd(c, "events", fmt.Sprintf("--since='%s'", timeBeginning), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
188
+	out, _ := dockerCmd(c, "events", "--since", timeBeginning, "--until", daemonUnixTime(c))
189 189
 	events := strings.Split(out, "\n")
190 190
 	events = events[:len(events)-1]
191 191
 
... ...
@@ -203,13 +196,12 @@ func (s *DockerSuite) TestEventsContainerEventsSinceUnixEpoch(c *check.C) {
203 203
 
204 204
 func (s *DockerSuite) TestEventsImageTag(c *check.C) {
205 205
 	time.Sleep(1 * time.Second) // because API has seconds granularity
206
-	since := daemonTime(c).Unix()
206
+	since := daemonUnixTime(c)
207 207
 	image := "testimageevents:tag"
208 208
 	dockerCmd(c, "tag", "busybox", image)
209 209
 
210 210
 	out, _ := dockerCmd(c, "events",
211
-		fmt.Sprintf("--since=%d", since),
212
-		fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
211
+		"--since", since, "--until", daemonUnixTime(c))
213 212
 
214 213
 	events := strings.Split(strings.TrimSpace(out), "\n")
215 214
 	c.Assert(events, checker.HasLen, 1, check.Commentf("was expecting 1 event. out=%s", out))
... ...
@@ -223,14 +215,13 @@ func (s *DockerSuite) TestEventsImageTag(c *check.C) {
223 223
 func (s *DockerSuite) TestEventsImagePull(c *check.C) {
224 224
 	// TODO Windows: Enable this test once pull and reliable image names are available
225 225
 	testRequires(c, DaemonIsLinux)
226
-	since := daemonTime(c).Unix()
226
+	since := daemonUnixTime(c)
227 227
 	testRequires(c, Network)
228 228
 
229 229
 	dockerCmd(c, "pull", "hello-world")
230 230
 
231 231
 	out, _ := dockerCmd(c, "events",
232
-		fmt.Sprintf("--since=%d", since),
233
-		fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
232
+		"--since", since, "--until", daemonUnixTime(c))
234 233
 
235 234
 	events := strings.Split(strings.TrimSpace(out), "\n")
236 235
 	event := strings.TrimSpace(events[len(events)-1])
... ...
@@ -248,7 +239,7 @@ func (s *DockerSuite) TestEventsImageImport(c *check.C) {
248 248
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
249 249
 	cleanedContainerID := strings.TrimSpace(out)
250 250
 
251
-	since := daemonTime(c).Unix()
251
+	since := daemonUnixTime(c)
252 252
 	out, _, err := runCommandPipelineWithOutput(
253 253
 		exec.Command(dockerBinary, "export", cleanedContainerID),
254 254
 		exec.Command(dockerBinary, "import", "-"),
... ...
@@ -256,7 +247,7 @@ func (s *DockerSuite) TestEventsImageImport(c *check.C) {
256 256
 	c.Assert(err, checker.IsNil, check.Commentf("import failed with output: %q", out))
257 257
 	imageRef := strings.TrimSpace(out)
258 258
 
259
-	out, _ = dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "event=import")
259
+	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=import")
260 260
 	events := strings.Split(strings.TrimSpace(out), "\n")
261 261
 	c.Assert(events, checker.HasLen, 1)
262 262
 	matches := eventstestutils.ScanMap(events[0])
... ...
@@ -265,13 +256,13 @@ func (s *DockerSuite) TestEventsImageImport(c *check.C) {
265 265
 }
266 266
 
267 267
 func (s *DockerSuite) TestEventsFilters(c *check.C) {
268
-	since := daemonTime(c).Unix()
268
+	since := daemonUnixTime(c)
269 269
 	dockerCmd(c, "run", "--rm", "busybox", "true")
270 270
 	dockerCmd(c, "run", "--rm", "busybox", "true")
271
-	out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "event=die")
271
+	out, _ := dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=die")
272 272
 	parseEvents(c, out, "die")
273 273
 
274
-	out, _ = dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "event=die", "--filter", "event=start")
274
+	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=die", "--filter", "event=start")
275 275
 	parseEvents(c, out, "die|start")
276 276
 
277 277
 	// make sure we at least got 2 start events
... ...
@@ -281,7 +272,7 @@ func (s *DockerSuite) TestEventsFilters(c *check.C) {
281 281
 }
282 282
 
283 283
 func (s *DockerSuite) TestEventsFilterImageName(c *check.C) {
284
-	since := daemonTime(c).Unix()
284
+	since := daemonUnixTime(c)
285 285
 
286 286
 	out, _ := dockerCmd(c, "run", "--name", "container_1", "-d", "busybox:latest", "true")
287 287
 	container1 := strings.TrimSpace(out)
... ...
@@ -290,7 +281,7 @@ func (s *DockerSuite) TestEventsFilterImageName(c *check.C) {
290 290
 	container2 := strings.TrimSpace(out)
291 291
 
292 292
 	name := "busybox"
293
-	out, _ = dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", fmt.Sprintf("image=%s", name))
293
+	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("image=%s", name))
294 294
 	events := strings.Split(out, "\n")
295 295
 	events = events[:len(events)-1]
296 296
 	c.Assert(events, checker.Not(checker.HasLen), 0) //Expected events but found none for the image busybox:latest
... ...
@@ -310,7 +301,7 @@ func (s *DockerSuite) TestEventsFilterImageName(c *check.C) {
310 310
 }
311 311
 
312 312
 func (s *DockerSuite) TestEventsFilterLabels(c *check.C) {
313
-	since := daemonTime(c).Unix()
313
+	since := daemonUnixTime(c)
314 314
 	label := "io.docker.testing=foo"
315 315
 
316 316
 	out, _ := dockerCmd(c, "run", "-d", "-l", label, "busybox:latest", "true")
... ...
@@ -322,8 +313,8 @@ func (s *DockerSuite) TestEventsFilterLabels(c *check.C) {
322 322
 	out, _ = dockerCmd(
323 323
 		c,
324 324
 		"events",
325
-		fmt.Sprintf("--since=%d", since),
326
-		fmt.Sprintf("--until=%d", daemonTime(c).Unix()),
325
+		"--since", since,
326
+		"--until", daemonUnixTime(c),
327 327
 		"--filter", fmt.Sprintf("label=%s", label))
328 328
 
329 329
 	events := strings.Split(strings.TrimSpace(out), "\n")
... ...
@@ -336,7 +327,7 @@ func (s *DockerSuite) TestEventsFilterLabels(c *check.C) {
336 336
 }
337 337
 
338 338
 func (s *DockerSuite) TestEventsFilterImageLabels(c *check.C) {
339
-	since := daemonTime(c).Unix()
339
+	since := daemonUnixTime(c)
340 340
 	name := "labelfiltertest"
341 341
 	label := "io.docker.testing=image"
342 342
 
... ...
@@ -353,8 +344,8 @@ func (s *DockerSuite) TestEventsFilterImageLabels(c *check.C) {
353 353
 	out, _ := dockerCmd(
354 354
 		c,
355 355
 		"events",
356
-		fmt.Sprintf("--since=%d", since),
357
-		fmt.Sprintf("--until=%d", daemonTime(c).Unix()),
356
+		"--since", since,
357
+		"--until", daemonUnixTime(c),
358 358
 		"--filter", fmt.Sprintf("label=%s", label),
359 359
 		"--filter", "type=image")
360 360
 
... ...
@@ -368,7 +359,7 @@ func (s *DockerSuite) TestEventsFilterImageLabels(c *check.C) {
368 368
 }
369 369
 
370 370
 func (s *DockerSuite) TestEventsFilterContainer(c *check.C) {
371
-	since := fmt.Sprintf("%d", daemonTime(c).Unix())
371
+	since := daemonUnixTime(c)
372 372
 	nameID := make(map[string]string)
373 373
 
374 374
 	for _, name := range []string{"container_1", "container_2"} {
... ...
@@ -377,7 +368,7 @@ func (s *DockerSuite) TestEventsFilterContainer(c *check.C) {
377 377
 		nameID[name] = id
378 378
 	}
379 379
 
380
-	until := fmt.Sprintf("%d", daemonTime(c).Unix())
380
+	until := daemonUnixTime(c)
381 381
 
382 382
 	checkEvents := func(id string, events []string) error {
383 383
 		if len(events) != 4 { // create, attach, start, die
... ...
@@ -408,22 +399,21 @@ func (s *DockerSuite) TestEventsFilterContainer(c *check.C) {
408 408
 func (s *DockerSuite) TestEventsCommit(c *check.C) {
409 409
 	// Problematic on Windows as cannot commit a running container
410 410
 	testRequires(c, DaemonIsLinux)
411
-	since := daemonTime(c).Unix()
412 411
 
413
-	out, _ := runSleepingContainer(c, "-d")
412
+	out, _ := runSleepingContainer(c)
414 413
 	cID := strings.TrimSpace(out)
415 414
 	c.Assert(waitRun(cID), checker.IsNil)
416 415
 
417 416
 	dockerCmd(c, "commit", "-m", "test", cID)
418 417
 	dockerCmd(c, "stop", cID)
418
+	c.Assert(waitExited(cID, 5*time.Second), checker.IsNil)
419 419
 
420
-	out, _ = dockerCmd(c, "events", "--since=0", "-f", "container="+cID, "--until="+strconv.Itoa(int(since)))
420
+	until := daemonUnixTime(c)
421
+	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until="+until)
421 422
 	c.Assert(out, checker.Contains, "commit", check.Commentf("Missing 'commit' log event"))
422 423
 }
423 424
 
424 425
 func (s *DockerSuite) TestEventsCopy(c *check.C) {
425
-	since := daemonTime(c).Unix()
426
-
427 426
 	// Build a test image.
428 427
 	id, err := buildImage("cpimg", `
429 428
 		  FROM busybox
... ...
@@ -441,18 +431,18 @@ func (s *DockerSuite) TestEventsCopy(c *check.C) {
441 441
 
442 442
 	dockerCmd(c, "cp", "cptest:/file", tempFile.Name())
443 443
 
444
-	out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=cptest", "--until="+strconv.Itoa(int(since)))
444
+	until := daemonUnixTime(c)
445
+	out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=cptest", "--until="+until)
445 446
 	c.Assert(out, checker.Contains, "archive-path", check.Commentf("Missing 'archive-path' log event\n"))
446 447
 
447 448
 	dockerCmd(c, "cp", tempFile.Name(), "cptest:/filecopy")
448 449
 
449
-	out, _ = dockerCmd(c, "events", "--since=0", "-f", "container=cptest", "--until="+strconv.Itoa(int(since)))
450
+	until = daemonUnixTime(c)
451
+	out, _ = dockerCmd(c, "events", "-f", "container=cptest", "--until="+until)
450 452
 	c.Assert(out, checker.Contains, "extract-to-dir", check.Commentf("Missing 'extract-to-dir' log event"))
451 453
 }
452 454
 
453 455
 func (s *DockerSuite) TestEventsResize(c *check.C) {
454
-	since := daemonTime(c).Unix()
455
-
456 456
 	out, _ := runSleepingContainer(c, "-d")
457 457
 	cID := strings.TrimSpace(out)
458 458
 	c.Assert(waitRun(cID), checker.IsNil)
... ...
@@ -464,17 +454,18 @@ func (s *DockerSuite) TestEventsResize(c *check.C) {
464 464
 
465 465
 	dockerCmd(c, "stop", cID)
466 466
 
467
-	out, _ = dockerCmd(c, "events", "--since=0", "-f", "container="+cID, "--until="+strconv.Itoa(int(since)))
467
+	until := daemonUnixTime(c)
468
+	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until="+until)
468 469
 	c.Assert(out, checker.Contains, "resize", check.Commentf("Missing 'resize' log event"))
469 470
 }
470 471
 
471 472
 func (s *DockerSuite) TestEventsAttach(c *check.C) {
472 473
 	// TODO Windows CI: Figure out why this test fails intermittently (TP4 and TP5).
473 474
 	testRequires(c, DaemonIsLinux)
474
-	since := daemonTime(c).Unix()
475 475
 
476 476
 	out, _ := dockerCmd(c, "run", "-di", "busybox", "cat")
477 477
 	cID := strings.TrimSpace(out)
478
+	c.Assert(waitRun(cID), checker.IsNil)
478 479
 
479 480
 	cmd := exec.Command(dockerBinary, "attach", cID)
480 481
 	stdin, err := cmd.StdinPipe()
... ...
@@ -496,25 +487,27 @@ func (s *DockerSuite) TestEventsAttach(c *check.C) {
496 496
 	c.Assert(stdin.Close(), checker.IsNil)
497 497
 
498 498
 	dockerCmd(c, "kill", cID)
499
+	c.Assert(waitExited(cID, 5*time.Second), checker.IsNil)
499 500
 
500
-	out, _ = dockerCmd(c, "events", "--since=0", "-f", "container="+cID, "--until="+strconv.Itoa(int(since)))
501
+	until := daemonUnixTime(c)
502
+	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until="+until)
501 503
 	c.Assert(out, checker.Contains, "attach", check.Commentf("Missing 'attach' log event"))
502 504
 }
503 505
 
504 506
 func (s *DockerSuite) TestEventsRename(c *check.C) {
505
-	since := daemonTime(c).Unix()
506
-
507
-	dockerCmd(c, "run", "--name", "oldName", "busybox", "true")
507
+	out, _ := dockerCmd(c, "run", "--name", "oldName", "busybox", "true")
508
+	cID := strings.TrimSpace(out)
508 509
 	dockerCmd(c, "rename", "oldName", "newName")
509 510
 
510
-	out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=newName", "--until="+strconv.Itoa(int(since)))
511
+	until := daemonUnixTime(c)
512
+	// filter by the container id because the name in the event will be the new name.
513
+	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until", until)
511 514
 	c.Assert(out, checker.Contains, "rename", check.Commentf("Missing 'rename' log event\n"))
512 515
 }
513 516
 
514 517
 func (s *DockerSuite) TestEventsTop(c *check.C) {
515 518
 	// Problematic on Windows as Windows does not support top
516 519
 	testRequires(c, DaemonIsLinux)
517
-	since := daemonTime(c).Unix()
518 520
 
519 521
 	out, _ := runSleepingContainer(c, "-d")
520 522
 	cID := strings.TrimSpace(out)
... ...
@@ -523,24 +516,17 @@ func (s *DockerSuite) TestEventsTop(c *check.C) {
523 523
 	dockerCmd(c, "top", cID)
524 524
 	dockerCmd(c, "stop", cID)
525 525
 
526
-	out, _ = dockerCmd(c, "events", "--since=0", "-f", "container="+cID, "--until="+strconv.Itoa(int(since)))
526
+	until := daemonUnixTime(c)
527
+	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until="+until)
527 528
 	c.Assert(out, checker.Contains, " top", check.Commentf("Missing 'top' log event"))
528 529
 }
529 530
 
530
-// #13753
531
-func (s *DockerSuite) TestEventsDefaultEmpty(c *check.C) {
532
-	dockerCmd(c, "run", "busybox")
533
-	out, _ := dockerCmd(c, "events", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
534
-	c.Assert(strings.TrimSpace(out), checker.Equals, "")
535
-}
536
-
537 531
 // #14316
538 532
 func (s *DockerRegistrySuite) TestEventsImageFilterPush(c *check.C) {
539 533
 	// Problematic to port for Windows CI during TP4/TP5 timeframe while
540 534
 	// not supporting push
541 535
 	testRequires(c, DaemonIsLinux)
542 536
 	testRequires(c, Network)
543
-	since := daemonTime(c).Unix()
544 537
 	repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL)
545 538
 
546 539
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
... ...
@@ -551,12 +537,13 @@ func (s *DockerRegistrySuite) TestEventsImageFilterPush(c *check.C) {
551 551
 	dockerCmd(c, "stop", cID)
552 552
 	dockerCmd(c, "push", repoName)
553 553
 
554
-	out, _ = dockerCmd(c, "events", "--since=0", "-f", "image="+repoName, "-f", "event=push", "--until="+strconv.Itoa(int(since)))
554
+	until := daemonUnixTime(c)
555
+	out, _ = dockerCmd(c, "events", "-f", "image="+repoName, "-f", "event=push", "--until", until)
555 556
 	c.Assert(out, checker.Contains, repoName, check.Commentf("Missing 'push' log event for %s", repoName))
556 557
 }
557 558
 
558 559
 func (s *DockerSuite) TestEventsFilterType(c *check.C) {
559
-	since := daemonTime(c).Unix()
560
+	since := daemonUnixTime(c)
560 561
 	name := "labelfiltertest"
561 562
 	label := "io.docker.testing=image"
562 563
 
... ...
@@ -573,8 +560,8 @@ func (s *DockerSuite) TestEventsFilterType(c *check.C) {
573 573
 	out, _ := dockerCmd(
574 574
 		c,
575 575
 		"events",
576
-		fmt.Sprintf("--since=%d", since),
577
-		fmt.Sprintf("--until=%d", daemonTime(c).Unix()),
576
+		"--since", since,
577
+		"--until", daemonUnixTime(c),
578 578
 		"--filter", fmt.Sprintf("label=%s", label),
579 579
 		"--filter", "type=image")
580 580
 
... ...
@@ -589,8 +576,8 @@ func (s *DockerSuite) TestEventsFilterType(c *check.C) {
589 589
 	out, _ = dockerCmd(
590 590
 		c,
591 591
 		"events",
592
-		fmt.Sprintf("--since=%d", since),
593
-		fmt.Sprintf("--until=%d", daemonTime(c).Unix()),
592
+		"--since", since,
593
+		"--until", daemonUnixTime(c),
594 594
 		"--filter", fmt.Sprintf("label=%s", label),
595 595
 		"--filter", "type=container")
596 596
 	events = strings.Split(strings.TrimSpace(out), "\n")
... ...
@@ -601,19 +588,19 @@ func (s *DockerSuite) TestEventsFilterType(c *check.C) {
601 601
 	out, _ = dockerCmd(
602 602
 		c,
603 603
 		"events",
604
-		fmt.Sprintf("--since=%d", since),
605
-		fmt.Sprintf("--until=%d", daemonTime(c).Unix()),
604
+		"--since", since,
605
+		"--until", daemonUnixTime(c),
606 606
 		"--filter", "type=network")
607 607
 	events = strings.Split(strings.TrimSpace(out), "\n")
608 608
 	c.Assert(len(events), checker.GreaterOrEqualThan, 1, check.Commentf("Events == %s", events))
609 609
 }
610 610
 
611 611
 func (s *DockerSuite) TestEventsFilterImageInContainerAction(c *check.C) {
612
-	since := daemonTime(c).Unix()
612
+	since := daemonUnixTime(c)
613 613
 	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
614 614
 	waitRun("test-container")
615 615
 
616
-	out, _ := dockerCmd(c, "events", "--filter", "image=busybox", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
616
+	out, _ := dockerCmd(c, "events", "--filter", "image=busybox", "--since", since, "--until", daemonUnixTime(c))
617 617
 	events := strings.Split(strings.TrimSpace(out), "\n")
618 618
 	c.Assert(len(events), checker.GreaterThan, 1, check.Commentf(out))
619 619
 }
... ...
@@ -636,7 +623,7 @@ func (s *DockerSuite) TestEventsContainerRestart(c *check.C) {
636 636
 		startCount  int
637 637
 		dieCount    int
638 638
 	)
639
-	out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "-f", "container=testEvent")
639
+	out, _ := dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c), "-f", "container=testEvent")
640 640
 	events := strings.Split(strings.TrimSpace(out), "\n")
641 641
 
642 642
 	nEvents := len(events)
... ...
@@ -656,5 +643,33 @@ func (s *DockerSuite) TestEventsContainerRestart(c *check.C) {
656 656
 	c.Assert(createCount, checker.Equals, 1, check.Commentf("testEvent should be created 1 times: %v", actions))
657 657
 	c.Assert(startCount, checker.Equals, 4, check.Commentf("testEvent should start 4 times: %v", actions))
658 658
 	c.Assert(dieCount, checker.Equals, 4, check.Commentf("testEvent should die 4 times: %v", actions))
659
+}
660
+
661
+func (s *DockerSuite) TestEventsSinceInTheFuture(c *check.C) {
662
+	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
663
+	waitRun("test-container")
664
+
665
+	since := daemonTime(c)
666
+	until := since.Add(time.Duration(-24) * time.Hour)
667
+	out, _, err := dockerCmdWithError("events", "--filter", "image=busybox", "--since", parseEventTime(since), "--until", parseEventTime(until))
668
+
669
+	c.Assert(err, checker.NotNil)
670
+	c.Assert(out, checker.Contains, "cannot be after `until`")
671
+}
672
+
673
+func (s *DockerSuite) TestEventsUntilInThePast(c *check.C) {
674
+	since := daemonUnixTime(c)
675
+
676
+	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
677
+	waitRun("test-container")
678
+
679
+	until := daemonUnixTime(c)
680
+
681
+	dockerCmd(c, "run", "--name", "test-container2", "-d", "busybox", "true")
682
+	waitRun("test-container2")
683
+
684
+	out, _ := dockerCmd(c, "events", "--filter", "image=busybox", "--since", since, "--until", until)
659 685
 
686
+	c.Assert(out, checker.Not(checker.Contains), "test-container2")
687
+	c.Assert(out, checker.Contains, "test-container")
660 688
 }
... ...
@@ -19,14 +19,14 @@ import (
19 19
 
20 20
 // #5979
21 21
 func (s *DockerSuite) TestEventsRedirectStdout(c *check.C) {
22
-	since := daemonTime(c).Unix()
22
+	since := daemonUnixTime(c)
23 23
 	dockerCmd(c, "run", "busybox", "true")
24 24
 
25 25
 	file, err := ioutil.TempFile("", "")
26 26
 	c.Assert(err, checker.IsNil, check.Commentf("could not create temp file"))
27 27
 	defer os.Remove(file.Name())
28 28
 
29
-	command := fmt.Sprintf("%s events --since=%d --until=%d > %s", dockerBinary, since, daemonTime(c).Unix(), file.Name())
29
+	command := fmt.Sprintf("%s events --since=%s --until=%s > %s", dockerBinary, since, daemonUnixTime(c), file.Name())
30 30
 	_, tty, err := pty.Open()
31 31
 	c.Assert(err, checker.IsNil, check.Commentf("Could not open pty"))
32 32
 	cmd := exec.Command("sh", "-c", command)
... ...
@@ -63,7 +63,7 @@ func (s *DockerSuite) TestEventsOOMDisableFalse(c *check.C) {
63 63
 		c.Fatal("Timeout waiting for container to die on OOM")
64 64
 	}
65 65
 
66
-	out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=oomFalse", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
66
+	out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=oomFalse", "--until", daemonUnixTime(c))
67 67
 	events := strings.Split(strings.TrimSuffix(out, "\n"), "\n")
68 68
 	nEvents := len(events)
69 69
 
... ...
@@ -131,7 +131,7 @@ func (s *DockerSuite) TestEventsContainerFilterByName(c *check.C) {
131 131
 	cOut, _ = dockerCmd(c, "run", "--name=bar", "-d", "busybox", "top")
132 132
 	c2 := strings.TrimSpace(cOut)
133 133
 	waitRun("bar")
134
-	out, _ := dockerCmd(c, "events", "-f", "container=foo", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
134
+	out, _ := dockerCmd(c, "events", "-f", "container=foo", "--since=0", "--until", daemonUnixTime(c))
135 135
 	c.Assert(out, checker.Contains, c1, check.Commentf(out))
136 136
 	c.Assert(out, checker.Not(checker.Contains), c2, check.Commentf(out))
137 137
 }
... ...
@@ -147,15 +147,19 @@ func (s *DockerSuite) TestEventsContainerFilterBeforeCreate(c *check.C) {
147 147
 
148 148
 	// calculate the time it takes to create and start a container and sleep 2 seconds
149 149
 	// this is to make sure the docker event will recevie the event of container
150
-	since := daemonTime(c).Unix()
150
+	since := daemonTime(c)
151 151
 	id, _ := dockerCmd(c, "run", "-d", "busybox", "top")
152 152
 	cID := strings.TrimSpace(id)
153 153
 	waitRun(cID)
154 154
 	time.Sleep(2 * time.Second)
155
-	duration := daemonTime(c).Unix() - since
155
+	duration := daemonTime(c).Sub(since)
156 156
 
157 157
 	go func() {
158
-		out, _ = dockerCmd(c, "events", "-f", "container=foo", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()+2*duration))
158
+		// start events and wait for future events to
159
+		// make sure the new container shows up even when
160
+		// the event stream was created before the container.
161
+		t := daemonTime(c).Add(2 * duration)
162
+		out, _ = dockerCmd(c, "events", "-f", "container=foo", "--since=0", "--until", parseEventTime(t))
159 163
 		close(ch)
160 164
 	}()
161 165
 	// Sleep 2 second to wait docker event to start
... ...
@@ -170,7 +174,7 @@ func (s *DockerSuite) TestEventsContainerFilterBeforeCreate(c *check.C) {
170 170
 func (s *DockerSuite) TestVolumeEvents(c *check.C) {
171 171
 	testRequires(c, DaemonIsLinux)
172 172
 
173
-	since := daemonTime(c).Unix()
173
+	since := daemonUnixTime(c)
174 174
 
175 175
 	// Observe create/mount volume actions
176 176
 	dockerCmd(c, "volume", "create", "--name", "test-event-volume-local")
... ...
@@ -181,7 +185,8 @@ func (s *DockerSuite) TestVolumeEvents(c *check.C) {
181 181
 	dockerCmd(c, "rm", "-f", "test-volume-container")
182 182
 	dockerCmd(c, "volume", "rm", "test-event-volume-local")
183 183
 
184
-	out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
184
+	until := daemonUnixTime(c)
185
+	out, _ := dockerCmd(c, "events", "--since", since, "--until", until)
185 186
 	events := strings.Split(strings.TrimSpace(out), "\n")
186 187
 	c.Assert(len(events), checker.GreaterThan, 4)
187 188
 
... ...
@@ -196,7 +201,7 @@ func (s *DockerSuite) TestVolumeEvents(c *check.C) {
196 196
 func (s *DockerSuite) TestNetworkEvents(c *check.C) {
197 197
 	testRequires(c, DaemonIsLinux)
198 198
 
199
-	since := daemonTime(c).Unix()
199
+	since := daemonUnixTime(c)
200 200
 
201 201
 	// Observe create/connect network actions
202 202
 	dockerCmd(c, "network", "create", "test-event-network-local")
... ...
@@ -207,7 +212,8 @@ func (s *DockerSuite) TestNetworkEvents(c *check.C) {
207 207
 	dockerCmd(c, "rm", "-f", "test-network-container")
208 208
 	dockerCmd(c, "network", "rm", "test-event-network-local")
209 209
 
210
-	out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
210
+	until := daemonUnixTime(c)
211
+	out, _ := dockerCmd(c, "events", "--since", since, "--until", until)
211 212
 	events := strings.Split(strings.TrimSpace(out), "\n")
212 213
 	c.Assert(len(events), checker.GreaterThan, 4)
213 214
 
... ...
@@ -317,12 +323,12 @@ func (s *DockerSuite) TestEventsImageUntagDelete(c *check.C) {
317 317
 func (s *DockerSuite) TestEventsFilterVolumeAndNetworkType(c *check.C) {
318 318
 	testRequires(c, DaemonIsLinux)
319 319
 
320
-	since := daemonTime(c).Unix()
320
+	since := daemonUnixTime(c)
321 321
 
322 322
 	dockerCmd(c, "network", "create", "test-event-network-type")
323 323
 	dockerCmd(c, "volume", "create", "--name", "test-event-volume-type")
324 324
 
325
-	out, _ := dockerCmd(c, "events", "--filter", "type=volume", "--filter", "type=network", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
325
+	out, _ := dockerCmd(c, "events", "--filter", "type=volume", "--filter", "type=network", "--since", since, "--until", daemonUnixTime(c))
326 326
 	events := strings.Split(strings.TrimSpace(out), "\n")
327 327
 	c.Assert(len(events), checker.GreaterOrEqualThan, 2, check.Commentf(out))
328 328
 
... ...
@@ -336,10 +342,10 @@ func (s *DockerSuite) TestEventsFilterVolumeAndNetworkType(c *check.C) {
336 336
 func (s *DockerSuite) TestEventsFilterVolumeID(c *check.C) {
337 337
 	testRequires(c, DaemonIsLinux)
338 338
 
339
-	since := daemonTime(c).Unix()
339
+	since := daemonUnixTime(c)
340 340
 
341 341
 	dockerCmd(c, "volume", "create", "--name", "test-event-volume-id")
342
-	out, _ := dockerCmd(c, "events", "--filter", "volume=test-event-volume-id", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
342
+	out, _ := dockerCmd(c, "events", "--filter", "volume=test-event-volume-id", "--since", since, "--until", daemonUnixTime(c))
343 343
 	events := strings.Split(strings.TrimSpace(out), "\n")
344 344
 	c.Assert(events, checker.HasLen, 1)
345 345
 
... ...
@@ -350,10 +356,10 @@ func (s *DockerSuite) TestEventsFilterVolumeID(c *check.C) {
350 350
 func (s *DockerSuite) TestEventsFilterNetworkID(c *check.C) {
351 351
 	testRequires(c, DaemonIsLinux)
352 352
 
353
-	since := daemonTime(c).Unix()
353
+	since := daemonUnixTime(c)
354 354
 
355 355
 	dockerCmd(c, "network", "create", "test-event-network-local")
356
-	out, _ := dockerCmd(c, "events", "--filter", "network=test-event-network-local", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
356
+	out, _ := dockerCmd(c, "events", "--filter", "network=test-event-network-local", "--since", since, "--until", daemonUnixTime(c))
357 357
 	events := strings.Split(strings.TrimSpace(out), "\n")
358 358
 	c.Assert(events, checker.HasLen, 1)
359 359
 
... ...
@@ -1,7 +1,6 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"fmt"
5 4
 	"strings"
6 5
 
7 6
 	"github.com/docker/docker/pkg/integration/checker"
... ...
@@ -22,7 +21,7 @@ func (s *DockerSuite) TestPause(c *check.C) {
22 22
 
23 23
 	dockerCmd(c, "unpause", name)
24 24
 
25
-	out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
25
+	out, _ := dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c))
26 26
 	events := strings.Split(strings.TrimSpace(out), "\n")
27 27
 	actions := eventActionsByIDAndType(c, events, name, "container")
28 28
 
... ...
@@ -48,7 +47,7 @@ func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
48 48
 
49 49
 	dockerCmd(c, append([]string{"unpause"}, containers...)...)
50 50
 
51
-	out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
51
+	out, _ := dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c))
52 52
 	events := strings.Split(strings.TrimSpace(out), "\n")
53 53
 
54 54
 	for _, name := range containers {
... ...
@@ -1246,6 +1246,16 @@ func daemonTime(c *check.C) time.Time {
1246 1246
 	return dt
1247 1247
 }
1248 1248
 
1249
+// daemonUnixTime returns the current time on the daemon host with nanoseconds precission.
1250
+// It return the time formatted how the client sends timestamps to the server.
1251
+func daemonUnixTime(c *check.C) string {
1252
+	return parseEventTime(daemonTime(c))
1253
+}
1254
+
1255
+func parseEventTime(t time.Time) string {
1256
+	return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond()))
1257
+}
1258
+
1249 1259
 func setupRegistry(c *check.C, schema1 bool, auth, tokenURL string) *testRegistryV2 {
1250 1260
 	reg, err := newTestRegistryV2(c, schema1, auth, tokenURL)
1251 1261
 	c.Assert(err, check.IsNil)
... ...
@@ -98,7 +98,7 @@ func (e *eventObserver) CheckEventError(c *check.C, id, event string, match even
98 98
 	scannerOut := e.buffer.String()
99 99
 
100 100
 	if e.disconnectionError != nil {
101
-		until := strconv.FormatInt(daemonTime(c).Unix(), 10)
101
+		until := daemonUnixTime(c)
102 102
 		out, _ := dockerCmd(c, "events", "--since", e.startTime, "--until", until)
103 103
 		events := strings.Split(strings.TrimSpace(out), "\n")
104 104
 		for _, e := range events {