Add event types.
- Stop serializing JSONMessage in favor of events.Message. - Keep backwards compatibility with JSONMessage for container events. Signed-off-by: David Calavera <david.calavera@gmail.com> Upstream-commit: 72f1881df102fce9ad31e98045b91c204dd44513 Component: engine
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
@@ -28,3 +34,40 @@ func (s *DockerSuite) TestEventsApiEmptyOutput(c *check.C) {
|
||||
c.Fatal("timeout waiting for events api to respond, should have responded immediately")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsApiBackwardsCompatible(c *check.C) {
|
||||
since := daemonTime(c).Unix()
|
||||
ts := strconv.FormatInt(since, 10)
|
||||
|
||||
out, _ := dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top")
|
||||
containerID := strings.TrimSpace(out)
|
||||
c.Assert(waitRun(containerID), checker.IsNil)
|
||||
|
||||
q := url.Values{}
|
||||
q.Set("since", ts)
|
||||
|
||||
_, body, err := sockRequestRaw("GET", "/events?"+q.Encode(), nil, "")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer body.Close()
|
||||
|
||||
dec := json.NewDecoder(body)
|
||||
var containerCreateEvent *jsonmessage.JSONMessage
|
||||
for {
|
||||
var event jsonmessage.JSONMessage
|
||||
if err := dec.Decode(&event); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
c.Fatal(err)
|
||||
}
|
||||
if event.Status == "create" && event.ID == containerID {
|
||||
containerCreateEvent = &event
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.Assert(containerCreateEvent, checker.Not(checker.IsNil))
|
||||
c.Assert(containerCreateEvent.Status, checker.Equals, "create")
|
||||
c.Assert(containerCreateEvent.ID, checker.Equals, containerID)
|
||||
c.Assert(containerCreateEvent.From, checker.Equals, "busybox")
|
||||
}
|
||||
|
||||
@@ -1874,6 +1874,12 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
name := "testbuildcancellation"
|
||||
|
||||
observer, err := newEventObserver(c)
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = observer.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer observer.Stop()
|
||||
|
||||
// (Note: one year, will never finish)
|
||||
ctx, err := fakeContext("FROM busybox\nRUN sleep 31536000", nil)
|
||||
if err != nil {
|
||||
@@ -1881,15 +1887,6 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
|
||||
}
|
||||
defer ctx.Close()
|
||||
|
||||
eventStart := make(chan struct{})
|
||||
eventDie := make(chan struct{})
|
||||
|
||||
observer, err := newEventObserver(c)
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = observer.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer observer.Stop()
|
||||
|
||||
buildCmd := exec.Command(dockerBinary, "build", "-t", name, ".")
|
||||
buildCmd.Dir = ctx.Dir
|
||||
|
||||
@@ -1917,26 +1914,18 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
|
||||
c.Fatalf("Unable to find build container id in build output:\n%s", outputBuffer.String())
|
||||
}
|
||||
|
||||
matchStart := regexp.MustCompile(buildID + `.* start\z`)
|
||||
matchDie := regexp.MustCompile(buildID + `.* die\z`)
|
||||
|
||||
matcher := func(text string) {
|
||||
switch {
|
||||
case matchStart.MatchString(text):
|
||||
close(eventStart)
|
||||
case matchDie.MatchString(text):
|
||||
close(eventDie)
|
||||
}
|
||||
testActions := map[string]chan bool{
|
||||
"start": make(chan bool),
|
||||
"die": make(chan bool),
|
||||
}
|
||||
go observer.Match(matcher)
|
||||
|
||||
go observer.Match(matchEventLine(buildID, "container", testActions))
|
||||
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
c.Fatal(observer.TimeoutError(buildID, "start"))
|
||||
case <-eventStart:
|
||||
// Proceeds from here when we see the container fly past in the
|
||||
// output of "docker events".
|
||||
// Now we know the container is running.
|
||||
case <-testActions["start"]:
|
||||
// ignore, done
|
||||
}
|
||||
|
||||
// Send a kill to the `docker build` command.
|
||||
@@ -1952,13 +1941,10 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
|
||||
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
// If we don't get here in a timely fashion, it wasn't killed.
|
||||
c.Fatal(observer.TimeoutError(buildID, "die"))
|
||||
case <-eventDie:
|
||||
// We saw the container shut down in the `docker events` stream,
|
||||
// as expected.
|
||||
case <-testActions["die"]:
|
||||
// ignore, done
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestBuildRm(c *check.C) {
|
||||
@@ -6489,33 +6475,26 @@ func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) {
|
||||
func (s *DockerSuite) TestBuildTagEvent(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
observer, err := newEventObserver(c, "--filter", "event=tag")
|
||||
c.Assert(err, check.IsNil)
|
||||
err = observer.Start()
|
||||
c.Assert(err, check.IsNil)
|
||||
defer observer.Stop()
|
||||
since := daemonTime(c).Unix()
|
||||
|
||||
dockerFile := `FROM busybox
|
||||
RUN echo events
|
||||
`
|
||||
_, err = buildImage("test", dockerFile, false)
|
||||
_, err := buildImage("test", dockerFile, false)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
matchTag := regexp.MustCompile("test:latest")
|
||||
eventTag := make(chan bool)
|
||||
matcher := func(text string) {
|
||||
if matchTag.MatchString(text) {
|
||||
close(eventTag)
|
||||
out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "type=image")
|
||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
actions := eventActionsByIDAndType(c, events, "test:latest", "image")
|
||||
var foundTag bool
|
||||
for _, a := range actions {
|
||||
if a == "tag" {
|
||||
foundTag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
go observer.Match(matcher)
|
||||
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
c.Fatal(observer.TimeoutError("test:latest", "tag"))
|
||||
case <-eventTag:
|
||||
// We saw the tag event as expected.
|
||||
}
|
||||
c.Assert(foundTag, checker.True, check.Commentf("No tag event found:\n%s", out))
|
||||
}
|
||||
|
||||
// #15780
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -67,7 +66,6 @@ func (s *DockerSuite) TestEventsUntag(c *check.C) {
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsContainerFailStartDie(c *check.C) {
|
||||
|
||||
out, _ := dockerCmd(c, "images", "-q")
|
||||
image := strings.Split(out, "\n")[0]
|
||||
_, _, err := dockerCmdWithError("run", "--name", "testeventdie", image, "blerg")
|
||||
@@ -75,14 +73,12 @@ func (s *DockerSuite) TestEventsContainerFailStartDie(c *check.C) {
|
||||
|
||||
out, _ = dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
events := strings.Split(out, "\n")
|
||||
c.Assert(len(events), checker.GreaterThan, 1) //Missing expected event
|
||||
|
||||
startEvent := strings.Fields(events[len(events)-3])
|
||||
dieEvent := strings.Fields(events[len(events)-2])
|
||||
|
||||
c.Assert(startEvent[len(startEvent)-1], checker.Equals, "start", check.Commentf("event should be start, not %#v", startEvent))
|
||||
c.Assert(dieEvent[len(dieEvent)-1], checker.Equals, "die", check.Commentf("event should be die, not %#v", dieEvent))
|
||||
nEvents := len(events)
|
||||
c.Assert(nEvents, checker.GreaterOrEqualThan, 1) //Missing expected event
|
||||
|
||||
c.Assert(parseEventAction(c, events[nEvents-3]), checker.Equals, "start")
|
||||
c.Assert(parseEventAction(c, events[nEvents-2]), checker.Equals, "die")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsLimit(c *check.C) {
|
||||
@@ -114,65 +110,83 @@ func (s *DockerSuite) TestEventsLimit(c *check.C) {
|
||||
|
||||
func (s *DockerSuite) TestEventsContainerEvents(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
dockerCmd(c, "run", "--rm", "busybox", "true")
|
||||
containerID, _ := dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true")
|
||||
containerID = strings.TrimSpace(containerID)
|
||||
|
||||
out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
events := strings.Split(out, "\n")
|
||||
events = events[:len(events)-1]
|
||||
c.Assert(len(events), checker.GreaterOrEqualThan, 5) //Missing expected event
|
||||
createEvent := strings.Fields(events[len(events)-5])
|
||||
attachEvent := strings.Fields(events[len(events)-4])
|
||||
startEvent := strings.Fields(events[len(events)-3])
|
||||
dieEvent := strings.Fields(events[len(events)-2])
|
||||
destroyEvent := strings.Fields(events[len(events)-1])
|
||||
c.Assert(createEvent[len(createEvent)-1], checker.Equals, "create", check.Commentf("event should be create, not %#v", createEvent))
|
||||
c.Assert(attachEvent[len(attachEvent)-1], checker.Equals, "attach", check.Commentf("event should be attach, not %#v", attachEvent))
|
||||
c.Assert(startEvent[len(startEvent)-1], checker.Equals, "start", check.Commentf("event should be start, not %#v", startEvent))
|
||||
c.Assert(dieEvent[len(dieEvent)-1], checker.Equals, "die", check.Commentf("event should be die, not %#v", dieEvent))
|
||||
c.Assert(destroyEvent[len(destroyEvent)-1], checker.Equals, "destroy", check.Commentf("event should be destroy, not %#v", destroyEvent))
|
||||
|
||||
nEvents := len(events)
|
||||
c.Assert(nEvents, checker.GreaterOrEqualThan, 5) //Missing expected event
|
||||
containerEvents := eventActionsByIDAndType(c, events, "container-events-test", "container")
|
||||
c.Assert(containerEvents, checker.HasLen, 5, check.Commentf("events: %v", events))
|
||||
|
||||
c.Assert(containerEvents[0], checker.Equals, "create", check.Commentf(out))
|
||||
c.Assert(containerEvents[1], checker.Equals, "attach", check.Commentf(out))
|
||||
c.Assert(containerEvents[2], checker.Equals, "start", check.Commentf(out))
|
||||
c.Assert(containerEvents[3], checker.Equals, "die", check.Commentf(out))
|
||||
c.Assert(containerEvents[4], checker.Equals, "destroy", check.Commentf(out))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsContainerEventsSinceUnixEpoch(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
dockerCmd(c, "run", "--rm", "busybox", "true")
|
||||
dockerCmd(c, "run", "--rm", "--name", "since-epoch-test", "busybox", "true")
|
||||
timeBeginning := time.Unix(0, 0).Format(time.RFC3339Nano)
|
||||
timeBeginning = strings.Replace(timeBeginning, "Z", ".000000000Z", -1)
|
||||
out, _ := dockerCmd(c, "events", fmt.Sprintf("--since='%s'", timeBeginning),
|
||||
fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
out, _ := dockerCmd(c, "events", fmt.Sprintf("--since='%s'", timeBeginning), fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
events := strings.Split(out, "\n")
|
||||
events = events[:len(events)-1]
|
||||
c.Assert(len(events), checker.GreaterOrEqualThan, 5) //Missing expected event
|
||||
createEvent := strings.Fields(events[len(events)-5])
|
||||
attachEvent := strings.Fields(events[len(events)-4])
|
||||
startEvent := strings.Fields(events[len(events)-3])
|
||||
dieEvent := strings.Fields(events[len(events)-2])
|
||||
destroyEvent := strings.Fields(events[len(events)-1])
|
||||
c.Assert(createEvent[len(createEvent)-1], checker.Equals, "create", check.Commentf("event should be create, not %#v", createEvent))
|
||||
c.Assert(attachEvent[len(attachEvent)-1], checker.Equals, "attach", check.Commentf("event should be attach, not %#v", attachEvent))
|
||||
c.Assert(startEvent[len(startEvent)-1], checker.Equals, "start", check.Commentf("event should be start, not %#v", startEvent))
|
||||
c.Assert(dieEvent[len(dieEvent)-1], checker.Equals, "die", check.Commentf("event should be die, not %#v", dieEvent))
|
||||
c.Assert(destroyEvent[len(destroyEvent)-1], checker.Equals, "destroy", check.Commentf("event should be destroy, not %#v", destroyEvent))
|
||||
|
||||
nEvents := len(events)
|
||||
c.Assert(nEvents, checker.GreaterOrEqualThan, 5) //Missing expected event
|
||||
containerEvents := eventActionsByIDAndType(c, events, "since-epoch-test", "container")
|
||||
c.Assert(containerEvents, checker.HasLen, 5, check.Commentf("events: %v", events))
|
||||
|
||||
c.Assert(containerEvents[0], checker.Equals, "create", check.Commentf(out))
|
||||
c.Assert(containerEvents[1], checker.Equals, "attach", check.Commentf(out))
|
||||
c.Assert(containerEvents[2], checker.Equals, "start", check.Commentf(out))
|
||||
c.Assert(containerEvents[3], checker.Equals, "die", check.Commentf(out))
|
||||
c.Assert(containerEvents[4], checker.Equals, "destroy", check.Commentf(out))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsImageUntagDelete(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
observer, err := newEventObserver(c)
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = observer.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer observer.Stop()
|
||||
|
||||
name := "testimageevents"
|
||||
_, err := buildImage(name,
|
||||
imageID, err := buildImage(name,
|
||||
`FROM scratch
|
||||
MAINTAINER "docker"`,
|
||||
true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(deleteImages(name), checker.IsNil)
|
||||
out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
events := strings.Split(out, "\n")
|
||||
|
||||
events = events[:len(events)-1]
|
||||
c.Assert(len(events), checker.GreaterOrEqualThan, 2) //Missing expected event
|
||||
untagEvent := strings.Fields(events[len(events)-2])
|
||||
deleteEvent := strings.Fields(events[len(events)-1])
|
||||
c.Assert(untagEvent[len(untagEvent)-1], checker.Equals, "untag", check.Commentf("untag should be untag, not %#v", untagEvent))
|
||||
c.Assert(deleteEvent[len(deleteEvent)-1], checker.Equals, "delete", check.Commentf("untag should be delete, not %#v", untagEvent))
|
||||
testActions := map[string]chan bool{
|
||||
"untag": make(chan bool),
|
||||
"delete": make(chan bool),
|
||||
}
|
||||
|
||||
go observer.Match(matchEventLine(imageID, "image", testActions))
|
||||
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
c.Fatal(observer.TimeoutError(imageID, "untag"))
|
||||
case <-testActions["untag"]:
|
||||
// ignore, done
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
c.Fatal(observer.TimeoutError(imageID, "delete"))
|
||||
case <-testActions["delete"]:
|
||||
// ignore, done
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsImageTag(c *check.C) {
|
||||
@@ -189,10 +203,11 @@ func (s *DockerSuite) TestEventsImageTag(c *check.C) {
|
||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
c.Assert(events, checker.HasLen, 1, check.Commentf("was expecting 1 event. out=%s", out))
|
||||
event := strings.TrimSpace(events[0])
|
||||
expectedStr := image + ": tag"
|
||||
|
||||
c.Assert(event, checker.HasSuffix, expectedStr, check.Commentf("wrong event format. expected='%s' got=%s", expectedStr, event))
|
||||
|
||||
matches := parseEventText(event)
|
||||
c.Assert(matches, checker.Not(checker.IsNil))
|
||||
c.Assert(matchEventID(matches, image), checker.True, check.Commentf("matches: %v\nout:\n%s", matches, out))
|
||||
c.Assert(matches["action"], checker.Equals, "tag")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsImagePull(c *check.C) {
|
||||
@@ -208,68 +223,46 @@ func (s *DockerSuite) TestEventsImagePull(c *check.C) {
|
||||
|
||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
event := strings.TrimSpace(events[len(events)-1])
|
||||
|
||||
c.Assert(event, checker.HasSuffix, "hello-world:latest: pull", check.Commentf("Missing pull event - got:%q", event))
|
||||
matches := parseEventText(event)
|
||||
c.Assert(matches, checker.Not(checker.IsNil))
|
||||
c.Assert(matches["id"], checker.Equals, "hello-world:latest")
|
||||
c.Assert(matches["action"], checker.Equals, "pull")
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsImageImport(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
observer, err := newEventObserver(c)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = observer.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer observer.Stop()
|
||||
|
||||
out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
|
||||
cleanedContainerID := strings.TrimSpace(out)
|
||||
|
||||
out, _, err = runCommandPipelineWithOutput(
|
||||
since := daemonTime(c).Unix()
|
||||
out, _, err := runCommandPipelineWithOutput(
|
||||
exec.Command(dockerBinary, "export", cleanedContainerID),
|
||||
exec.Command(dockerBinary, "import", "-"),
|
||||
)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("import failed with output: %q", out))
|
||||
imageRef := strings.TrimSpace(out)
|
||||
|
||||
eventImport := make(chan bool)
|
||||
matchImport := regexp.MustCompile(imageRef + `: import\z`)
|
||||
matcher := func(text string) {
|
||||
if matchImport.MatchString(text) {
|
||||
close(eventImport)
|
||||
}
|
||||
}
|
||||
go observer.Match(matcher)
|
||||
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
c.Fatal(observer.TimeoutError(imageRef, "import"))
|
||||
case <-eventImport:
|
||||
// ignore, done
|
||||
}
|
||||
out, _ = dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "event=import")
|
||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
c.Assert(events, checker.HasLen, 1)
|
||||
matches := parseEventText(events[0])
|
||||
c.Assert(matches["id"], checker.Equals, imageRef, check.Commentf("matches: %v\nout:\n%s\n", matches, out))
|
||||
c.Assert(matches["action"], checker.Equals, "import", check.Commentf("matches: %v\nout:\n%s\n", matches, out))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsFilters(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
parseEvents := func(out, match string) {
|
||||
events := strings.Split(out, "\n")
|
||||
events = events[:len(events)-1]
|
||||
for _, event := range events {
|
||||
eventFields := strings.Fields(event)
|
||||
eventName := eventFields[len(eventFields)-1]
|
||||
c.Assert(eventName, checker.Matches, match)
|
||||
}
|
||||
}
|
||||
|
||||
since := daemonTime(c).Unix()
|
||||
dockerCmd(c, "run", "--rm", "busybox", "true")
|
||||
dockerCmd(c, "run", "--rm", "busybox", "true")
|
||||
out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "event=die")
|
||||
parseEvents(out, "die")
|
||||
parseEvents(c, out, "die")
|
||||
|
||||
out, _ = dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "event=die", "--filter", "event=start")
|
||||
parseEvents(out, "((die)|(start))")
|
||||
parseEvents(c, out, "die|start")
|
||||
|
||||
// make sure we at least got 2 start events
|
||||
count := strings.Count(out, "start")
|
||||
@@ -385,15 +378,9 @@ func (s *DockerSuite) TestEventsFilterContainer(c *check.C) {
|
||||
return fmt.Errorf("expected 4 events, got %v", events)
|
||||
}
|
||||
for _, event := range events {
|
||||
e := strings.Fields(event)
|
||||
if len(e) < 3 {
|
||||
return fmt.Errorf("got malformed event: %s", event)
|
||||
}
|
||||
|
||||
// Check the id
|
||||
parsedID := strings.TrimSuffix(e[1], ":")
|
||||
if parsedID != id {
|
||||
return fmt.Errorf("expected event for container id %s: %s - parsed container id: %s", id, event, parsedID)
|
||||
matches := parseEventText(event)
|
||||
if !matchEventID(matches, id) {
|
||||
return fmt.Errorf("expected event for container id %s: %s - parsed container id: %s", id, event, matches["id"])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -450,21 +437,21 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) {
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
c.Fatal(observer.TimeoutError(containerID, "create"))
|
||||
case <-eventCreate:
|
||||
case <-testActions["create"]:
|
||||
// ignore, done
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
c.Fatal(observer.TimeoutError(containerID, "start"))
|
||||
case <-eventStart:
|
||||
case <-testActions["start"]:
|
||||
// ignore, done
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
c.Fatal(observer.TimeoutError(containerID, "die"))
|
||||
case <-eventDie:
|
||||
case <-testActions["die"]:
|
||||
// ignore, done
|
||||
}
|
||||
|
||||
@@ -490,7 +477,7 @@ func (s *DockerSuite) TestEventsCommit(c *check.C) {
|
||||
dockerCmd(c, "stop", cID)
|
||||
|
||||
out, _ = dockerCmd(c, "events", "--since=0", "-f", "container="+cID, "--until="+strconv.Itoa(int(since)))
|
||||
c.Assert(out, checker.Contains, " commit\n", check.Commentf("Missing 'commit' log event"))
|
||||
c.Assert(out, checker.Contains, "commit", check.Commentf("Missing 'commit' log event"))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsCopy(c *check.C) {
|
||||
@@ -515,12 +502,12 @@ func (s *DockerSuite) TestEventsCopy(c *check.C) {
|
||||
dockerCmd(c, "cp", "cptest:/tmp/file", tempFile.Name())
|
||||
|
||||
out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=cptest", "--until="+strconv.Itoa(int(since)))
|
||||
c.Assert(out, checker.Contains, " archive-path\n", check.Commentf("Missing 'archive-path' log event\n"))
|
||||
c.Assert(out, checker.Contains, "archive-path", check.Commentf("Missing 'archive-path' log event\n"))
|
||||
|
||||
dockerCmd(c, "cp", tempFile.Name(), "cptest:/tmp/filecopy")
|
||||
|
||||
out, _ = dockerCmd(c, "events", "--since=0", "-f", "container=cptest", "--until="+strconv.Itoa(int(since)))
|
||||
c.Assert(out, checker.Contains, " extract-to-dir\n", check.Commentf("Missing 'extract-to-dir' log event"))
|
||||
c.Assert(out, checker.Contains, "extract-to-dir", check.Commentf("Missing 'extract-to-dir' log event"))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsResize(c *check.C) {
|
||||
@@ -539,7 +526,7 @@ func (s *DockerSuite) TestEventsResize(c *check.C) {
|
||||
dockerCmd(c, "stop", cID)
|
||||
|
||||
out, _ = dockerCmd(c, "events", "--since=0", "-f", "container="+cID, "--until="+strconv.Itoa(int(since)))
|
||||
c.Assert(out, checker.Contains, " resize\n", check.Commentf("Missing 'resize' log event"))
|
||||
c.Assert(out, checker.Contains, "resize", check.Commentf("Missing 'resize' log event"))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsAttach(c *check.C) {
|
||||
@@ -571,7 +558,7 @@ func (s *DockerSuite) TestEventsAttach(c *check.C) {
|
||||
dockerCmd(c, "stop", cID)
|
||||
|
||||
out, _ = dockerCmd(c, "events", "--since=0", "-f", "container="+cID, "--until="+strconv.Itoa(int(since)))
|
||||
c.Assert(out, checker.Contains, " attach\n", check.Commentf("Missing 'attach' log event"))
|
||||
c.Assert(out, checker.Contains, "attach", check.Commentf("Missing 'attach' log event"))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsRename(c *check.C) {
|
||||
@@ -582,7 +569,7 @@ func (s *DockerSuite) TestEventsRename(c *check.C) {
|
||||
dockerCmd(c, "rename", "oldName", "newName")
|
||||
|
||||
out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=newName", "--until="+strconv.Itoa(int(since)))
|
||||
c.Assert(out, checker.Contains, " rename\n", check.Commentf("Missing 'rename' log event\n"))
|
||||
c.Assert(out, checker.Contains, "rename", check.Commentf("Missing 'rename' log event\n"))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsTop(c *check.C) {
|
||||
@@ -597,7 +584,7 @@ func (s *DockerSuite) TestEventsTop(c *check.C) {
|
||||
dockerCmd(c, "stop", cID)
|
||||
|
||||
out, _ = dockerCmd(c, "events", "--since=0", "-f", "container="+cID, "--until="+strconv.Itoa(int(since)))
|
||||
c.Assert(out, checker.Contains, " top\n", check.Commentf("Missing 'top' log event"))
|
||||
c.Assert(out, checker.Contains, " top", check.Commentf("Missing 'top' log event"))
|
||||
}
|
||||
|
||||
// #13753
|
||||
@@ -624,5 +611,5 @@ func (s *DockerRegistrySuite) TestEventsImageFilterPush(c *check.C) {
|
||||
dockerCmd(c, "push", repoName)
|
||||
|
||||
out, _ = dockerCmd(c, "events", "--since=0", "-f", "image="+repoName, "-f", "event=push", "--until="+strconv.Itoa(int(since)))
|
||||
c.Assert(out, checker.Contains, repoName+": push\n", check.Commentf("Missing 'push' log event"))
|
||||
c.Assert(out, checker.Contains, repoName, check.Commentf("Missing 'push' log event for %s", repoName))
|
||||
}
|
||||
|
||||
@@ -65,18 +65,14 @@ func (s *DockerSuite) TestEventsOOMDisableFalse(c *check.C) {
|
||||
|
||||
out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=oomFalse", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
events := strings.Split(strings.TrimSuffix(out, "\n"), "\n")
|
||||
c.Assert(len(events), checker.GreaterOrEqualThan, 5) //Missing expected event
|
||||
nEvents := len(events)
|
||||
|
||||
createEvent := strings.Fields(events[len(events)-5])
|
||||
attachEvent := strings.Fields(events[len(events)-4])
|
||||
startEvent := strings.Fields(events[len(events)-3])
|
||||
oomEvent := strings.Fields(events[len(events)-2])
|
||||
dieEvent := strings.Fields(events[len(events)-1])
|
||||
c.Assert(createEvent[len(createEvent)-1], checker.Equals, "create", check.Commentf("event should be create, not %#v", createEvent))
|
||||
c.Assert(attachEvent[len(attachEvent)-1], checker.Equals, "attach", check.Commentf("event should be attach, not %#v", attachEvent))
|
||||
c.Assert(startEvent[len(startEvent)-1], checker.Equals, "start", check.Commentf("event should be start, not %#v", startEvent))
|
||||
c.Assert(oomEvent[len(oomEvent)-1], checker.Equals, "oom", check.Commentf("event should be oom, not %#v", oomEvent))
|
||||
c.Assert(dieEvent[len(dieEvent)-1], checker.Equals, "die", check.Commentf("event should be die, not %#v", dieEvent))
|
||||
c.Assert(nEvents, checker.GreaterOrEqualThan, 5) //Missing expected event
|
||||
c.Assert(parseEventAction(c, events[nEvents-5]), checker.Equals, "create")
|
||||
c.Assert(parseEventAction(c, events[nEvents-4]), checker.Equals, "attach")
|
||||
c.Assert(parseEventAction(c, events[nEvents-3]), checker.Equals, "start")
|
||||
c.Assert(parseEventAction(c, events[nEvents-2]), checker.Equals, "oom")
|
||||
c.Assert(parseEventAction(c, events[nEvents-1]), checker.Equals, "die")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsOOMDisableTrue(c *check.C) {
|
||||
@@ -98,17 +94,13 @@ func (s *DockerSuite) TestEventsOOMDisableTrue(c *check.C) {
|
||||
|
||||
out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=oomTrue", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
events := strings.Split(strings.TrimSuffix(out, "\n"), "\n")
|
||||
c.Assert(len(events), checker.GreaterOrEqualThan, 4) //Missing expected event
|
||||
nEvents := len(events)
|
||||
c.Assert(nEvents, checker.GreaterOrEqualThan, 4) //Missing expected event
|
||||
|
||||
createEvent := strings.Fields(events[len(events)-4])
|
||||
attachEvent := strings.Fields(events[len(events)-3])
|
||||
startEvent := strings.Fields(events[len(events)-2])
|
||||
oomEvent := strings.Fields(events[len(events)-1])
|
||||
|
||||
c.Assert(createEvent[len(createEvent)-1], checker.Equals, "create", check.Commentf("event should be create, not %#v", createEvent))
|
||||
c.Assert(attachEvent[len(attachEvent)-1], checker.Equals, "attach", check.Commentf("event should be attach, not %#v", attachEvent))
|
||||
c.Assert(startEvent[len(startEvent)-1], checker.Equals, "start", check.Commentf("event should be start, not %#v", startEvent))
|
||||
c.Assert(oomEvent[len(oomEvent)-1], checker.Equals, "oom", check.Commentf("event should be oom, not %#v", oomEvent))
|
||||
c.Assert(parseEventAction(c, events[nEvents-4]), checker.Equals, "create")
|
||||
c.Assert(parseEventAction(c, events[nEvents-3]), checker.Equals, "attach")
|
||||
c.Assert(parseEventAction(c, events[nEvents-2]), checker.Equals, "start")
|
||||
c.Assert(parseEventAction(c, events[nEvents-1]), checker.Equals, "oom")
|
||||
|
||||
out, _ = dockerCmd(c, "inspect", "-f", "{{.State.Status}}", "oomTrue")
|
||||
c.Assert(strings.TrimSpace(out), checker.Equals, "running", check.Commentf("container should be still running"))
|
||||
@@ -116,17 +108,17 @@ func (s *DockerSuite) TestEventsOOMDisableTrue(c *check.C) {
|
||||
}
|
||||
|
||||
// #18453
|
||||
func (s *DockerSuite) TestEventsContainerFilter(c *check.C) {
|
||||
func (s *DockerSuite) TestEventsContainerFilterByName(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
out, _ := dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top")
|
||||
c1 := strings.TrimSpace(out)
|
||||
waitRun(c1)
|
||||
out, _ = dockerCmd(c, "run", "--name=bar", "-d", "busybox", "top")
|
||||
c2 := strings.TrimSpace(out)
|
||||
waitRun(c2)
|
||||
out, _ = dockerCmd(c, "events", "-f", "container=foo", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
c.Assert(out, checker.Contains, c1, check.Commentf("Missing event of container (foo)"))
|
||||
c.Assert(out, checker.Not(checker.Contains), c2, check.Commentf("Should not contain event of container (bar)"))
|
||||
cOut, _ := dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top")
|
||||
c1 := strings.TrimSpace(cOut)
|
||||
waitRun("foo")
|
||||
cOut, _ = dockerCmd(c, "run", "--name=bar", "-d", "busybox", "top")
|
||||
c2 := strings.TrimSpace(cOut)
|
||||
waitRun("bar")
|
||||
out, _ := dockerCmd(c, "events", "-f", "container=foo", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
c.Assert(out, checker.Contains, c1, check.Commentf(out))
|
||||
c.Assert(out, checker.Not(checker.Contains), c2, check.Commentf(out))
|
||||
}
|
||||
|
||||
// #18453
|
||||
|
||||
@@ -23,15 +23,11 @@ func (s *DockerSuite) TestPause(c *check.C) {
|
||||
dockerCmd(c, "unpause", name)
|
||||
|
||||
out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
events := strings.Split(out, "\n")
|
||||
c.Assert(len(events) > 1, checker.Equals, true)
|
||||
|
||||
pauseEvent := strings.Fields(events[len(events)-3])
|
||||
unpauseEvent := strings.Fields(events[len(events)-2])
|
||||
|
||||
c.Assert(pauseEvent[len(pauseEvent)-1], checker.Equals, "pause")
|
||||
c.Assert(unpauseEvent[len(unpauseEvent)-1], checker.Equals, "unpause")
|
||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
actions := eventActionsByIDAndType(c, events, name, "container")
|
||||
|
||||
c.Assert(actions[len(actions)-2], checker.Equals, "pause")
|
||||
c.Assert(actions[len(actions)-1], checker.Equals, "unpause")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
||||
@@ -53,21 +49,12 @@ func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
||||
dockerCmd(c, append([]string{"unpause"}, containers...)...)
|
||||
|
||||
out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()))
|
||||
events := strings.Split(out, "\n")
|
||||
c.Assert(len(events) > len(containers)*3-2, checker.Equals, true)
|
||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
|
||||
pauseEvents := make([][]string, len(containers))
|
||||
unpauseEvents := make([][]string, len(containers))
|
||||
for i := range containers {
|
||||
pauseEvents[i] = strings.Fields(events[len(events)-len(containers)*2-1+i])
|
||||
unpauseEvents[i] = strings.Fields(events[len(events)-len(containers)-1+i])
|
||||
}
|
||||
for _, name := range containers {
|
||||
actions := eventActionsByIDAndType(c, events, name, "container")
|
||||
|
||||
for _, pauseEvent := range pauseEvents {
|
||||
c.Assert(pauseEvent[len(pauseEvent)-1], checker.Equals, "pause")
|
||||
c.Assert(actions[len(actions)-2], checker.Equals, "pause")
|
||||
c.Assert(actions[len(actions)-1], checker.Equals, "unpause")
|
||||
}
|
||||
for _, unpauseEvent := range unpauseEvents {
|
||||
c.Assert(unpauseEvent[len(unpauseEvent)-1], checker.Equals, "unpause")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,24 @@ import (
|
||||
"io"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
var (
|
||||
reTimestamp = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{9}(:?(:?(:?-|\+)\d{2}:\d{2})|Z)`
|
||||
reEventType = `(?P<eventType>\w+)`
|
||||
reAction = `(?P<action>\w+)`
|
||||
reID = `(?P<id>[^\s]+)`
|
||||
reAttributes = `(\s\((?P<attributes>[^\)]+)\))?`
|
||||
reString = fmt.Sprintf(`\A%s\s%s\s%s\s%s%s\z`, reTimestamp, reEventType, reAction, reID, reAttributes)
|
||||
|
||||
// eventCliRegexp is a regular expression that matches all possible event outputs in the cli
|
||||
eventCliRegexp = regexp.MustCompile(reString)
|
||||
)
|
||||
|
||||
// eventMatcher is a function that tries to match an event input.
|
||||
type eventMatcher func(text string)
|
||||
|
||||
@@ -25,7 +39,11 @@ type eventObserver struct {
|
||||
// without running it. Users must call `eventObserver.Start` to start the command.
|
||||
func newEventObserver(c *check.C, args ...string) (*eventObserver, error) {
|
||||
since := daemonTime(c).Unix()
|
||||
return newEventObserverWithBacklog(c, since, args...)
|
||||
}
|
||||
|
||||
// newEventObserverWithBacklog creates a new observer changing the start time of the backlog to return.
|
||||
func newEventObserverWithBacklog(c *check.C, since int64, args ...string) (*eventObserver, error) {
|
||||
cmdArgs := []string{"events", "--since", strconv.FormatInt(since, 10)}
|
||||
if len(args) > 0 {
|
||||
cmdArgs = append(cmdArgs, args...)
|
||||
@@ -51,6 +69,7 @@ func (e *eventObserver) Start() error {
|
||||
// Stop stops the events command.
|
||||
func (e *eventObserver) Stop() {
|
||||
e.command.Process.Kill()
|
||||
e.command.Process.Release()
|
||||
}
|
||||
|
||||
// Match tries to match the events output with a given matcher.
|
||||
@@ -76,3 +95,103 @@ func (e *eventObserver) TimeoutError(id, event string) error {
|
||||
func (e *eventObserver) output() string {
|
||||
return e.buffer.String()
|
||||
}
|
||||
|
||||
// matchEventLine matches a text with the event regular expression.
|
||||
// It returns the action and true if the regular expression matches with the given id and event type.
|
||||
// It returns an empty string and false if there is no match.
|
||||
func matchEventLine(id, eventType string, actions map[string]chan bool) eventMatcher {
|
||||
return func(text string) {
|
||||
matches := parseEventText(text)
|
||||
if matches == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if matchIDAndEventType(matches, id, eventType) {
|
||||
if ch, ok := actions[matches["action"]]; ok {
|
||||
close(ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseEventText parses a line of events coming from the cli and returns
|
||||
// the matchers in a map.
|
||||
func parseEventText(text string) map[string]string {
|
||||
matches := eventCliRegexp.FindAllStringSubmatch(text, -1)
|
||||
if len(matches) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
names := eventCliRegexp.SubexpNames()
|
||||
md := map[string]string{}
|
||||
for i, n := range matches[0] {
|
||||
md[names[i]] = n
|
||||
}
|
||||
return md
|
||||
}
|
||||
|
||||
// parseEventAction parses an event text and returns the action.
|
||||
// It fails if the text is not in the event format.
|
||||
func parseEventAction(c *check.C, text string) string {
|
||||
matches := parseEventText(text)
|
||||
c.Assert(matches, checker.Not(checker.IsNil))
|
||||
return matches["action"]
|
||||
}
|
||||
|
||||
// eventActionsByIDAndType returns the actions for a given id and type.
|
||||
// It fails if the text is not in the event format.
|
||||
func eventActionsByIDAndType(c *check.C, events []string, id, eventType string) []string {
|
||||
var filtered []string
|
||||
for _, event := range events {
|
||||
matches := parseEventText(event)
|
||||
c.Assert(matches, checker.Not(checker.IsNil))
|
||||
if matchIDAndEventType(matches, id, eventType) {
|
||||
filtered = append(filtered, matches["action"])
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// matchIDAndEventType returns true if an event matches a given id and type.
|
||||
// It also resolves names in the event attributes if the id doesn't match.
|
||||
func matchIDAndEventType(matches map[string]string, id, eventType string) bool {
|
||||
return matchEventID(matches, id) && matches["eventType"] == eventType
|
||||
}
|
||||
|
||||
func matchEventID(matches map[string]string, id string) bool {
|
||||
matchID := matches["id"] == id || strings.HasPrefix(matches["id"], id)
|
||||
if !matchID && matches["attributes"] != "" {
|
||||
// try matching a name in the attributes
|
||||
attributes := map[string]string{}
|
||||
for _, a := range strings.Split(matches["attributes"], ", ") {
|
||||
kv := strings.Split(a, "=")
|
||||
attributes[kv[0]] = kv[1]
|
||||
}
|
||||
matchID = attributes["name"] == id
|
||||
}
|
||||
return matchID
|
||||
}
|
||||
|
||||
func parseEvents(c *check.C, out, match string) {
|
||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
for _, event := range events {
|
||||
matches := parseEventText(event)
|
||||
c.Assert(matches, checker.Not(checker.IsNil))
|
||||
matched, err := regexp.MatchString(match, matches["action"])
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(matched, checker.True)
|
||||
}
|
||||
}
|
||||
|
||||
func parseEventsWithID(c *check.C, out, match, id string) {
|
||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
for _, event := range events {
|
||||
matches := parseEventText(event)
|
||||
c.Assert(matches, checker.Not(checker.IsNil))
|
||||
c.Assert(matchEventID(matches, id), checker.True)
|
||||
|
||||
matched, err := regexp.MatchString(match, matches["action"])
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(matched, checker.True)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user