1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

events filtering

Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
Victor Vieux 2014-11-20 19:46:48 +00:00
parent 998b591a71
commit 7ff3b81054
6 changed files with 179 additions and 24 deletions

View file

@ -1760,6 +1760,10 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
cmd := cli.Subcmd("events", "", "Get real time events from the server") cmd := cli.Subcmd("events", "", "Get real time events from the server")
since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp") since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp") until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
flFilter := opts.NewListOpts(nil)
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'event=stop')")
if err := cmd.Parse(args); err != nil { if err := cmd.Parse(args); err != nil {
return nil return nil
} }
@ -1769,9 +1773,20 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
return nil return nil
} }
var ( var (
v = url.Values{} v = url.Values{}
loc = time.FixedZone(time.Now().Zone()) loc = time.FixedZone(time.Now().Zone())
eventFilterArgs = filters.Args{}
) )
// Consolidate all filter flags, and sanity check them early.
// They'll get process in the daemon/server.
for _, f := range flFilter.GetAll() {
var err error
eventFilterArgs, err = filters.ParseFlag(f, eventFilterArgs)
if err != nil {
return err
}
}
var setTime = func(key, value string) { var setTime = func(key, value string) {
format := timeutils.RFC3339NanoFixed format := timeutils.RFC3339NanoFixed
if len(value) < len(format) { if len(value) < len(format) {
@ -1789,6 +1804,13 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
if *until != "" { if *until != "" {
setTime("until", *until) setTime("until", *until)
} }
if len(eventFilterArgs) > 0 {
filterJson, err := filters.ToParam(eventFilterArgs)
if err != nil {
return err
}
v.Set("filters", filterJson)
}
if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil { if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
return err return err
} }

View file

@ -315,6 +315,7 @@ func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWrite
streamJSON(job, w, true) streamJSON(job, w, true)
job.Setenv("since", r.Form.Get("since")) job.Setenv("since", r.Form.Get("since"))
job.Setenv("until", r.Form.Get("until")) job.Setenv("until", r.Form.Get("until"))
job.Setenv("filters", r.Form.Get("filters"))
return job.Run() return job.Run()
} }

View file

@ -1386,6 +1386,7 @@ Query Parameters:
- **since** timestamp used for polling - **since** timestamp used for polling
- **until** timestamp used for polling - **until** timestamp used for polling
- **filters** a json encoded value of the filters (a map[string][]string) to process on the event list.
Status Codes: Status Codes:

View file

@ -610,7 +610,10 @@ For example:
Usage: docker events [OPTIONS] Usage: docker events [OPTIONS]
Get real time events from the server Get real time events from the server
-f, --filter=[] Provide filter values. Valid filters:
event=<string> - event to filter
image=<string> - image to filter
container=<string> - container to filter
--since="" Show all events created since timestamp --since="" Show all events created since timestamp
--until="" Stream events until this timestamp --until="" Stream events until this timestamp
@ -622,6 +625,24 @@ and Docker images will report:
untag, delete untag, delete
#### Filtering
The filtering flag (`-f` or `--filter`) format is of "key=value". If you would like to use
multiple filters, pass multiple flags (e.g., `--filter "foo=bar" --filter "bif=baz"`)
Using the same filter multiple times will be handled as a `OR`; for example
`--filter container=588a23dac085 --filter container=a8f7720b8c22` will display events for
container 588a23dac085 `OR` container a8f7720b8c22
Using multiple filters will be handled as a `AND`; for example
`--filter container=588a23dac085 --filter event=start` will display events for container
container 588a23dac085 `AND` only when the event type is `start`
Current filters:
* event
* image
* container
#### Examples #### Examples
You'll need two shells for this example. You'll need two shells for this example.
@ -630,31 +651,64 @@ You'll need two shells for this example.
$ sudo docker events $ sudo docker events
**Shell 2: Start and Stop a Container:** **Shell 2: Start and Stop containers:**
$ sudo docker start 4386fb97867d $ sudo docker start 4386fb97867d
$ sudo docker stop 4386fb97867d $ sudo docker stop 4386fb97867d
$ sudo docker stop 7805c1d35632
**Shell 1: (Again .. now showing events):** **Shell 1: (Again .. now showing events):**
2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from 12de384bfb10) start 2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) start
2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from 12de384bfb10) die 2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from 12de384bfb10) stop 2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) stop
2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
**Show events in the past from a specified time:** **Show events in the past from a specified time:**
$ sudo docker events --since 1378216169 $ sudo docker events --since 1378216169
2014-03-10T17:42:14.999999999Z07:00 4386fb97867d: (from 12de384bfb10) die 2014-03-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
2014-03-10T17:42:14.999999999Z07:00 4386fb97867d: (from 12de384bfb10) stop 2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) stop
2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2014-03-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
$ sudo docker events --since '2013-09-03' $ sudo docker events --since '2013-09-03'
2014-09-03T17:42:14.999999999Z07:00 4386fb97867d: (from 12de384bfb10) start 2014-09-03T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) start
2014-09-03T17:42:14.999999999Z07:00 4386fb97867d: (from 12de384bfb10) die 2014-09-03T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
2014-09-03T17:42:14.999999999Z07:00 4386fb97867d: (from 12de384bfb10) stop 2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) stop
2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2014-09-03T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
$ sudo docker events --since '2013-09-03 15:49:29 +0200 CEST' $ sudo docker events --since '2013-09-03 15:49:29 +0200 CEST'
2014-09-03T15:49:29.999999999Z07:00 4386fb97867d: (from 12de384bfb10) die 2014-09-03T15:49:29.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
2014-09-03T15:49:29.999999999Z07:00 4386fb97867d: (from 12de384bfb10) stop 2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) stop
2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2014-09-03T15:49:29.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
**Filter events:**
$ sudo docker events --filter 'event=stop'
2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) stop
2014-09-03T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
$ sudo docker events --filter 'image=ubuntu-1:14.04'
2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) start
2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) stop
$ sudo docker events --filter 'container=7805c1d35632'
2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2014-09-03T15:49:29.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
$ sudo docker events --filter 'container=7805c1d35632' --filter 'container=4386fb97867d'
2014-09-03T15:49:29.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
2014-05-10T17:42:14.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) stop
2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2014-09-03T15:49:29.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
$ sudo docker events --filter 'container=7805c1d35632' --filter 'event=stop'
2014-09-03T15:49:29.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
## exec ## exec
@ -768,7 +822,7 @@ by default.
#### Filtering #### Filtering
The filtering flag (`-f` or `--filter`) format is of "key=value". If there are more The filtering flag (`-f` or `--filter`) format is of "key=value". If there is more
than one filter, then pass multiple flags (e.g., `--filter "foo=bar" --filter "bif=baz"`) than one filter, then pass multiple flags (e.g., `--filter "foo=bar" --filter "bif=baz"`)
Current filters: Current filters:

