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