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