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>
... | ... |
@@ -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 |
|
... | ... |
@@ -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 |
+} |