github.com/adityamillind98/moby@v23.0.0-rc.4+incompatible/integration-cli/docker_cli_events_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"os/exec"
    11  	"strconv"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/docker/docker/api/types"
    17  	eventtypes "github.com/docker/docker/api/types/events"
    18  	"github.com/docker/docker/client"
    19  	eventstestutils "github.com/docker/docker/daemon/events/testutils"
    20  	"github.com/docker/docker/integration-cli/cli"
    21  	"github.com/docker/docker/integration-cli/cli/build"
    22  	"gotest.tools/v3/assert"
    23  	is "gotest.tools/v3/assert/cmp"
    24  	"gotest.tools/v3/icmd"
    25  )
    26  
    27  type DockerCLIEventSuite struct {
    28  	ds *DockerSuite
    29  }
    30  
    31  func (s *DockerCLIEventSuite) TearDownTest(c *testing.T) {
    32  	s.ds.TearDownTest(c)
    33  }
    34  
    35  func (s *DockerCLIEventSuite) OnTimeout(c *testing.T) {
    36  	s.ds.OnTimeout(c)
    37  }
    38  
    39  func (s *DockerCLIEventSuite) TestEventsTimestampFormats(c *testing.T) {
    40  	name := "events-time-format-test"
    41  
    42  	// Start stopwatch, generate an event
    43  	start := daemonTime(c)
    44  	time.Sleep(1100 * time.Millisecond) // so that first event occur in different second from since (just for the case)
    45  	dockerCmd(c, "run", "--rm", "--name", name, "busybox", "true")
    46  	time.Sleep(1100 * time.Millisecond) // so that until > since
    47  	end := daemonTime(c)
    48  
    49  	// List of available time formats to --since
    50  	unixTs := func(t time.Time) string { return fmt.Sprintf("%v", t.Unix()) }
    51  	rfc3339 := func(t time.Time) string { return t.Format(time.RFC3339) }
    52  	duration := func(t time.Time) string { return time.Since(t).String() }
    53  
    54  	// --since=$start must contain only the 'untag' event
    55  	for _, f := range []func(time.Time) string{unixTs, rfc3339, duration} {
    56  		since, until := f(start), f(end)
    57  		out, _ := dockerCmd(c, "events", "--since="+since, "--until="+until)
    58  		events := strings.Split(out, "\n")
    59  		events = events[:len(events)-1]
    60  
    61  		nEvents := len(events)
    62  		assert.Assert(c, nEvents >= 5)
    63  		containerEvents := eventActionsByIDAndType(c, events, name, "container")
    64  		assert.Assert(c, is.DeepEqual(containerEvents, []string{"create", "attach", "start", "die", "destroy"}), out)
    65  	}
    66  }
    67  
    68  func (s *DockerCLIEventSuite) TestEventsUntag(c *testing.T) {
    69  	image := "busybox"
    70  	dockerCmd(c, "tag", image, "utest:tag1")
    71  	dockerCmd(c, "tag", image, "utest:tag2")
    72  	dockerCmd(c, "rmi", "utest:tag1")
    73  	dockerCmd(c, "rmi", "utest:tag2")
    74  
    75  	result := icmd.RunCmd(icmd.Cmd{
    76  		Command: []string{dockerBinary, "events", "--since=1"},
    77  		Timeout: time.Millisecond * 2500,
    78  	})
    79  	result.Assert(c, icmd.Expected{Timeout: true})
    80  
    81  	events := strings.Split(result.Stdout(), "\n")
    82  	nEvents := len(events)
    83  	// The last element after the split above will be an empty string, so we
    84  	// get the two elements before the last, which are the untags we're
    85  	// looking for.
    86  	for _, v := range events[nEvents-3 : nEvents-1] {
    87  		assert.Check(c, strings.Contains(v, "untag"), "event should be untag")
    88  	}
    89  }
    90  
    91  func (s *DockerCLIEventSuite) TestEventsContainerEvents(c *testing.T) {
    92  	dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true")
    93  
    94  	out, _ := dockerCmd(c, "events", "--until", daemonUnixTime(c))
    95  	events := strings.Split(out, "\n")
    96  	events = events[:len(events)-1]
    97  
    98  	containerEvents := eventActionsByIDAndType(c, events, "container-events-test", "container")
    99  	if len(containerEvents) > 5 {
   100  		containerEvents = containerEvents[:5]
   101  	}
   102  	assert.Assert(c, is.DeepEqual(containerEvents, []string{"create", "attach", "start", "die", "destroy"}), out)
   103  }
   104  
   105  func (s *DockerCLIEventSuite) TestEventsContainerEventsAttrSort(c *testing.T) {
   106  	since := daemonUnixTime(c)
   107  	dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true")
   108  
   109  	out, _ := dockerCmd(c, "events", "--filter", "container=container-events-test", "--since", since, "--until", daemonUnixTime(c))
   110  	events := strings.Split(out, "\n")
   111  
   112  	nEvents := len(events)
   113  	assert.Assert(c, nEvents >= 3)
   114  	matchedEvents := 0
   115  	for _, event := range events {
   116  		matches := eventstestutils.ScanMap(event)
   117  		if matches["eventType"] == "container" && matches["action"] == "create" {
   118  			matchedEvents++
   119  			assert.Check(c, strings.Contains(out, "(image=busybox, name=container-events-test)"), "Event attributes not sorted")
   120  		} else if matches["eventType"] == "container" && matches["action"] == "start" {
   121  			matchedEvents++
   122  			assert.Check(c, strings.Contains(out, "(image=busybox, name=container-events-test)"), "Event attributes not sorted")
   123  		}
   124  	}
   125  	assert.Equal(c, matchedEvents, 2, "missing events for container container-events-test:\n%s", out)
   126  }
   127  
   128  func (s *DockerCLIEventSuite) TestEventsContainerEventsSinceUnixEpoch(c *testing.T) {
   129  	dockerCmd(c, "run", "--rm", "--name", "since-epoch-test", "busybox", "true")
   130  	timeBeginning := time.Unix(0, 0).Format(time.RFC3339Nano)
   131  	timeBeginning = strings.ReplaceAll(timeBeginning, "Z", ".000000000Z")
   132  	out, _ := dockerCmd(c, "events", "--since", timeBeginning, "--until", daemonUnixTime(c))
   133  	events := strings.Split(out, "\n")
   134  	events = events[:len(events)-1]
   135  
   136  	nEvents := len(events)
   137  	assert.Assert(c, nEvents >= 5)
   138  	containerEvents := eventActionsByIDAndType(c, events, "since-epoch-test", "container")
   139  	assert.Assert(c, is.DeepEqual(containerEvents, []string{"create", "attach", "start", "die", "destroy"}), out)
   140  }
   141  
   142  func (s *DockerCLIEventSuite) TestEventsImageTag(c *testing.T) {
   143  	time.Sleep(1 * time.Second) // because API has seconds granularity
   144  	since := daemonUnixTime(c)
   145  	image := "testimageevents:tag"
   146  	dockerCmd(c, "tag", "busybox", image)
   147  
   148  	out, _ := dockerCmd(c, "events",
   149  		"--since", since, "--until", daemonUnixTime(c))
   150  
   151  	events := strings.Split(strings.TrimSpace(out), "\n")
   152  	assert.Equal(c, len(events), 1, "was expecting 1 event. out=%s", out)
   153  	event := strings.TrimSpace(events[0])
   154  
   155  	matches := eventstestutils.ScanMap(event)
   156  	assert.Assert(c, matchEventID(matches, image), "matches: %v\nout:\n%s", matches, out)
   157  	assert.Equal(c, matches["action"], "tag")
   158  }
   159  
   160  func (s *DockerCLIEventSuite) TestEventsImagePull(c *testing.T) {
   161  	// TODO Windows: Enable this test once pull and reliable image names are available
   162  	testRequires(c, DaemonIsLinux)
   163  	since := daemonUnixTime(c)
   164  	testRequires(c, Network)
   165  
   166  	dockerCmd(c, "pull", "hello-world")
   167  
   168  	out, _ := dockerCmd(c, "events",
   169  		"--since", since, "--until", daemonUnixTime(c))
   170  
   171  	events := strings.Split(strings.TrimSpace(out), "\n")
   172  	event := strings.TrimSpace(events[len(events)-1])
   173  	matches := eventstestutils.ScanMap(event)
   174  	assert.Equal(c, matches["id"], "hello-world:latest")
   175  	assert.Equal(c, matches["action"], "pull")
   176  }
   177  
   178  func (s *DockerCLIEventSuite) TestEventsImageImport(c *testing.T) {
   179  	// TODO Windows CI. This should be portable once export/import are
   180  	// more reliable (@swernli)
   181  	testRequires(c, DaemonIsLinux)
   182  
   183  	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
   184  	cleanedContainerID := strings.TrimSpace(out)
   185  
   186  	since := daemonUnixTime(c)
   187  	out, err := RunCommandPipelineWithOutput(
   188  		exec.Command(dockerBinary, "export", cleanedContainerID),
   189  		exec.Command(dockerBinary, "import", "-"),
   190  	)
   191  	assert.NilError(c, err, "import failed with output: %q", out)
   192  	imageRef := strings.TrimSpace(out)
   193  
   194  	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=import")
   195  	events := strings.Split(strings.TrimSpace(out), "\n")
   196  	assert.Equal(c, len(events), 1)
   197  	matches := eventstestutils.ScanMap(events[0])
   198  	assert.Equal(c, matches["id"], imageRef, "matches: %v\nout:\n%s\n", matches, out)
   199  	assert.Equal(c, matches["action"], "import", "matches: %v\nout:\n%s\n", matches, out)
   200  }
   201  
   202  func (s *DockerCLIEventSuite) TestEventsImageLoad(c *testing.T) {
   203  	testRequires(c, DaemonIsLinux)
   204  	myImageName := "footest:v1"
   205  	dockerCmd(c, "tag", "busybox", myImageName)
   206  	since := daemonUnixTime(c)
   207  
   208  	out, _ := dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
   209  	longImageID := strings.TrimSpace(out)
   210  	assert.Assert(c, longImageID != "", "Id should not be empty")
   211  
   212  	dockerCmd(c, "save", "-o", "saveimg.tar", myImageName)
   213  	dockerCmd(c, "rmi", myImageName)
   214  	out, _ = dockerCmd(c, "images", "-q", myImageName)
   215  	noImageID := strings.TrimSpace(out)
   216  	assert.Equal(c, noImageID, "", "Should not have any image")
   217  	dockerCmd(c, "load", "-i", "saveimg.tar")
   218  
   219  	result := icmd.RunCommand("rm", "-rf", "saveimg.tar")
   220  	result.Assert(c, icmd.Success)
   221  
   222  	out, _ = dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
   223  	imageID := strings.TrimSpace(out)
   224  	assert.Equal(c, imageID, longImageID, "Should have same image id as before")
   225  
   226  	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=load")
   227  	events := strings.Split(strings.TrimSpace(out), "\n")
   228  	assert.Equal(c, len(events), 1)
   229  	matches := eventstestutils.ScanMap(events[0])
   230  	assert.Equal(c, matches["id"], imageID, "matches: %v\nout:\n%s\n", matches, out)
   231  	assert.Equal(c, matches["action"], "load", "matches: %v\nout:\n%s\n", matches, out)
   232  
   233  	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=save")
   234  	events = strings.Split(strings.TrimSpace(out), "\n")
   235  	assert.Equal(c, len(events), 1)
   236  	matches = eventstestutils.ScanMap(events[0])
   237  	assert.Equal(c, matches["id"], imageID, "matches: %v\nout:\n%s\n", matches, out)
   238  	assert.Equal(c, matches["action"], "save", "matches: %v\nout:\n%s\n", matches, out)
   239  }
   240  
   241  func (s *DockerCLIEventSuite) TestEventsPluginOps(c *testing.T) {
   242  	testRequires(c, DaemonIsLinux, IsAmd64, Network)
   243  
   244  	since := daemonUnixTime(c)
   245  
   246  	dockerCmd(c, "plugin", "install", pNameWithTag, "--grant-all-permissions")
   247  	dockerCmd(c, "plugin", "disable", pNameWithTag)
   248  	dockerCmd(c, "plugin", "remove", pNameWithTag)
   249  
   250  	out, _ := dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c))
   251  	events := strings.Split(out, "\n")
   252  	events = events[:len(events)-1]
   253  
   254  	assert.Assert(c, len(events) >= 4)
   255  
   256  	pluginEvents := eventActionsByIDAndType(c, events, pNameWithTag, "plugin")
   257  	assert.Assert(c, is.DeepEqual(pluginEvents, []string{"pull", "enable", "disable", "remove"}), out)
   258  }
   259  
   260  func (s *DockerCLIEventSuite) TestEventsFilters(c *testing.T) {
   261  	since := daemonUnixTime(c)
   262  	dockerCmd(c, "run", "--rm", "busybox", "true")
   263  	dockerCmd(c, "run", "--rm", "busybox", "true")
   264  	out, _ := dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=die")
   265  	parseEvents(c, out, "die")
   266  
   267  	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=die", "--filter", "event=start")
   268  	parseEvents(c, out, "die|start")
   269  
   270  	// make sure we at least got 2 start events
   271  	count := strings.Count(out, "start")
   272  	assert.Assert(c, count >= 2, "should have had 2 start events but had %d, out: %s", count, out)
   273  }
   274  
   275  func (s *DockerCLIEventSuite) TestEventsFilterImageName(c *testing.T) {
   276  	since := daemonUnixTime(c)
   277  
   278  	out, _ := dockerCmd(c, "run", "--name", "container_1", "-d", "busybox:latest", "true")
   279  	container1 := strings.TrimSpace(out)
   280  
   281  	out, _ = dockerCmd(c, "run", "--name", "container_2", "-d", "busybox", "true")
   282  	container2 := strings.TrimSpace(out)
   283  
   284  	name := "busybox"
   285  	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("image=%s", name))
   286  	events := strings.Split(out, "\n")
   287  	events = events[:len(events)-1]
   288  	assert.Assert(c, len(events) != 0, "Expected events but found none for the image busybox:latest")
   289  	count1 := 0
   290  	count2 := 0
   291  
   292  	for _, e := range events {
   293  		if strings.Contains(e, container1) {
   294  			count1++
   295  		} else if strings.Contains(e, container2) {
   296  			count2++
   297  		}
   298  	}
   299  	assert.Assert(c, count1 != 0, "Expected event from container but got %d from %s", count1, container1)
   300  	assert.Assert(c, count2 != 0, "Expected event from container but got %d from %s", count2, container2)
   301  }
   302  
   303  func (s *DockerCLIEventSuite) TestEventsFilterLabels(c *testing.T) {
   304  	since := strconv.FormatUint(uint64(daemonTime(c).Unix()), 10)
   305  	label := "io.docker.testing=foo"
   306  
   307  	out, exit := dockerCmd(c, "create", "-l", label, "busybox")
   308  	assert.Equal(c, exit, 0)
   309  	container1 := strings.TrimSpace(out)
   310  
   311  	out, exit = dockerCmd(c, "create", "busybox")
   312  	assert.Equal(c, exit, 0)
   313  	container2 := strings.TrimSpace(out)
   314  
   315  	// fetch events with `--until`, so that the client detaches after a second
   316  	// instead of staying attached, waiting for more events to arrive.
   317  	out, _ = dockerCmd(
   318  		c,
   319  		"events",
   320  		"--since", since,
   321  		"--until", strconv.FormatUint(uint64(daemonTime(c).Add(time.Second).Unix()), 10),
   322  		"--filter", "label="+label,
   323  	)
   324  
   325  	events := strings.Split(strings.TrimSpace(out), "\n")
   326  	assert.Assert(c, len(events) > 0)
   327  
   328  	var found bool
   329  	for _, e := range events {
   330  		if strings.Contains(e, container1) {
   331  			found = true
   332  		}
   333  		assert.Assert(c, !strings.Contains(e, container2))
   334  	}
   335  	assert.Assert(c, found)
   336  }
   337  
   338  func (s *DockerCLIEventSuite) TestEventsFilterImageLabels(c *testing.T) {
   339  	since := daemonUnixTime(c)
   340  	name := "labelfiltertest"
   341  	label := "io.docker.testing=image"
   342  
   343  	// Build a test image.
   344  	buildImageSuccessfully(c, name, build.WithDockerfile(fmt.Sprintf(`
   345  		FROM busybox:latest
   346  		LABEL %s`, label)))
   347  	dockerCmd(c, "tag", name, "labelfiltertest:tag1")
   348  	dockerCmd(c, "tag", name, "labelfiltertest:tag2")
   349  	dockerCmd(c, "tag", "busybox:latest", "labelfiltertest:tag3")
   350  
   351  	out, _ := dockerCmd(
   352  		c,
   353  		"events",
   354  		"--since", since,
   355  		"--until", daemonUnixTime(c),
   356  		"--filter", fmt.Sprintf("label=%s", label),
   357  		"--filter", "type=image")
   358  
   359  	events := strings.Split(strings.TrimSpace(out), "\n")
   360  
   361  	// 2 events from the "docker tag" command, another one is from "docker build"
   362  	assert.Equal(c, len(events), 3, "Events == %s", events)
   363  	for _, e := range events {
   364  		assert.Check(c, strings.Contains(e, "labelfiltertest"))
   365  	}
   366  }
   367  
   368  func (s *DockerCLIEventSuite) TestEventsFilterContainer(c *testing.T) {
   369  	since := daemonUnixTime(c)
   370  	nameID := make(map[string]string)
   371  
   372  	for _, name := range []string{"container_1", "container_2"} {
   373  		dockerCmd(c, "run", "--name", name, "busybox", "true")
   374  		id := inspectField(c, name, "Id")
   375  		nameID[name] = id
   376  	}
   377  
   378  	until := daemonUnixTime(c)
   379  
   380  	checkEvents := func(id string, events []string) error {
   381  		if len(events) != 4 { // create, attach, start, die
   382  			return fmt.Errorf("expected 4 events, got %v", events)
   383  		}
   384  		for _, event := range events {
   385  			matches := eventstestutils.ScanMap(event)
   386  			if !matchEventID(matches, id) {
   387  				return fmt.Errorf("expected event for container id %s: %s - parsed container id: %s", id, event, matches["id"])
   388  			}
   389  		}
   390  		return nil
   391  	}
   392  
   393  	for name, ID := range nameID {
   394  		// filter by names
   395  		out, _ := dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "container="+name)
   396  		events := strings.Split(strings.TrimSuffix(out, "\n"), "\n")
   397  		assert.NilError(c, checkEvents(ID, events))
   398  
   399  		// filter by ID's
   400  		out, _ = dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "container="+ID)
   401  		events = strings.Split(strings.TrimSuffix(out, "\n"), "\n")
   402  		assert.NilError(c, checkEvents(ID, events))
   403  	}
   404  }
   405  
   406  func (s *DockerCLIEventSuite) TestEventsCommit(c *testing.T) {
   407  	// Problematic on Windows as cannot commit a running container
   408  	testRequires(c, DaemonIsLinux)
   409  
   410  	out := runSleepingContainer(c)
   411  	cID := strings.TrimSpace(out)
   412  	cli.WaitRun(c, cID)
   413  
   414  	cli.DockerCmd(c, "commit", "-m", "test", cID)
   415  	cli.DockerCmd(c, "stop", cID)
   416  	cli.WaitExited(c, cID, 5*time.Second)
   417  
   418  	until := daemonUnixTime(c)
   419  	out = cli.DockerCmd(c, "events", "-f", "container="+cID, "--until="+until).Combined()
   420  	assert.Assert(c, strings.Contains(out, "commit"), "Missing 'commit' log event")
   421  }
   422  
   423  func (s *DockerCLIEventSuite) TestEventsCopy(c *testing.T) {
   424  	// Build a test image.
   425  	buildImageSuccessfully(c, "cpimg", build.WithDockerfile(`
   426  		  FROM busybox
   427  		  RUN echo HI > /file`))
   428  	id := getIDByName(c, "cpimg")
   429  
   430  	// Create an empty test file.
   431  	tempFile, err := os.CreateTemp("", "test-events-copy-")
   432  	assert.NilError(c, err)
   433  	defer os.Remove(tempFile.Name())
   434  
   435  	assert.NilError(c, tempFile.Close())
   436  
   437  	dockerCmd(c, "create", "--name=cptest", id)
   438  
   439  	dockerCmd(c, "cp", "cptest:/file", tempFile.Name())
   440  
   441  	until := daemonUnixTime(c)
   442  	out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=cptest", "--until="+until)
   443  	assert.Assert(c, strings.Contains(out, "archive-path"), "Missing 'archive-path' log event")
   444  
   445  	dockerCmd(c, "cp", tempFile.Name(), "cptest:/filecopy")
   446  
   447  	until = daemonUnixTime(c)
   448  	out, _ = dockerCmd(c, "events", "-f", "container=cptest", "--until="+until)
   449  	assert.Assert(c, strings.Contains(out, "extract-to-dir"), "Missing 'extract-to-dir' log event")
   450  }
   451  
   452  func (s *DockerCLIEventSuite) TestEventsResize(c *testing.T) {
   453  	out := runSleepingContainer(c, "-d", "-t")
   454  	cID := strings.TrimSpace(out)
   455  	assert.NilError(c, waitRun(cID))
   456  
   457  	cli, err := client.NewClientWithOpts(client.FromEnv)
   458  	assert.NilError(c, err)
   459  	defer cli.Close()
   460  
   461  	options := types.ResizeOptions{
   462  		Height: 80,
   463  		Width:  24,
   464  	}
   465  	err = cli.ContainerResize(context.Background(), cID, options)
   466  	assert.NilError(c, err)
   467  
   468  	dockerCmd(c, "stop", cID)
   469  
   470  	until := daemonUnixTime(c)
   471  	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until="+until)
   472  	assert.Assert(c, strings.Contains(out, "resize"), "Missing 'resize' log event")
   473  }
   474  
   475  func (s *DockerCLIEventSuite) TestEventsAttach(c *testing.T) {
   476  	// TODO Windows CI: Figure out why this test fails intermittently (TP5).
   477  	testRequires(c, DaemonIsLinux)
   478  
   479  	out := cli.DockerCmd(c, "run", "-di", "busybox", "cat").Combined()
   480  	cID := strings.TrimSpace(out)
   481  	cli.WaitRun(c, cID)
   482  
   483  	cmd := exec.Command(dockerBinary, "attach", cID)
   484  	stdin, err := cmd.StdinPipe()
   485  	assert.NilError(c, err)
   486  	defer stdin.Close()
   487  	stdout, err := cmd.StdoutPipe()
   488  	assert.NilError(c, err)
   489  	defer stdout.Close()
   490  	assert.NilError(c, cmd.Start())
   491  	defer func() {
   492  		cmd.Process.Kill()
   493  		cmd.Wait()
   494  	}()
   495  
   496  	// Make sure we're done attaching by writing/reading some stuff
   497  	_, err = stdin.Write([]byte("hello\n"))
   498  	assert.NilError(c, err)
   499  	out, err = bufio.NewReader(stdout).ReadString('\n')
   500  	assert.NilError(c, err)
   501  	assert.Equal(c, strings.TrimSpace(out), "hello")
   502  
   503  	assert.NilError(c, stdin.Close())
   504  
   505  	cli.DockerCmd(c, "kill", cID)
   506  	cli.WaitExited(c, cID, 5*time.Second)
   507  
   508  	until := daemonUnixTime(c)
   509  	out = cli.DockerCmd(c, "events", "-f", "container="+cID, "--until="+until).Combined()
   510  	assert.Assert(c, strings.Contains(out, "attach"), "Missing 'attach' log event")
   511  }
   512  
   513  func (s *DockerCLIEventSuite) TestEventsRename(c *testing.T) {
   514  	out, _ := dockerCmd(c, "run", "--name", "oldName", "busybox", "true")
   515  	cID := strings.TrimSpace(out)
   516  	dockerCmd(c, "rename", "oldName", "newName")
   517  
   518  	until := daemonUnixTime(c)
   519  	// filter by the container id because the name in the event will be the new name.
   520  	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until", until)
   521  	assert.Assert(c, strings.Contains(out, "rename"), "Missing 'rename' log event")
   522  }
   523  
   524  func (s *DockerCLIEventSuite) TestEventsTop(c *testing.T) {
   525  	// Problematic on Windows as Windows does not support top
   526  	testRequires(c, DaemonIsLinux)
   527  
   528  	out := runSleepingContainer(c, "-d")
   529  	cID := strings.TrimSpace(out)
   530  	assert.NilError(c, waitRun(cID))
   531  
   532  	dockerCmd(c, "top", cID)
   533  	dockerCmd(c, "stop", cID)
   534  
   535  	until := daemonUnixTime(c)
   536  	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until="+until)
   537  	assert.Assert(c, strings.Contains(out, "top"), "Missing 'top' log event")
   538  }
   539  
   540  // #14316
   541  func (s *DockerRegistrySuite) TestEventsImageFilterPush(c *testing.T) {
   542  	// Problematic to port for Windows CI during TP5 timeframe until
   543  	// supporting push
   544  	testRequires(c, DaemonIsLinux)
   545  	testRequires(c, Network)
   546  	repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL)
   547  
   548  	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
   549  	cID := strings.TrimSpace(out)
   550  	assert.NilError(c, waitRun(cID))
   551  
   552  	dockerCmd(c, "commit", cID, repoName)
   553  	dockerCmd(c, "stop", cID)
   554  	dockerCmd(c, "push", repoName)
   555  
   556  	until := daemonUnixTime(c)
   557  	out, _ = dockerCmd(c, "events", "-f", "image="+repoName, "-f", "event=push", "--until", until)
   558  	assert.Assert(c, strings.Contains(out, repoName), "Missing 'push' log event for %s", repoName)
   559  }
   560  
   561  func (s *DockerCLIEventSuite) TestEventsFilterType(c *testing.T) {
   562  	// FIXME(vdemeester) fails on e2e run
   563  	testRequires(c, testEnv.IsLocalDaemon)
   564  	since := daemonUnixTime(c)
   565  	name := "labelfiltertest"
   566  	label := "io.docker.testing=image"
   567  
   568  	// Build a test image.
   569  	buildImageSuccessfully(c, name, build.WithDockerfile(fmt.Sprintf(`
   570  		FROM busybox:latest
   571  		LABEL %s`, label)))
   572  	dockerCmd(c, "tag", name, "labelfiltertest:tag1")
   573  	dockerCmd(c, "tag", name, "labelfiltertest:tag2")
   574  	dockerCmd(c, "tag", "busybox:latest", "labelfiltertest:tag3")
   575  
   576  	out, _ := dockerCmd(
   577  		c,
   578  		"events",
   579  		"--since", since,
   580  		"--until", daemonUnixTime(c),
   581  		"--filter", fmt.Sprintf("label=%s", label),
   582  		"--filter", "type=image")
   583  
   584  	events := strings.Split(strings.TrimSpace(out), "\n")
   585  
   586  	// 2 events from the "docker tag" command, another one is from "docker build"
   587  	assert.Equal(c, len(events), 3, "Events == %s", events)
   588  	for _, e := range events {
   589  		assert.Check(c, strings.Contains(e, "labelfiltertest"))
   590  	}
   591  
   592  	out, _ = dockerCmd(
   593  		c,
   594  		"events",
   595  		"--since", since,
   596  		"--until", daemonUnixTime(c),
   597  		"--filter", fmt.Sprintf("label=%s", label),
   598  		"--filter", "type=container")
   599  	events = strings.Split(strings.TrimSpace(out), "\n")
   600  
   601  	// Events generated by the container that builds the image
   602  	assert.Equal(c, len(events), 2, "Events == %s", events)
   603  
   604  	out, _ = dockerCmd(
   605  		c,
   606  		"events",
   607  		"--since", since,
   608  		"--until", daemonUnixTime(c),
   609  		"--filter", "type=network")
   610  	events = strings.Split(strings.TrimSpace(out), "\n")
   611  	assert.Assert(c, len(events) >= 1, "Events == %s", events)
   612  }
   613  
   614  // #25798
   615  func (s *DockerCLIEventSuite) TestEventsSpecialFiltersWithExecCreate(c *testing.T) {
   616  	since := daemonUnixTime(c)
   617  	runSleepingContainer(c, "--name", "test-container", "-d")
   618  	waitRun("test-container")
   619  
   620  	dockerCmd(c, "exec", "test-container", "echo", "hello-world")
   621  
   622  	out, _ := dockerCmd(
   623  		c,
   624  		"events",
   625  		"--since", since,
   626  		"--until", daemonUnixTime(c),
   627  		"--filter",
   628  		"event='exec_create: echo hello-world'",
   629  	)
   630  
   631  	events := strings.Split(strings.TrimSpace(out), "\n")
   632  	assert.Equal(c, len(events), 1, out)
   633  
   634  	out, _ = dockerCmd(
   635  		c,
   636  		"events",
   637  		"--since", since,
   638  		"--until", daemonUnixTime(c),
   639  		"--filter",
   640  		"event=exec_create",
   641  	)
   642  	assert.Equal(c, len(events), 1, out)
   643  }
   644  
   645  func (s *DockerCLIEventSuite) TestEventsFilterImageInContainerAction(c *testing.T) {
   646  	since := daemonUnixTime(c)
   647  	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
   648  	waitRun("test-container")
   649  
   650  	out, _ := dockerCmd(c, "events", "--filter", "image=busybox", "--since", since, "--until", daemonUnixTime(c))
   651  	events := strings.Split(strings.TrimSpace(out), "\n")
   652  	assert.Assert(c, len(events) > 1, out)
   653  }
   654  
   655  func (s *DockerCLIEventSuite) TestEventsContainerRestart(c *testing.T) {
   656  	dockerCmd(c, "run", "-d", "--name=testEvent", "--restart=on-failure:3", "busybox", "false")
   657  
   658  	// wait until test2 is auto removed.
   659  	waitTime := 10 * time.Second
   660  	if testEnv.OSType == "windows" {
   661  		// Windows takes longer...
   662  		waitTime = 90 * time.Second
   663  	}
   664  
   665  	err := waitInspect("testEvent", "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTime)
   666  	assert.NilError(c, err)
   667  
   668  	var (
   669  		createCount int
   670  		startCount  int
   671  		dieCount    int
   672  	)
   673  	out, _ := dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c), "-f", "container=testEvent")
   674  	events := strings.Split(strings.TrimSpace(out), "\n")
   675  
   676  	nEvents := len(events)
   677  	assert.Assert(c, nEvents >= 1)
   678  	actions := eventActionsByIDAndType(c, events, "testEvent", "container")
   679  
   680  	for _, a := range actions {
   681  		switch a {
   682  		case "create":
   683  			createCount++
   684  		case "start":
   685  			startCount++
   686  		case "die":
   687  			dieCount++
   688  		}
   689  	}
   690  	assert.Equal(c, createCount, 1, "testEvent should be created 1 times: %v", actions)
   691  	assert.Equal(c, startCount, 4, "testEvent should start 4 times: %v", actions)
   692  	assert.Equal(c, dieCount, 4, "testEvent should die 4 times: %v", actions)
   693  }
   694  
   695  func (s *DockerCLIEventSuite) TestEventsSinceInTheFuture(c *testing.T) {
   696  	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
   697  	waitRun("test-container")
   698  
   699  	since := daemonTime(c)
   700  	until := since.Add(time.Duration(-24) * time.Hour)
   701  	out, _, err := dockerCmdWithError("events", "--filter", "image=busybox", "--since", parseEventTime(since), "--until", parseEventTime(until))
   702  
   703  	assert.ErrorContains(c, err, "")
   704  	assert.Assert(c, strings.Contains(out, "cannot be after `until`"))
   705  }
   706  
   707  func (s *DockerCLIEventSuite) TestEventsUntilInThePast(c *testing.T) {
   708  	since := daemonUnixTime(c)
   709  
   710  	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
   711  	waitRun("test-container")
   712  
   713  	until := daemonUnixTime(c)
   714  
   715  	dockerCmd(c, "run", "--name", "test-container2", "-d", "busybox", "true")
   716  	waitRun("test-container2")
   717  
   718  	out, _ := dockerCmd(c, "events", "--filter", "image=busybox", "--since", since, "--until", until)
   719  
   720  	assert.Assert(c, !strings.Contains(out, "test-container2"))
   721  	assert.Assert(c, strings.Contains(out, "test-container"))
   722  }
   723  
   724  func (s *DockerCLIEventSuite) TestEventsFormat(c *testing.T) {
   725  	since := daemonUnixTime(c)
   726  	dockerCmd(c, "run", "--rm", "busybox", "true")
   727  	dockerCmd(c, "run", "--rm", "busybox", "true")
   728  	out, _ := dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--format", "{{json .}}")
   729  	dec := json.NewDecoder(strings.NewReader(out))
   730  	// make sure we got 2 start events
   731  	startCount := 0
   732  	for {
   733  		var err error
   734  		var ev eventtypes.Message
   735  		if err = dec.Decode(&ev); err == io.EOF {
   736  			break
   737  		}
   738  		assert.NilError(c, err)
   739  		if ev.Status == "start" {
   740  			startCount++
   741  		}
   742  	}
   743  
   744  	assert.Equal(c, startCount, 2, "should have had 2 start events but had %d, out: %s", startCount, out)
   745  }
   746  
   747  func (s *DockerCLIEventSuite) TestEventsFormatBadFunc(c *testing.T) {
   748  	// make sure it fails immediately, without receiving any event
   749  	result := dockerCmdWithResult("events", "--format", "{{badFuncString .}}")
   750  	result.Assert(c, icmd.Expected{
   751  		Error:    "exit status 64",
   752  		ExitCode: 64,
   753  		Err:      "Error parsing format: template: :1: function \"badFuncString\" not defined",
   754  	})
   755  }
   756  
   757  func (s *DockerCLIEventSuite) TestEventsFormatBadField(c *testing.T) {
   758  	// make sure it fails immediately, without receiving any event
   759  	result := dockerCmdWithResult("events", "--format", "{{.badFieldString}}")
   760  	result.Assert(c, icmd.Expected{
   761  		Error:    "exit status 64",
   762  		ExitCode: 64,
   763  		Err:      "Error parsing format: template: :1:2: executing \"\" at <.badFieldString>: can't evaluate field badFieldString in type *events.Message",
   764  	})
   765  }