Browse code

Emit events for docker daemon

This fix tries to cover the issue raised in #22463 by emitting
events for docker daemon so that user could be notified by
scenarios like config reload, etc.

This fix adds the `daemon reload`, and events for docker daemon.

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:11:34
Showing 6 changed files
... ...
@@ -6,6 +6,7 @@
6 6
 package daemon
7 7
 
8 8
 import (
9
+	"encoding/json"
9 10
 	"fmt"
10 11
 	"io"
11 12
 	"io/ioutil"
... ...
@@ -1337,6 +1338,11 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
1337 1337
 func (daemon *Daemon) Reload(config *Config) error {
1338 1338
 	daemon.configStore.reloadLock.Lock()
1339 1339
 	defer daemon.configStore.reloadLock.Unlock()
1340
+
1341
+	if err := daemon.reloadClusterDiscovery(config); err != nil {
1342
+		return err
1343
+	}
1344
+
1340 1345
 	if config.IsValueSet("labels") {
1341 1346
 		daemon.configStore.Labels = config.Labels
1342 1347
 	}
... ...
@@ -1370,7 +1376,26 @@ func (daemon *Daemon) Reload(config *Config) error {
1370 1370
 		daemon.uploadManager.SetConcurrency(*daemon.configStore.MaxConcurrentUploads)
1371 1371
 	}
1372 1372
 
1373
-	return daemon.reloadClusterDiscovery(config)
1373
+	// We emit daemon reload event here with updatable configurations
1374
+	attributes := map[string]string{}
1375
+	attributes["debug"] = fmt.Sprintf("%t", daemon.configStore.Debug)
1376
+	attributes["cluster-store"] = daemon.configStore.ClusterStore
1377
+	if daemon.configStore.ClusterOpts != nil {
1378
+		opts, _ := json.Marshal(daemon.configStore.ClusterOpts)
1379
+		attributes["cluster-store-opts"] = string(opts)
1380
+	} else {
1381
+		attributes["cluster-store-opts"] = "{}"
1382
+	}
1383
+	attributes["cluster-advertise"] = daemon.configStore.ClusterAdvertise
1384
+	if daemon.configStore.Labels != nil {
1385
+		labels, _ := json.Marshal(daemon.configStore.Labels)
1386
+		attributes["labels"] = string(labels)
1387
+	} else {
1388
+		attributes["labels"] = "[]"
1389
+	}
1390
+	daemon.LogDaemonEventWithAttributes("reload", attributes)
1391
+
1392
+	return nil
1374 1393
 }
1375 1394
 
1376 1395
 func (daemon *Daemon) reloadClusterDiscovery(config *Config) error {
... ...
@@ -80,6 +80,17 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, actio
80 80
 	daemon.EventsService.Log(action, events.NetworkEventType, actor)
81 81
 }
82 82
 
83
+// LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
84
+func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
85
+	if daemon.EventsService != nil {
86
+		actor := events.Actor{
87
+			ID:         daemon.ID,
88
+			Attributes: attributes,
89
+		}
90
+		daemon.EventsService.Log(action, events.DaemonEventType, actor)
91
+	}
92
+}
93
+
83 94
 // SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
84 95
 func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
85 96
 	ef := daemonevents.NewFilter(filter)
... ...
@@ -119,6 +119,7 @@ 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 123
 
123 124
 ### v1.23 API changes
124 125
 
... ...
@@ -2420,6 +2420,10 @@ Docker networks report the following events:
2420 2420
 
2421 2421
     create, connect, disconnect, destroy
2422 2422
 
2423
+Docker daemon report the following event:
2424
+
2425
+    reload
2426
+
2423 2427
 **Example request**:
2424 2428
 
2425 2429
     GET /events?since=1374067924
... ...
@@ -35,6 +35,10 @@ Docker networks report the following events:
35 35
 
36 36
     create, connect, disconnect, destroy
37 37
 
38
+Docker daemon report the following events:
39
+
40
+    reload
41
+
38 42
 The `--since` and `--until` parameters can be Unix timestamps, date formatted
39 43
 timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed
40 44
 relative to the client machine’s time. If you do not provide the `--since` option,
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"os"
10 10
 	"os/exec"
11 11
 	"strings"
12
+	"syscall"
12 13
 	"time"
13 14
 	"unicode"
14 15
 
... ...
@@ -366,3 +367,44 @@ func (s *DockerSuite) TestEventsFilterNetworkID(c *check.C) {
366 366
 	c.Assert(events[0], checker.Contains, "test-event-network-local")
367 367
 	c.Assert(events[0], checker.Contains, "type=bridge")
368 368
 }
369
+
370
+func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
371
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
372
+
373
+	// daemon config file
374
+	configFilePath := "test.json"
375
+	configFile, err := os.Create(configFilePath)
376
+	c.Assert(err, checker.IsNil)
377
+	defer os.Remove(configFilePath)
378
+
379
+	daemonConfig := `{"labels":["foo=bar"]}`
380
+	fmt.Fprintf(configFile, "%s", daemonConfig)
381
+	configFile.Close()
382
+	c.Assert(s.d.Start(fmt.Sprintf("--config-file=%s", configFilePath)), check.IsNil)
383
+
384
+	// Get daemon ID
385
+	out, err := s.d.Cmd("info")
386
+	c.Assert(err, checker.IsNil)
387
+	daemonID := ""
388
+	for _, line := range strings.Split(out, "\n") {
389
+		if strings.HasPrefix(line, "ID: ") {
390
+			daemonID = strings.TrimPrefix(line, "ID: ")
391
+			break
392
+		}
393
+	}
394
+	c.Assert(daemonID, checker.Not(checker.Equals), "")
395
+
396
+	configFile, err = os.Create(configFilePath)
397
+	c.Assert(err, checker.IsNil)
398
+	daemonConfig = `{"labels":["bar=foo"]}`
399
+	fmt.Fprintf(configFile, "%s", daemonConfig)
400
+	configFile.Close()
401
+
402
+	syscall.Kill(s.d.cmd.Process.Pid, syscall.SIGHUP)
403
+
404
+	time.Sleep(3 * time.Second)
405
+
406
+	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
407
+	c.Assert(err, checker.IsNil)
408
+	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, labels=[\"bar=foo\"])", daemonID))
409
+}