docker_cli_stats_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package main
  2. import (
  3. "bufio"
  4. "context"
  5. "os/exec"
  6. "regexp"
  7. "strings"
  8. "testing"
  9. "time"
  10. "github.com/docker/docker/integration-cli/cli"
  11. "gotest.tools/v3/assert"
  12. is "gotest.tools/v3/assert/cmp"
  13. )
  14. type DockerCLIStatsSuite struct {
  15. ds *DockerSuite
  16. }
  17. func (s *DockerCLIStatsSuite) TearDownTest(ctx context.Context, c *testing.T) {
  18. s.ds.TearDownTest(ctx, c)
  19. }
  20. func (s *DockerCLIStatsSuite) OnTimeout(c *testing.T) {
  21. s.ds.OnTimeout(c)
  22. }
  23. func (s *DockerCLIStatsSuite) TestStatsNoStream(c *testing.T) {
  24. // Windows does not support stats
  25. testRequires(c, DaemonIsLinux)
  26. id := cli.DockerCmd(c, "run", "-d", "busybox", "top").Stdout()
  27. id = strings.TrimSpace(id)
  28. cli.WaitRun(c, id)
  29. statsCmd := exec.Command(dockerBinary, "stats", "--no-stream", id)
  30. type output struct {
  31. out []byte
  32. err error
  33. }
  34. ch := make(chan output, 1)
  35. go func() {
  36. out, err := statsCmd.Output()
  37. ch <- output{out, err}
  38. }()
  39. select {
  40. case outerr := <-ch:
  41. assert.NilError(c, outerr.err, "Error running stats: %v", outerr.err)
  42. assert.Assert(c, is.Contains(string(outerr.out), id[:12]), "running container wasn't present in output")
  43. case <-time.After(3 * time.Second):
  44. statsCmd.Process.Kill()
  45. c.Fatalf("stats did not return immediately when not streaming")
  46. }
  47. }
  48. func (s *DockerCLIStatsSuite) TestStatsContainerNotFound(c *testing.T) {
  49. // Windows does not support stats
  50. testRequires(c, DaemonIsLinux)
  51. out, _, err := dockerCmdWithError("stats", "notfound")
  52. assert.ErrorContains(c, err, "")
  53. assert.Assert(c, is.Contains(out, "No such container: notfound"), "Expected to fail on not found container stats, got %q instead", out)
  54. out, _, err = dockerCmdWithError("stats", "--no-stream", "notfound")
  55. assert.ErrorContains(c, err, "")
  56. assert.Assert(c, is.Contains(out, "No such container: notfound"), "Expected to fail on not found container stats with --no-stream, got %q instead", out)
  57. }
  58. func (s *DockerCLIStatsSuite) TestStatsAllRunningNoStream(c *testing.T) {
  59. // Windows does not support stats
  60. testRequires(c, DaemonIsLinux)
  61. id1 := cli.DockerCmd(c, "run", "-d", "busybox", "top").Stdout()
  62. id1 = strings.TrimSpace(id1)[:12]
  63. cli.WaitRun(c, id1)
  64. id2 := cli.DockerCmd(c, "run", "-d", "busybox", "top").Stdout()
  65. id2 = strings.TrimSpace(id2)[:12]
  66. cli.WaitRun(c, id2)
  67. id3 := cli.DockerCmd(c, "run", "-d", "busybox", "top").Stdout()
  68. id3 = strings.TrimSpace(id3)[:12]
  69. cli.WaitRun(c, id3)
  70. cli.DockerCmd(c, "stop", id3)
  71. out := cli.DockerCmd(c, "stats", "--no-stream").Combined()
  72. if !strings.Contains(out, id1) || !strings.Contains(out, id2) {
  73. c.Fatalf("Expected stats output to contain both %s and %s, got %s", id1, id2, out)
  74. }
  75. if strings.Contains(out, id3) {
  76. c.Fatalf("Did not expect %s in stats, got %s", id3, out)
  77. }
  78. // check output contains real data, but not all zeros
  79. reg, _ := regexp.Compile("[1-9]+")
  80. // split output with "\n", outLines[1] is id2's output
  81. // outLines[2] is id1's output
  82. outLines := strings.Split(out, "\n")
  83. // check stat result of id2 contains real data
  84. realData := reg.Find([]byte(outLines[1][12:]))
  85. assert.Assert(c, realData != nil, "stat result are empty: %s", out)
  86. // check stat result of id1 contains real data
  87. realData = reg.Find([]byte(outLines[2][12:]))
  88. assert.Assert(c, realData != nil, "stat result are empty: %s", out)
  89. }
  90. func (s *DockerCLIStatsSuite) TestStatsAllNoStream(c *testing.T) {
  91. // Windows does not support stats
  92. testRequires(c, DaemonIsLinux)
  93. id1 := cli.DockerCmd(c, "run", "-d", "busybox", "top").Stdout()
  94. id1 = strings.TrimSpace(id1)[:12]
  95. cli.WaitRun(c, id1)
  96. cli.DockerCmd(c, "stop", id1)
  97. id2 := cli.DockerCmd(c, "run", "-d", "busybox", "top").Stdout()
  98. id2 = strings.TrimSpace(id2)[:12]
  99. cli.WaitRun(c, id2)
  100. out := cli.DockerCmd(c, "stats", "--all", "--no-stream").Combined()
  101. if !strings.Contains(out, id1) || !strings.Contains(out, id2) {
  102. c.Fatalf("Expected stats output to contain both %s and %s, got %s", id1, id2, out)
  103. }
  104. // check output contains real data, but not all zeros
  105. reg, _ := regexp.Compile("[1-9]+")
  106. // split output with "\n", outLines[1] is id2's output
  107. outLines := strings.Split(out, "\n")
  108. // check stat result of id2 contains real data
  109. realData := reg.Find([]byte(outLines[1][12:]))
  110. assert.Assert(c, realData != nil, "stat result of %s is empty: %s", id2, out)
  111. // check stat result of id1 contains all zero
  112. realData = reg.Find([]byte(outLines[2][12:]))
  113. assert.Assert(c, realData == nil, "stat result of %s should be empty : %s", id1, out)
  114. }
  115. func (s *DockerCLIStatsSuite) TestStatsAllNewContainersAdded(c *testing.T) {
  116. // Windows does not support stats
  117. testRequires(c, DaemonIsLinux)
  118. id := make(chan string)
  119. addedChan := make(chan struct{})
  120. runSleepingContainer(c, "-d")
  121. statsCmd := exec.Command(dockerBinary, "stats")
  122. stdout, err := statsCmd.StdoutPipe()
  123. assert.NilError(c, err)
  124. assert.NilError(c, statsCmd.Start())
  125. go statsCmd.Wait()
  126. defer statsCmd.Process.Kill()
  127. go func() {
  128. containerID := <-id
  129. matchID := regexp.MustCompile(containerID)
  130. scanner := bufio.NewScanner(stdout)
  131. for scanner.Scan() {
  132. switch {
  133. case matchID.MatchString(scanner.Text()):
  134. close(addedChan)
  135. return
  136. }
  137. }
  138. }()
  139. out := runSleepingContainer(c, "-d")
  140. cli.WaitRun(c, out)
  141. id <- strings.TrimSpace(out)[:12]
  142. select {
  143. case <-time.After(30 * time.Second):
  144. c.Fatal("failed to observe new container created added to stats")
  145. case <-addedChan:
  146. // ignore, done
  147. }
  148. }
  149. func (s *DockerCLIStatsSuite) TestStatsFormatAll(c *testing.T) {
  150. // Windows does not support stats
  151. testRequires(c, DaemonIsLinux)
  152. cli.DockerCmd(c, "run", "-d", "--name=RunningOne", "busybox", "top")
  153. cli.WaitRun(c, "RunningOne")
  154. cli.DockerCmd(c, "run", "-d", "--name=ExitedOne", "busybox", "top")
  155. cli.DockerCmd(c, "stop", "ExitedOne")
  156. cli.WaitExited(c, "ExitedOne", 5*time.Second)
  157. out := cli.DockerCmd(c, "stats", "--no-stream", "--format", "{{.Name}}").Combined()
  158. assert.Assert(c, is.Contains(out, "RunningOne"))
  159. assert.Assert(c, !strings.Contains(out, "ExitedOne"))
  160. out = cli.DockerCmd(c, "stats", "--all", "--no-stream", "--format", "{{.Name}}").Combined()
  161. assert.Assert(c, is.Contains(out, "RunningOne"))
  162. assert.Assert(c, is.Contains(out, "ExitedOne"))
  163. }