Browse code

Add filter for events emitted by docker daemon

This fix tries to cover the issue raised in #22463 by adding
filter for events emitted by docker daemon so that user could
utilize filter to receive events of interest.

Documentations have been updated for this fix.

Additional tests have been added to cover the changes in this fix.

This fix fixes #22463.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

Yong Tang authored on 2016/05/09 08:15:33
Showing 7 changed files
... ...
@@ -1393,6 +1393,8 @@ func (daemon *Daemon) Reload(config *Config) error {
1393 1393
 	} else {
1394 1394
 		attributes["labels"] = "[]"
1395 1395
 	}
1396
+	attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
1397
+	attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads)
1396 1398
 	daemon.LogDaemonEventWithAttributes("reload", attributes)
1397 1399
 
1398 1400
 	return nil
... ...
@@ -83,6 +83,9 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, actio
83 83
 // LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
84 84
 func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
85 85
 	if daemon.EventsService != nil {
86
+		if info, err := daemon.SystemInfo(); err == nil && info.Name != "" {
87
+			attributes["name"] = info.Name
88
+		}
86 89
 		actor := events.Actor{
87 90
 			ID:         daemon.ID,
88 91
 			Attributes: attributes,
... ...
@@ -20,6 +20,7 @@ func NewFilter(filter filters.Args) *Filter {
20 20
 func (ef *Filter) Include(ev events.Message) bool {
21 21
 	return ef.filter.ExactMatch("event", ev.Action) &&
22 22
 		ef.filter.ExactMatch("type", ev.Type) &&
23
+		ef.matchDaemon(ev) &&
23 24
 		ef.matchContainer(ev) &&
24 25
 		ef.matchVolume(ev) &&
25 26
 		ef.matchNetwork(ev) &&
... ...
@@ -34,6 +35,10 @@ func (ef *Filter) matchLabels(attributes map[string]string) bool {
34 34
 	return ef.filter.MatchKVList("label", attributes)
35 35
 }
36 36
 
37
+func (ef *Filter) matchDaemon(ev events.Message) bool {
38
+	return ef.fuzzyMatchName(ev, events.DaemonEventType)
39
+}
40
+
37 41
 func (ef *Filter) matchContainer(ev events.Message) bool {
38 42
 	return ef.fuzzyMatchName(ev, events.ContainerEventType)
39 43
 }
... ...
@@ -119,7 +119,8 @@ This section lists each version from latest to oldest.  Each listing includes a
119 119
 * `POST /containers/create` now returns a HTTP 400 "bad parameter" message
120 120
   if no command is specified (instead of a HTTP 500 "server error")
121 121
 * `GET /images/search` now takes a `filters` query parameter.
122
-* `GET /events` now supports daemon events of `reload`.
122
+* `GET /events` now supports a `reload` event that is emitted when the daemon configuration is reloaded.
123
+* `GET /events` now supports filtering by daemon name or ID.
123 124
 
124 125
 ### v1.23 API changes
125 126
 
... ...
@@ -2593,9 +2593,10 @@ Query Parameters:
2593 2593
   -   `event=<string>`; -- event to filter
2594 2594
   -   `image=<string>`; -- image to filter
2595 2595
   -   `label=<string>`; -- image and container label to filter
2596
-  -   `type=<string>`; -- either `container` or `image` or `volume` or `network`
2596
+  -   `type=<string>`; -- either `container` or `image` or `volume` or `network` or `daemon`
2597 2597
   -   `volume=<string>`; -- volume to filter
2598 2598
   -   `network=<string>`; -- network to filter
2599
+  -   `daemon=<string>`; -- daemon name or id to filter
2599 2600
 
2600 2601
 Status Codes:
2601 2602
 
... ...
@@ -72,9 +72,10 @@ The currently supported filters are:
72 72
 * event (`event=<event action>`)
73 73
 * image (`image=<tag or id>`)
74 74
 * label (`label=<key>` or `label=<key>=<value>`)
75
-* type (`type=<container or image or volume or network>`)
75
+* type (`type=<container or image or volume or network or daemon>`)
76 76
 * volume (`volume=<name or id>`)
77 77
 * network (`network=<name or id>`)
78
+* daemon (`daemon=<name or id>`)
78 79
 
79 80
 ## Examples
80 81
 
... ...
@@ -386,17 +386,19 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
386 386
 	out, err := s.d.Cmd("info")
387 387
 	c.Assert(err, checker.IsNil)
388 388
 	daemonID := ""
389
+	daemonName := ""
389 390
 	for _, line := range strings.Split(out, "\n") {
390 391
 		if strings.HasPrefix(line, "ID: ") {
391 392
 			daemonID = strings.TrimPrefix(line, "ID: ")
392
-			break
393
+		} else if strings.HasPrefix(line, "Name: ") {
394
+			daemonName = strings.TrimPrefix(line, "Name: ")
393 395
 		}
394 396
 	}
395 397
 	c.Assert(daemonID, checker.Not(checker.Equals), "")
396 398
 
397 399
 	configFile, err = os.Create(configFilePath)
398 400
 	c.Assert(err, checker.IsNil)
399
-	daemonConfig = `{"labels":["bar=foo"]}`
401
+	daemonConfig = `{"max-concurrent-downloads":1,"labels":["bar=foo"]}`
400 402
 	fmt.Fprintf(configFile, "%s", daemonConfig)
401 403
 	configFile.Close()
402 404
 
... ...
@@ -406,5 +408,58 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
406 406
 
407 407
 	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
408 408
 	c.Assert(err, checker.IsNil)
409
-	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, labels=[\"bar=foo\"])", daemonID))
409
+	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, labels=[\"bar=foo\"], max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s)", daemonID, daemonName))
410
+}
411
+
412
+func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
413
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
414
+
415
+	// daemon config file
416
+	configFilePath := "test.json"
417
+	configFile, err := os.Create(configFilePath)
418
+	c.Assert(err, checker.IsNil)
419
+	defer os.Remove(configFilePath)
420
+
421
+	daemonConfig := `{"labels":["foo=bar"]}`
422
+	fmt.Fprintf(configFile, "%s", daemonConfig)
423
+	configFile.Close()
424
+	c.Assert(s.d.Start(fmt.Sprintf("--config-file=%s", configFilePath)), check.IsNil)
425
+
426
+	// Get daemon ID
427
+	out, err := s.d.Cmd("info")
428
+	c.Assert(err, checker.IsNil)
429
+	daemonID := ""
430
+	daemonName := ""
431
+	for _, line := range strings.Split(out, "\n") {
432
+		if strings.HasPrefix(line, "ID: ") {
433
+			daemonID = strings.TrimPrefix(line, "ID: ")
434
+		} else if strings.HasPrefix(line, "Name: ") {
435
+			daemonName = strings.TrimPrefix(line, "Name: ")
436
+		}
437
+	}
438
+	c.Assert(daemonID, checker.Not(checker.Equals), "")
439
+
440
+	syscall.Kill(s.d.cmd.Process.Pid, syscall.SIGHUP)
441
+
442
+	time.Sleep(3 * time.Second)
443
+
444
+	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", daemonID))
445
+	c.Assert(err, checker.IsNil)
446
+	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
447
+
448
+	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", daemonName))
449
+	c.Assert(err, checker.IsNil)
450
+	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
451
+
452
+	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "daemon=foo")
453
+	c.Assert(err, checker.IsNil)
454
+	c.Assert(out, checker.Not(checker.Contains), fmt.Sprintf("daemon reload %s", daemonID))
455
+
456
+	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=daemon")
457
+	c.Assert(err, checker.IsNil)
458
+	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
459
+
460
+	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=container")
461
+	c.Assert(err, checker.IsNil)
462
+	c.Assert(out, checker.Not(checker.Contains), fmt.Sprintf("daemon reload %s", daemonID))
410 463
 }