View file

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/docker/docker/engine" "github.com/docker/docker/engine"
"github.com/docker/docker/pkg/parsers/filters"
"github.com/docker/docker/utils" "github.com/docker/docker/utils"
) )
@ -48,6 +49,11 @@ func (e *Events) Get(job *engine.Job) engine.Status {
timeout = time.NewTimer(time.Unix(until, 0).Sub(time.Now())) timeout = time.NewTimer(time.Unix(until, 0).Sub(time.Now()))
) )
eventFilters, err := filters.FromParam(job.Getenv("filters"))
if err != nil {
return job.Error(err)
}
// If no until, disable timeout // If no until, disable timeout
if until == 0 { if until == 0 {
timeout.Stop() timeout.Stop()
@ -61,7 +67,7 @@ func (e *Events) Get(job *engine.Job) engine.Status {
// Resend every event in the [since, until] time interval. // Resend every event in the [since, until] time interval.
if since != 0 { if since != 0 {
if err := e.writeCurrent(job, since, until); err != nil { if err := e.writeCurrent(job, since, until, eventFilters); err != nil {
return job.Error(err) return job.Error(err)
} }
} }
@ -72,7 +78,7 @@ func (e *Events) Get(job *engine.Job) engine.Status {
if !ok { if !ok {
return engine.StatusOK return engine.StatusOK
} }
if err := writeEvent(job, event); err != nil { if err := writeEvent(job, event, eventFilters); err != nil {
return job.Error(err) return job.Error(err)
} }
case <-timeout.C: case <-timeout.C:
@ -97,7 +103,23 @@ func (e *Events) SubscribersCount(job *engine.Job) engine.Status {
return engine.StatusOK return engine.StatusOK
} }
func writeEvent(job *engine.Job, event *utils.JSONMessage) error { func writeEvent(job *engine.Job, event *utils.JSONMessage, eventFilters filters.Args) error {
isFiltered := func(field string, filter []string) bool {
if len(filter) == 0 {
return false
}
for _, v := range filter {
if v == field {
return false
}
}
return true
}
if isFiltered(event.Status, eventFilters["event"]) || isFiltered(event.From, eventFilters["image"]) || isFiltered(event.ID, eventFilters["container"]) {
return nil
}
// When sending an event JSON serialization errors are ignored, but all // When sending an event JSON serialization errors are ignored, but all
// other errors lead to the eviction of the listener. // other errors lead to the eviction of the listener.
if b, err := json.Marshal(event); err == nil { if b, err := json.Marshal(event); err == nil {
@ -108,11 +130,11 @@ func writeEvent(job *engine.Job, event *utils.JSONMessage) error {
return nil return nil
} }
func (e *Events) writeCurrent(job *engine.Job, since, until int64) error { func (e *Events) writeCurrent(job *engine.Job, since, until int64, eventFilters filters.Args) error {
e.mu.RLock() e.mu.RLock()
for _, event := range e.events { for _, event := range e.events {
if event.Time >= since && (event.Time <= until || until == 0) { if event.Time >= since && (event.Time <= until || until == 0) {
if err := writeEvent(job, event); err != nil { if err := writeEvent(job, event, eventFilters); err != nil {
e.mu.RUnlock() e.mu.RUnlock()
return err return err
} }

View file

@ -61,7 +61,7 @@ func TestEventsPause(t *testing.T) {
t.Fatalf("event should be pause, not %#v", pauseEvent) t.Fatalf("event should be pause, not %#v", pauseEvent)
} }
if unpauseEvent[len(unpauseEvent)-1] != "unpause" { if unpauseEvent[len(unpauseEvent)-1] != "unpause" {
t.Fatalf("event should be pause, not %#v", unpauseEvent) t.Fatalf("event should be unpause, not %#v", unpauseEvent)
} }
waitCmd := exec.Command(dockerBinary, "wait", name) waitCmd := exec.Command(dockerBinary, "wait", name)
@ -138,13 +138,13 @@ func TestEventsContainerEvents(t *testing.T) {
t.Fatalf("event should be create, not %#v", createEvent) t.Fatalf("event should be create, not %#v", createEvent)
} }
if startEvent[len(startEvent)-1] != "start" { if startEvent[len(startEvent)-1] != "start" {
t.Fatalf("event should be pause, not %#v", startEvent) t.Fatalf("event should be start, not %#v", startEvent)
} }
if dieEvent[len(dieEvent)-1] != "die" { if dieEvent[len(dieEvent)-1] != "die" {
t.Fatalf("event should be pause, not %#v", dieEvent) t.Fatalf("event should be die, not %#v", dieEvent)
} }
if destroyEvent[len(destroyEvent)-1] != "destroy" { if destroyEvent[len(destroyEvent)-1] != "destroy" {
t.Fatalf("event should be pause, not %#v", destroyEvent) t.Fatalf("event should be destroy, not %#v", destroyEvent)
} }
logDone("events - container create, start, die, destroy is logged") logDone("events - container create, start, die, destroy is logged")
@ -283,3 +283,58 @@ func TestEventsImageImport(t *testing.T) {
logDone("events - image import is logged") logDone("events - image import is logged")
} }
func TestEventsFilters(t *testing.T) {
now := time.Now().Unix()
cmd(t, "run", "--rm", "busybox", "true")
cmd(t, "run", "--rm", "busybox", "true")
eventsCmd := exec.Command(dockerBinary, "events", fmt.Sprintf("--since=%d", now), fmt.Sprintf("--until=%d", time.Now().Unix()), "--filter", "event=die")
out, exitCode, err := runCommandWithOutput(eventsCmd)
if exitCode != 0 || err != nil {
t.Fatalf("Failed to get events with exit code %d: %s", exitCode, err)
}
events := strings.Split(out, "\n")
events = events[:len(events)-1]
if len(events) != 2 {
fmt.Printf("%v\n", events)
t.Fatalf("Unexpected event")
}
dieEvent := strings.Fields(events[len(events)-1])
if dieEvent[len(dieEvent)-1] != "die" {
t.Fatalf("event should be die, not %#v", dieEvent)
}
dieEvent = strings.Fields(events[len(events)-2])
if dieEvent[len(dieEvent)-1] != "die" {
t.Fatalf("event should be die, not %#v", dieEvent)
}
eventsCmd = exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", time.Now().Unix()), "--filter", "event=die", "--filter", "event=start")
out, exitCode, err = runCommandWithOutput(eventsCmd)
if exitCode != 0 || err != nil {
t.Fatalf("Failed to get events with exit code %d: %s", exitCode, err)
}
events = strings.Split(out, "\n")
events = events[:len(events)-1]
if len(events) != 4 {
t.Fatalf("Unexpected event")
}
startEvent := strings.Fields(events[len(events)-4])
if startEvent[len(startEvent)-1] != "start" {
t.Fatalf("event should be start, not %#v", startEvent)
}
dieEvent = strings.Fields(events[len(events)-3])
if dieEvent[len(dieEvent)-1] != "die" {
t.Fatalf("event should be die, not %#v", dieEvent)
}
startEvent = strings.Fields(events[len(events)-2])
if startEvent[len(startEvent)-1] != "start" {
t.Fatalf("event should be start, not %#v", startEvent)
}
dieEvent = strings.Fields(events[len(events)-1])
if dieEvent[len(dieEvent)-1] != "die" {
t.Fatalf("event should be die, not %#v", dieEvent)
}
logDone("events - filters")
}