Merge pull request #22590 from yongtang/22463-docker-daemon-events
Emit events for docker daemon
This commit is contained in:
commit
d76d38c7d0
7 changed files with 158 additions and 3 deletions
|
@ -6,6 +6,7 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -1332,6 +1333,11 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
|
|||
func (daemon *Daemon) Reload(config *Config) error {
|
||||
daemon.configStore.reloadLock.Lock()
|
||||
defer daemon.configStore.reloadLock.Unlock()
|
||||
|
||||
if err := daemon.reloadClusterDiscovery(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.IsValueSet("labels") {
|
||||
daemon.configStore.Labels = config.Labels
|
||||
}
|
||||
|
@ -1365,7 +1371,28 @@ func (daemon *Daemon) Reload(config *Config) error {
|
|||
daemon.uploadManager.SetConcurrency(*daemon.configStore.MaxConcurrentUploads)
|
||||
}
|
||||
|
||||
return daemon.reloadClusterDiscovery(config)
|
||||
// We emit daemon reload event here with updatable configurations
|
||||
attributes := map[string]string{}
|
||||
attributes["debug"] = fmt.Sprintf("%t", daemon.configStore.Debug)
|
||||
attributes["cluster-store"] = daemon.configStore.ClusterStore
|
||||
if daemon.configStore.ClusterOpts != nil {
|
||||
opts, _ := json.Marshal(daemon.configStore.ClusterOpts)
|
||||
attributes["cluster-store-opts"] = string(opts)
|
||||
} else {
|
||||
attributes["cluster-store-opts"] = "{}"
|
||||
}
|
||||
attributes["cluster-advertise"] = daemon.configStore.ClusterAdvertise
|
||||
if daemon.configStore.Labels != nil {
|
||||
labels, _ := json.Marshal(daemon.configStore.Labels)
|
||||
attributes["labels"] = string(labels)
|
||||
} else {
|
||||
attributes["labels"] = "[]"
|
||||
}
|
||||
attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
|
||||
attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads)
|
||||
daemon.LogDaemonEventWithAttributes("reload", attributes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) reloadClusterDiscovery(config *Config) error {
|
||||
|
|
|
@ -80,6 +80,20 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, actio
|
|||
daemon.EventsService.Log(action, events.NetworkEventType, actor)
|
||||
}
|
||||
|
||||
// LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
|
||||
func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
|
||||
if daemon.EventsService != nil {
|
||||
if info, err := daemon.SystemInfo(); err == nil && info.Name != "" {
|
||||
attributes["name"] = info.Name
|
||||
}
|
||||
actor := events.Actor{
|
||||
ID: daemon.ID,
|
||||
Attributes: attributes,
|
||||
}
|
||||
daemon.EventsService.Log(action, events.DaemonEventType, actor)
|
||||
}
|
||||
}
|
||||
|
||||
// SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
|
||||
func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
|
||||
ef := daemonevents.NewFilter(filter)
|
||||
|
|
|
@ -20,6 +20,7 @@ func NewFilter(filter filters.Args) *Filter {
|
|||
func (ef *Filter) Include(ev events.Message) bool {
|
||||
return ef.filter.ExactMatch("event", ev.Action) &&
|
||||
ef.filter.ExactMatch("type", ev.Type) &&
|
||||
ef.matchDaemon(ev) &&
|
||||
ef.matchContainer(ev) &&
|
||||
ef.matchVolume(ev) &&
|
||||
ef.matchNetwork(ev) &&
|
||||
|
@ -34,6 +35,10 @@ func (ef *Filter) matchLabels(attributes map[string]string) bool {
|
|||
return ef.filter.MatchKVList("label", attributes)
|
||||
}
|
||||
|
||||
func (ef *Filter) matchDaemon(ev events.Message) bool {
|
||||
return ef.fuzzyMatchName(ev, events.DaemonEventType)
|
||||
}
|
||||
|
||||
func (ef *Filter) matchContainer(ev events.Message) bool {
|
||||
return ef.fuzzyMatchName(ev, events.ContainerEventType)
|
||||
}
|
||||
|
|
|
@ -119,6 +119,8 @@ This section lists each version from latest to oldest. Each listing includes a
|
|||
* `POST /containers/create` now returns a HTTP 400 "bad parameter" message
|
||||
if no command is specified (instead of a HTTP 500 "server error")
|
||||
* `GET /images/search` now takes a `filters` query parameter.
|
||||
* `GET /events` now supports a `reload` event that is emitted when the daemon configuration is reloaded.
|
||||
* `GET /events` now supports filtering by daemon name or ID.
|
||||
|
||||
### v1.23 API changes
|
||||
|
||||
|
|
|
@ -2420,6 +2420,10 @@ Docker networks report the following events:
|
|||
|
||||
create, connect, disconnect, destroy
|
||||
|
||||
Docker daemon report the following event:
|
||||
|
||||
reload
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /events?since=1374067924
|
||||
|
@ -2589,9 +2593,10 @@ Query Parameters:
|
|||
- `event=<string>`; -- event to filter
|
||||
- `image=<string>`; -- image to filter
|
||||
- `label=<string>`; -- image and container label to filter
|
||||
- `type=<string>`; -- either `container` or `image` or `volume` or `network`
|
||||
- `type=<string>`; -- either `container` or `image` or `volume` or `network` or `daemon`
|
||||
- `volume=<string>`; -- volume to filter
|
||||
- `network=<string>`; -- network to filter
|
||||
- `daemon=<string>`; -- daemon name or id to filter
|
||||
|
||||
Status Codes:
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ Docker networks report the following events:
|
|||
|
||||
create, connect, disconnect, destroy
|
||||
|
||||
Docker daemon report the following events:
|
||||
|
||||
reload
|
||||
|
||||
The `--since` and `--until` parameters can be Unix timestamps, date formatted
|
||||
timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed
|
||||
relative to the client machine’s time. If you do not provide the `--since` option,
|
||||
|
@ -68,9 +72,10 @@ The currently supported filters are:
|
|||
* event (`event=<event action>`)
|
||||
* image (`image=<tag or id>`)
|
||||
* label (`label=<key>` or `label=<key>=<value>`)
|
||||
* type (`type=<container or image or volume or network>`)
|
||||
* type (`type=<container or image or volume or network or daemon>`)
|
||||
* volume (`volume=<name or id>`)
|
||||
* network (`network=<name or id>`)
|
||||
* daemon (`daemon=<name or id>`)
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
|
@ -366,3 +367,99 @@ func (s *DockerSuite) TestEventsFilterNetworkID(c *check.C) {
|
|||
c.Assert(events[0], checker.Contains, "test-event-network-local")
|
||||
c.Assert(events[0], checker.Contains, "type=bridge")
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
|
||||
testRequires(c, SameHostDaemon, DaemonIsLinux)
|
||||
|
||||
// daemon config file
|
||||
configFilePath := "test.json"
|
||||
configFile, err := os.Create(configFilePath)
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer os.Remove(configFilePath)
|
||||
|
||||
daemonConfig := `{"labels":["foo=bar"]}`
|
||||
fmt.Fprintf(configFile, "%s", daemonConfig)
|
||||
configFile.Close()
|
||||
c.Assert(s.d.Start(fmt.Sprintf("--config-file=%s", configFilePath)), check.IsNil)
|
||||
|
||||
// Get daemon ID
|
||||
out, err := s.d.Cmd("info")
|
||||
c.Assert(err, checker.IsNil)
|
||||
daemonID := ""
|
||||
daemonName := ""
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
if strings.HasPrefix(line, "ID: ") {
|
||||
daemonID = strings.TrimPrefix(line, "ID: ")
|
||||
} else if strings.HasPrefix(line, "Name: ") {
|
||||
daemonName = strings.TrimPrefix(line, "Name: ")
|
||||
}
|
||||
}
|
||||
c.Assert(daemonID, checker.Not(checker.Equals), "")
|
||||
|
||||
configFile, err = os.Create(configFilePath)
|
||||
c.Assert(err, checker.IsNil)
|
||||
daemonConfig = `{"max-concurrent-downloads":1,"labels":["bar=foo"]}`
|
||||
fmt.Fprintf(configFile, "%s", daemonConfig)
|
||||
configFile.Close()
|
||||
|
||||
syscall.Kill(s.d.cmd.Process.Pid, syscall.SIGHUP)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
|
||||
c.Assert(err, checker.IsNil)
|
||||
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))
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
|
||||
testRequires(c, SameHostDaemon, DaemonIsLinux)
|
||||
|
||||
// daemon config file
|
||||
configFilePath := "test.json"
|
||||
configFile, err := os.Create(configFilePath)
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer os.Remove(configFilePath)
|
||||
|
||||
daemonConfig := `{"labels":["foo=bar"]}`
|
||||
fmt.Fprintf(configFile, "%s", daemonConfig)
|
||||
configFile.Close()
|
||||
c.Assert(s.d.Start(fmt.Sprintf("--config-file=%s", configFilePath)), check.IsNil)
|
||||
|
||||
// Get daemon ID
|
||||
out, err := s.d.Cmd("info")
|
||||
c.Assert(err, checker.IsNil)
|
||||
daemonID := ""
|
||||
daemonName := ""
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
if strings.HasPrefix(line, "ID: ") {
|
||||
daemonID = strings.TrimPrefix(line, "ID: ")
|
||||
} else if strings.HasPrefix(line, "Name: ") {
|
||||
daemonName = strings.TrimPrefix(line, "Name: ")
|
||||
}
|
||||
}
|
||||
c.Assert(daemonID, checker.Not(checker.Equals), "")
|
||||
|
||||
syscall.Kill(s.d.cmd.Process.Pid, syscall.SIGHUP)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", daemonID))
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
|
||||
|
||||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", daemonName))
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
|
||||
|
||||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "daemon=foo")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Not(checker.Contains), fmt.Sprintf("daemon reload %s", daemonID))
|
||||
|
||||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=daemon")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
|
||||
|
||||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=container")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Not(checker.Contains), fmt.Sprintf("daemon reload %s", daemonID))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue