docker_cli_ps_test.go 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "sort"
  6. "strings"
  7. "testing"
  8. "time"
  9. "github.com/docker/docker/api/types/versions"
  10. "github.com/docker/docker/integration-cli/cli"
  11. "github.com/docker/docker/integration-cli/cli/build"
  12. "github.com/docker/docker/pkg/stringid"
  13. "github.com/docker/go-units"
  14. "gotest.tools/v3/assert"
  15. is "gotest.tools/v3/assert/cmp"
  16. "gotest.tools/v3/icmd"
  17. "gotest.tools/v3/skip"
  18. )
  19. type DockerCLIPsSuite struct {
  20. ds *DockerSuite
  21. }
  22. func (s *DockerCLIPsSuite) TearDownTest(ctx context.Context, c *testing.T) {
  23. s.ds.TearDownTest(ctx, c)
  24. }
  25. func (s *DockerCLIPsSuite) OnTimeout(c *testing.T) {
  26. s.ds.OnTimeout(c)
  27. }
  28. func (s *DockerCLIPsSuite) TestPsListContainersBase(c *testing.T) {
  29. existingContainers := ExistingContainerIDs(c)
  30. out := runSleepingContainer(c, "-d")
  31. firstID := strings.TrimSpace(out)
  32. out = runSleepingContainer(c, "-d")
  33. secondID := strings.TrimSpace(out)
  34. // not long running
  35. out, _ = dockerCmd(c, "run", "-d", "busybox", "true")
  36. thirdID := strings.TrimSpace(out)
  37. out = runSleepingContainer(c, "-d")
  38. fourthID := strings.TrimSpace(out)
  39. // make sure the second is running
  40. assert.Assert(c, waitRun(secondID) == nil)
  41. // make sure third one is not running
  42. dockerCmd(c, "wait", thirdID)
  43. // make sure the forth is running
  44. assert.Assert(c, waitRun(fourthID) == nil)
  45. // all
  46. out, _ = dockerCmd(c, "ps", "-a")
  47. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), []string{fourthID, thirdID, secondID, firstID}), true, fmt.Sprintf("ALL: Container list is not in the correct order: \n%s", out))
  48. // running
  49. out, _ = dockerCmd(c, "ps")
  50. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), []string{fourthID, secondID, firstID}), true, fmt.Sprintf("RUNNING: Container list is not in the correct order: \n%s", out))
  51. // limit
  52. out, _ = dockerCmd(c, "ps", "-n=2", "-a")
  53. expected := []string{fourthID, thirdID}
  54. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("LIMIT & ALL: Container list is not in the correct order: \n%s", out))
  55. out, _ = dockerCmd(c, "ps", "-n=2")
  56. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("LIMIT: Container list is not in the correct order: \n%s", out))
  57. // filter since
  58. out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-a")
  59. expected = []string{fourthID, thirdID, secondID}
  60. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter & ALL: Container list is not in the correct order: \n%s", out))
  61. out, _ = dockerCmd(c, "ps", "-f", "since="+firstID)
  62. expected = []string{fourthID, secondID}
  63. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter: Container list is not in the correct order: \n%s", out))
  64. out, _ = dockerCmd(c, "ps", "-f", "since="+thirdID)
  65. expected = []string{fourthID}
  66. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter: Container list is not in the correct order: \n%s", out))
  67. // filter before
  68. out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-a")
  69. expected = []string{thirdID, secondID, firstID}
  70. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("BEFORE filter & ALL: Container list is not in the correct order: \n%s", out))
  71. out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID)
  72. expected = []string{secondID, firstID}
  73. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("BEFORE filter: Container list is not in the correct order: \n%s", out))
  74. out, _ = dockerCmd(c, "ps", "-f", "before="+thirdID)
  75. expected = []string{secondID, firstID}
  76. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter: Container list is not in the correct order: \n%s", out))
  77. // filter since & before
  78. out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-a")
  79. expected = []string{thirdID, secondID}
  80. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, BEFORE filter & ALL: Container list is not in the correct order: \n%s", out))
  81. out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID)
  82. expected = []string{secondID}
  83. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, BEFORE filter: Container list is not in the correct order: \n%s", out))
  84. // filter since & limit
  85. out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-n=2", "-a")
  86. expected = []string{fourthID, thirdID}
  87. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
  88. out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-n=2")
  89. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, LIMIT: Container list is not in the correct order: \n%s", out))
  90. // filter before & limit
  91. out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-n=1", "-a")
  92. expected = []string{thirdID}
  93. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("BEFORE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
  94. out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-n=1")
  95. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("BEFORE filter, LIMIT: Container list is not in the correct order: \n%s", out))
  96. // filter since & filter before & limit
  97. out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-n=1", "-a")
  98. expected = []string{thirdID}
  99. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, BEFORE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
  100. out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-n=1")
  101. assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, BEFORE filter, LIMIT: Container list is not in the correct order: \n%s", out))
  102. }
  103. func assertContainerList(out string, expected []string) bool {
  104. lines := strings.Split(strings.Trim(out, "\n "), "\n")
  105. if len(lines)-1 != len(expected) {
  106. return false
  107. }
  108. containerIDIndex := strings.Index(lines[0], "CONTAINER ID")
  109. for i := 0; i < len(expected); i++ {
  110. foundID := lines[i+1][containerIDIndex : containerIDIndex+12]
  111. if foundID != expected[i][:12] {
  112. return false
  113. }
  114. }
  115. return true
  116. }
  117. func (s *DockerCLIPsSuite) TestPsListContainersSize(c *testing.T) {
  118. // Problematic on Windows as it doesn't report the size correctly @swernli
  119. testRequires(c, DaemonIsLinux)
  120. dockerCmd(c, "run", "-d", "busybox")
  121. baseOut, _ := dockerCmd(c, "ps", "-s", "-n=1")
  122. baseLines := strings.Split(strings.Trim(baseOut, "\n "), "\n")
  123. baseSizeIndex := strings.Index(baseLines[0], "SIZE")
  124. baseFoundsize, _, _ := strings.Cut(baseLines[1][baseSizeIndex:], " ")
  125. baseBytes, err := units.FromHumanSize(baseFoundsize)
  126. assert.NilError(c, err)
  127. name := "test_size"
  128. dockerCmd(c, "run", "--name", name, "busybox", "sh", "-c", "echo 1 > test")
  129. id := getIDByName(c, name)
  130. var result *icmd.Result
  131. wait := make(chan struct{})
  132. go func() {
  133. result = icmd.RunCommand(dockerBinary, "ps", "-s", "-n=1")
  134. close(wait)
  135. }()
  136. select {
  137. case <-wait:
  138. case <-time.After(3 * time.Second):
  139. c.Fatalf(`Calling "docker ps -s" timed out!`)
  140. }
  141. result.Assert(c, icmd.Success)
  142. lines := strings.Split(strings.Trim(result.Combined(), "\n "), "\n")
  143. assert.Equal(c, len(lines), 2, "Expected 2 lines for 'ps -s -n=1' output, got %d", len(lines))
  144. sizeIndex := strings.Index(lines[0], "SIZE")
  145. idIndex := strings.Index(lines[0], "CONTAINER ID")
  146. foundID := lines[1][idIndex : idIndex+12]
  147. assert.Equal(c, foundID, id[:12], fmt.Sprintf("Expected id %s, got %s", id[:12], foundID))
  148. foundSize, _, _ := strings.Cut(strings.TrimSpace(lines[1][sizeIndex:]), " ")
  149. // With snapshotters the reported usage is the real space occupied on the
  150. // filesystem (also includes metadata), so this new file can actually
  151. // result in a bigger increase depending on the underlying filesystem (on
  152. // ext4 this would be 4096 which is a minimum allocation unit).
  153. if testEnv.UsingSnapshotter() {
  154. newBytes, err := units.FromHumanSize(foundSize)
  155. assert.NilError(c, err)
  156. // Check if size increased by at least 2 bytes.
  157. assert.Check(c, newBytes >= baseBytes+2)
  158. } else {
  159. expectedSize := units.HumanSize(float64(baseBytes + 2))
  160. assert.Assert(c, strings.Contains(foundSize, expectedSize), "Expected size %q, got %q", expectedSize, foundSize)
  161. }
  162. }
  163. func (s *DockerCLIPsSuite) TestPsListContainersFilterStatus(c *testing.T) {
  164. existingContainers := ExistingContainerIDs(c)
  165. // start exited container
  166. out := cli.DockerCmd(c, "run", "-d", "busybox").Combined()
  167. firstID := strings.TrimSpace(out)
  168. // make sure the exited container is not running
  169. cli.DockerCmd(c, "wait", firstID)
  170. // start running container
  171. out = cli.DockerCmd(c, "run", "-itd", "busybox").Combined()
  172. secondID := strings.TrimSpace(out)
  173. // filter containers by exited
  174. out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "--filter=status=exited").Combined()
  175. containerOut := strings.TrimSpace(out)
  176. assert.Equal(c, RemoveOutputForExistingElements(containerOut, existingContainers), firstID)
  177. out = cli.DockerCmd(c, "ps", "-a", "--no-trunc", "-q", "--filter=status=running").Combined()
  178. containerOut = strings.TrimSpace(out)
  179. assert.Equal(c, RemoveOutputForExistingElements(containerOut, existingContainers), secondID)
  180. result := cli.Docker(cli.Args("ps", "-a", "-q", "--filter=status=rubbish"), cli.WithTimeout(time.Second*60))
  181. err := "invalid filter 'status=rubbish'"
  182. if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
  183. err = "Unrecognised filter value for status: rubbish"
  184. }
  185. result.Assert(c, icmd.Expected{
  186. ExitCode: 1,
  187. Err: err,
  188. })
  189. // Windows doesn't support pausing of containers
  190. if testEnv.DaemonInfo.OSType != "windows" {
  191. // pause running container
  192. out = cli.DockerCmd(c, "run", "-itd", "busybox").Combined()
  193. pausedID := strings.TrimSpace(out)
  194. cli.DockerCmd(c, "pause", pausedID)
  195. // make sure the container is unpaused to let the daemon stop it properly
  196. defer func() { cli.DockerCmd(c, "unpause", pausedID) }()
  197. out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "--filter=status=paused").Combined()
  198. containerOut = strings.TrimSpace(out)
  199. assert.Equal(c, RemoveOutputForExistingElements(containerOut, existingContainers), pausedID)
  200. }
  201. }
  202. func (s *DockerCLIPsSuite) TestPsListContainersFilterHealth(c *testing.T) {
  203. skip.If(c, RuntimeIsWindowsContainerd(), "FIXME. Hang on Windows + containerd combination")
  204. existingContainers := ExistingContainerIDs(c)
  205. // Test legacy no health check
  206. out := runSleepingContainer(c, "--name=none_legacy")
  207. containerID := strings.TrimSpace(out)
  208. cli.WaitRun(c, containerID)
  209. out = cli.DockerCmd(c, "ps", "-q", "-l", "--no-trunc", "--filter=health=none").Combined()
  210. containerOut := strings.TrimSpace(out)
  211. assert.Equal(c, containerOut, containerID, fmt.Sprintf("Expected id %s, got %s for legacy none filter, output: %q", containerID, containerOut, out))
  212. // Test no health check specified explicitly
  213. out = runSleepingContainer(c, "--name=none", "--no-healthcheck")
  214. containerID = strings.TrimSpace(out)
  215. cli.WaitRun(c, containerID)
  216. out = cli.DockerCmd(c, "ps", "-q", "-l", "--no-trunc", "--filter=health=none").Combined()
  217. containerOut = strings.TrimSpace(out)
  218. assert.Equal(c, containerOut, containerID, fmt.Sprintf("Expected id %s, got %s for none filter, output: %q", containerID, containerOut, out))
  219. // Test failing health check
  220. out = runSleepingContainer(c, "--name=failing_container", "--health-cmd=exit 1", "--health-interval=1s")
  221. containerID = strings.TrimSpace(out)
  222. waitForHealthStatus(c, "failing_container", "starting", "unhealthy")
  223. out = cli.DockerCmd(c, "ps", "-q", "--no-trunc", "--filter=health=unhealthy").Combined()
  224. containerOut = strings.TrimSpace(out)
  225. assert.Equal(c, containerOut, containerID, fmt.Sprintf("Expected containerID %s, got %s for unhealthy filter, output: %q", containerID, containerOut, out))
  226. // Check passing healthcheck
  227. out = runSleepingContainer(c, "--name=passing_container", "--health-cmd=exit 0", "--health-interval=1s")
  228. containerID = strings.TrimSpace(out)
  229. waitForHealthStatus(c, "passing_container", "starting", "healthy")
  230. out = cli.DockerCmd(c, "ps", "-q", "--no-trunc", "--filter=health=healthy").Combined()
  231. containerOut = strings.TrimSpace(RemoveOutputForExistingElements(out, existingContainers))
  232. assert.Equal(c, containerOut, containerID, fmt.Sprintf("Expected containerID %s, got %s for healthy filter, output: %q", containerID, containerOut, out))
  233. }
  234. func (s *DockerCLIPsSuite) TestPsListContainersFilterID(c *testing.T) {
  235. // start container
  236. out, _ := dockerCmd(c, "run", "-d", "busybox")
  237. firstID := strings.TrimSpace(out)
  238. // start another container
  239. runSleepingContainer(c)
  240. // filter containers by id
  241. out, _ = dockerCmd(c, "ps", "-a", "-q", "--filter=id="+firstID)
  242. containerOut := strings.TrimSpace(out)
  243. assert.Equal(c, containerOut, firstID[:12], fmt.Sprintf("Expected id %s, got %s for exited filter, output: %q", firstID[:12], containerOut, out))
  244. }
  245. func (s *DockerCLIPsSuite) TestPsListContainersFilterName(c *testing.T) {
  246. // start container
  247. dockerCmd(c, "run", "--name=a_name_to_match", "busybox")
  248. id := getIDByName(c, "a_name_to_match")
  249. // start another container
  250. runSleepingContainer(c, "--name=b_name_to_match")
  251. // filter containers by name
  252. out, _ := dockerCmd(c, "ps", "-a", "-q", "--filter=name=a_name_to_match")
  253. containerOut := strings.TrimSpace(out)
  254. assert.Equal(c, containerOut, id[:12], fmt.Sprintf("Expected id %s, got %s for exited filter, output: %q", id[:12], containerOut, out))
  255. }
  256. // Test for the ancestor filter for ps.
  257. // There is also the same test but with image:tag@digest in docker_cli_by_digest_test.go
  258. //
  259. // What the test setups :
  260. // - Create 2 image based on busybox using the same repository but different tags
  261. // - Create an image based on the previous image (images_ps_filter_test2)
  262. // - Run containers for each of those image (busybox, images_ps_filter_test1, images_ps_filter_test2)
  263. // - Filter them out :P
  264. func (s *DockerCLIPsSuite) TestPsListContainersFilterAncestorImage(c *testing.T) {
  265. existingContainers := ExistingContainerIDs(c)
  266. // Build images
  267. imageName1 := "images_ps_filter_test1"
  268. buildImageSuccessfully(c, imageName1, build.WithDockerfile(`FROM busybox
  269. LABEL match me 1`))
  270. imageID1 := getIDByName(c, imageName1)
  271. imageName1Tagged := "images_ps_filter_test1:tag"
  272. buildImageSuccessfully(c, imageName1Tagged, build.WithDockerfile(`FROM busybox
  273. LABEL match me 1 tagged`))
  274. imageID1Tagged := getIDByName(c, imageName1Tagged)
  275. imageName2 := "images_ps_filter_test2"
  276. buildImageSuccessfully(c, imageName2, build.WithDockerfile(fmt.Sprintf(`FROM %s
  277. LABEL match me 2`, imageName1)))
  278. imageID2 := getIDByName(c, imageName2)
  279. // start containers
  280. dockerCmd(c, "run", "--name=first", "busybox", "echo", "hello")
  281. firstID := getIDByName(c, "first")
  282. // start another container
  283. dockerCmd(c, "run", "--name=second", "busybox", "echo", "hello")
  284. secondID := getIDByName(c, "second")
  285. // start third container
  286. dockerCmd(c, "run", "--name=third", imageName1, "echo", "hello")
  287. thirdID := getIDByName(c, "third")
  288. // start fourth container
  289. dockerCmd(c, "run", "--name=fourth", imageName1Tagged, "echo", "hello")
  290. fourthID := getIDByName(c, "fourth")
  291. // start fifth container
  292. dockerCmd(c, "run", "--name=fifth", imageName2, "echo", "hello")
  293. fifthID := getIDByName(c, "fifth")
  294. filterTestSuite := []struct {
  295. filterName string
  296. expectedIDs []string
  297. }{
  298. // non existent stuff
  299. {"nonexistent", []string{}},
  300. {"nonexistent:tag", []string{}},
  301. // image
  302. {"busybox", []string{firstID, secondID, thirdID, fourthID, fifthID}},
  303. {imageName1, []string{thirdID, fifthID}},
  304. {imageName2, []string{fifthID}},
  305. // image:tag
  306. {fmt.Sprintf("%s:latest", imageName1), []string{thirdID, fifthID}},
  307. {imageName1Tagged, []string{fourthID}},
  308. // short-id
  309. {stringid.TruncateID(imageID1), []string{thirdID, fifthID}},
  310. {stringid.TruncateID(imageID2), []string{fifthID}},
  311. // full-id
  312. {imageID1, []string{thirdID, fifthID}},
  313. {imageID1Tagged, []string{fourthID}},
  314. {imageID2, []string{fifthID}},
  315. }
  316. var out string
  317. for _, filter := range filterTestSuite {
  318. out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=ancestor="+filter.filterName)
  319. checkPsAncestorFilterOutput(c, RemoveOutputForExistingElements(out, existingContainers), filter.filterName, filter.expectedIDs)
  320. }
  321. // Multiple ancestor filter
  322. out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=ancestor="+imageName2, "--filter=ancestor="+imageName1Tagged)
  323. checkPsAncestorFilterOutput(c, RemoveOutputForExistingElements(out, existingContainers), imageName2+","+imageName1Tagged, []string{fourthID, fifthID})
  324. }
  325. func checkPsAncestorFilterOutput(c *testing.T, out string, filterName string, expectedIDs []string) {
  326. var actualIDs []string
  327. if out != "" {
  328. actualIDs = strings.Split(out[:len(out)-1], "\n")
  329. }
  330. sort.Strings(actualIDs)
  331. sort.Strings(expectedIDs)
  332. assert.Equal(c, len(actualIDs), len(expectedIDs), fmt.Sprintf("Expected filtered container(s) for %s ancestor filter to be %v:%v, got %v:%v", filterName, len(expectedIDs), expectedIDs, len(actualIDs), actualIDs))
  333. if len(expectedIDs) > 0 {
  334. same := true
  335. for i := range expectedIDs {
  336. if actualIDs[i] != expectedIDs[i] {
  337. c.Logf("%s, %s", actualIDs[i], expectedIDs[i])
  338. same = false
  339. break
  340. }
  341. }
  342. assert.Equal(c, same, true, fmt.Sprintf("Expected filtered container(s) for %s ancestor filter to be %v, got %v", filterName, expectedIDs, actualIDs))
  343. }
  344. }
  345. func (s *DockerCLIPsSuite) TestPsListContainersFilterLabel(c *testing.T) {
  346. // start container
  347. dockerCmd(c, "run", "--name=first", "-l", "match=me", "-l", "second=tag", "busybox")
  348. firstID := getIDByName(c, "first")
  349. // start another container
  350. dockerCmd(c, "run", "--name=second", "-l", "match=me too", "busybox")
  351. secondID := getIDByName(c, "second")
  352. // start third container
  353. dockerCmd(c, "run", "--name=third", "-l", "nomatch=me", "busybox")
  354. thirdID := getIDByName(c, "third")
  355. // filter containers by exact match
  356. out, _ := dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me")
  357. containerOut := strings.TrimSpace(out)
  358. assert.Equal(c, containerOut, firstID, fmt.Sprintf("Expected id %s, got %s for exited filter, output: %q", firstID, containerOut, out))
  359. // filter containers by two labels
  360. out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me", "--filter=label=second=tag")
  361. containerOut = strings.TrimSpace(out)
  362. assert.Equal(c, containerOut, firstID, fmt.Sprintf("Expected id %s, got %s for exited filter, output: %q", firstID, containerOut, out))
  363. // filter containers by two labels, but expect not found because of AND behavior
  364. out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me", "--filter=label=second=tag-no")
  365. containerOut = strings.TrimSpace(out)
  366. assert.Equal(c, containerOut, "", fmt.Sprintf("Expected nothing, got %s for exited filter, output: %q", containerOut, out))
  367. // filter containers by exact key
  368. out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=label=match")
  369. containerOut = strings.TrimSpace(out)
  370. assert.Assert(c, strings.Contains(containerOut, firstID))
  371. assert.Assert(c, strings.Contains(containerOut, secondID))
  372. assert.Assert(c, !strings.Contains(containerOut, thirdID))
  373. }
  374. func (s *DockerCLIPsSuite) TestPsListContainersFilterExited(c *testing.T) {
  375. // TODO Flaky on Windows CI [both RS1 and RS5]
  376. // On slower machines the container may not have exited
  377. // yet when we filter below by exit status/exit value.
  378. skip.If(c, DaemonIsWindows(), "FLAKY on Windows, see #20819")
  379. runSleepingContainer(c, "--name=sleep")
  380. firstZero, _ := dockerCmd(c, "run", "-d", "busybox", "true")
  381. secondZero, _ := dockerCmd(c, "run", "-d", "busybox", "true")
  382. out, _, err := dockerCmdWithError("run", "--name", "nonzero1", "busybox", "false")
  383. assert.Assert(c, err != nil, "Should fail. out: %s", out)
  384. firstNonZero := getIDByName(c, "nonzero1")
  385. out, _, err = dockerCmdWithError("run", "--name", "nonzero2", "busybox", "false")
  386. assert.Assert(c, err != nil, "Should fail. out: %s", out)
  387. secondNonZero := getIDByName(c, "nonzero2")
  388. // filter containers by exited=0
  389. out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=exited=0")
  390. assert.Assert(c, strings.Contains(out, strings.TrimSpace(firstZero)))
  391. assert.Assert(c, strings.Contains(out, strings.TrimSpace(secondZero)))
  392. assert.Assert(c, !strings.Contains(out, strings.TrimSpace(firstNonZero)))
  393. assert.Assert(c, !strings.Contains(out, strings.TrimSpace(secondNonZero)))
  394. out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=exited=1")
  395. assert.Assert(c, strings.Contains(out, strings.TrimSpace(firstNonZero)))
  396. assert.Assert(c, strings.Contains(out, strings.TrimSpace(secondNonZero)))
  397. assert.Assert(c, !strings.Contains(out, strings.TrimSpace(firstZero)))
  398. assert.Assert(c, !strings.Contains(out, strings.TrimSpace(secondZero)))
  399. }
  400. func (s *DockerCLIPsSuite) TestPsRightTagName(c *testing.T) {
  401. // TODO Investigate further why this fails on Windows to Windows CI
  402. testRequires(c, DaemonIsLinux)
  403. existingContainers := ExistingContainerNames(c)
  404. tag := "asybox:shmatest"
  405. dockerCmd(c, "tag", "busybox", tag)
  406. var id1 string
  407. out := runSleepingContainer(c)
  408. id1 = strings.TrimSpace(out)
  409. var id2 string
  410. out = runSleepingContainerInImage(c, tag)
  411. id2 = strings.TrimSpace(out)
  412. var imageID string
  413. out = inspectField(c, "busybox", "Id")
  414. imageID = strings.TrimSpace(out)
  415. var id3 string
  416. out = runSleepingContainerInImage(c, imageID)
  417. id3 = strings.TrimSpace(out)
  418. out, _ = dockerCmd(c, "ps", "--no-trunc")
  419. lines := strings.Split(strings.TrimSpace(out), "\n")
  420. lines = RemoveLinesForExistingElements(lines, existingContainers)
  421. // skip header
  422. lines = lines[1:]
  423. assert.Equal(c, len(lines), 3, "There should be 3 running container, got %d", len(lines))
  424. for _, line := range lines {
  425. f := strings.Fields(line)
  426. switch f[0] {
  427. case id1:
  428. assert.Equal(c, f[1], "busybox", fmt.Sprintf("Expected %s tag for id %s, got %s", "busybox", id1, f[1]))
  429. case id2:
  430. assert.Equal(c, f[1], tag, fmt.Sprintf("Expected %s tag for id %s, got %s", tag, id2, f[1]))
  431. case id3:
  432. assert.Equal(c, f[1], imageID, fmt.Sprintf("Expected %s imageID for id %s, got %s", tag, id3, f[1]))
  433. default:
  434. c.Fatalf("Unexpected id %s, expected %s and %s and %s", f[0], id1, id2, id3)
  435. }
  436. }
  437. }
  438. func (s *DockerCLIPsSuite) TestPsListContainersFilterCreated(c *testing.T) {
  439. // create a container
  440. out, _ := dockerCmd(c, "create", "busybox")
  441. cID := strings.TrimSpace(out)
  442. shortCID := cID[:12]
  443. // Make sure it DOESN'T show up w/o a '-a' for normal 'ps'
  444. out, _ = dockerCmd(c, "ps", "-q")
  445. assert.Assert(c, !strings.Contains(out, shortCID), "Should have not seen '%s' in ps output:\n%s", shortCID, out)
  446. // Make sure it DOES show up as 'Created' for 'ps -a'
  447. out, _ = dockerCmd(c, "ps", "-a")
  448. hits := 0
  449. for _, line := range strings.Split(out, "\n") {
  450. if !strings.Contains(line, shortCID) {
  451. continue
  452. }
  453. hits++
  454. assert.Assert(c, strings.Contains(line, "Created"), "Missing 'Created' on '%s'", line)
  455. }
  456. assert.Equal(c, hits, 1, fmt.Sprintf("Should have seen '%s' in ps -a output once:%d\n%s", shortCID, hits, out))
  457. // filter containers by 'create' - note, no -a needed
  458. out, _ = dockerCmd(c, "ps", "-q", "-f", "status=created")
  459. containerOut := strings.TrimSpace(out)
  460. assert.Assert(c, strings.Contains(containerOut, shortCID), "Should have seen '%s' in ps output:\n%s", shortCID, out)
  461. }
  462. // Test for GitHub issue #12595
  463. func (s *DockerCLIPsSuite) TestPsImageIDAfterUpdate(c *testing.T) {
  464. // TODO: Investigate why this fails on Windows to Windows CI further.
  465. testRequires(c, DaemonIsLinux)
  466. originalImageName := "busybox:TestPsImageIDAfterUpdate-original"
  467. updatedImageName := "busybox:TestPsImageIDAfterUpdate-updated"
  468. existingContainers := ExistingContainerIDs(c)
  469. icmd.RunCommand(dockerBinary, "tag", "busybox:latest", originalImageName).Assert(c, icmd.Success)
  470. originalImageID := getIDByName(c, originalImageName)
  471. result := icmd.RunCommand(dockerBinary, append([]string{"run", "-d", originalImageName}, sleepCommandForDaemonPlatform()...)...)
  472. result.Assert(c, icmd.Success)
  473. containerID := strings.TrimSpace(result.Combined())
  474. result = icmd.RunCommand(dockerBinary, "ps", "--no-trunc")
  475. result.Assert(c, icmd.Success)
  476. lines := strings.Split(strings.TrimSpace(result.Combined()), "\n")
  477. lines = RemoveLinesForExistingElements(lines, existingContainers)
  478. // skip header
  479. lines = lines[1:]
  480. assert.Equal(c, len(lines), 1)
  481. for _, line := range lines {
  482. f := strings.Fields(line)
  483. assert.Equal(c, f[1], originalImageName)
  484. }
  485. icmd.RunCommand(dockerBinary, "commit", containerID, updatedImageName).Assert(c, icmd.Success)
  486. icmd.RunCommand(dockerBinary, "tag", updatedImageName, originalImageName).Assert(c, icmd.Success)
  487. result = icmd.RunCommand(dockerBinary, "ps", "--no-trunc")
  488. result.Assert(c, icmd.Success)
  489. lines = strings.Split(strings.TrimSpace(result.Combined()), "\n")
  490. lines = RemoveLinesForExistingElements(lines, existingContainers)
  491. // skip header
  492. lines = lines[1:]
  493. assert.Equal(c, len(lines), 1)
  494. for _, line := range lines {
  495. f := strings.Fields(line)
  496. assert.Equal(c, f[1], originalImageID)
  497. }
  498. }
  499. func (s *DockerCLIPsSuite) TestPsNotShowPortsOfStoppedContainer(c *testing.T) {
  500. testRequires(c, DaemonIsLinux)
  501. dockerCmd(c, "run", "--name=foo", "-d", "-p", "6000:5000", "busybox", "top")
  502. assert.Assert(c, waitRun("foo") == nil)
  503. ports, _ := dockerCmd(c, "ps", "--format", "{{ .Ports }}", "--filter", "name=foo")
  504. expected := ":6000->5000/tcp"
  505. assert.Assert(c, is.Contains(ports, expected), "Expected: %v, got: %v", expected, ports)
  506. dockerCmd(c, "kill", "foo")
  507. dockerCmd(c, "wait", "foo")
  508. ports, _ = dockerCmd(c, "ps", "--format", "{{ .Ports }}", "--filter", "name=foo")
  509. assert.Equal(c, ports, "", "Should not got %v", expected)
  510. }
  511. func (s *DockerCLIPsSuite) TestPsShowMounts(c *testing.T) {
  512. existingContainers := ExistingContainerNames(c)
  513. prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  514. mp := prefix + slash + "test"
  515. dockerCmd(c, "volume", "create", "ps-volume-test")
  516. // volume mount containers
  517. runSleepingContainer(c, "--name=volume-test-1", "--volume", "ps-volume-test:"+mp)
  518. assert.Assert(c, waitRun("volume-test-1") == nil)
  519. runSleepingContainer(c, "--name=volume-test-2", "--volume", mp)
  520. assert.Assert(c, waitRun("volume-test-2") == nil)
  521. // bind mount container
  522. var bindMountSource string
  523. var bindMountDestination string
  524. if DaemonIsWindows() {
  525. bindMountSource = `c:\`
  526. bindMountDestination = `c:\t`
  527. } else {
  528. bindMountSource = "/tmp"
  529. bindMountDestination = "/t"
  530. }
  531. runSleepingContainer(c, "--name=bind-mount-test", "-v", bindMountSource+":"+bindMountDestination)
  532. assert.Assert(c, waitRun("bind-mount-test") == nil)
  533. out, _ := dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}")
  534. lines := strings.Split(strings.TrimSpace(out), "\n")
  535. lines = RemoveLinesForExistingElements(lines, existingContainers)
  536. assert.Equal(c, len(lines), 3)
  537. fields := strings.Fields(lines[0])
  538. assert.Equal(c, len(fields), 2)
  539. assert.Equal(c, fields[0], "bind-mount-test")
  540. assert.Equal(c, fields[1], bindMountSource)
  541. fields = strings.Fields(lines[1])
  542. assert.Equal(c, len(fields), 2)
  543. anonymousVolumeID := fields[1]
  544. fields = strings.Fields(lines[2])
  545. assert.Equal(c, fields[1], "ps-volume-test")
  546. // filter by volume name
  547. out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume=ps-volume-test")
  548. lines = strings.Split(strings.TrimSpace(out), "\n")
  549. lines = RemoveLinesForExistingElements(lines, existingContainers)
  550. assert.Equal(c, len(lines), 1)
  551. fields = strings.Fields(lines[0])
  552. assert.Equal(c, fields[1], "ps-volume-test")
  553. // empty results filtering by unknown volume
  554. out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume=this-volume-should-not-exist")
  555. assert.Equal(c, len(strings.TrimSpace(out)), 0)
  556. // filter by mount destination
  557. out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+mp)
  558. lines = strings.Split(strings.TrimSpace(out), "\n")
  559. lines = RemoveLinesForExistingElements(lines, existingContainers)
  560. assert.Equal(c, len(lines), 2)
  561. fields = strings.Fields(lines[0])
  562. assert.Equal(c, fields[1], anonymousVolumeID)
  563. fields = strings.Fields(lines[1])
  564. assert.Equal(c, fields[1], "ps-volume-test")
  565. // filter by bind mount source
  566. out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+bindMountSource)
  567. lines = strings.Split(strings.TrimSpace(out), "\n")
  568. lines = RemoveLinesForExistingElements(lines, existingContainers)
  569. assert.Equal(c, len(lines), 1)
  570. fields = strings.Fields(lines[0])
  571. assert.Equal(c, len(fields), 2)
  572. assert.Equal(c, fields[0], "bind-mount-test")
  573. assert.Equal(c, fields[1], bindMountSource)
  574. // filter by bind mount destination
  575. out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+bindMountDestination)
  576. lines = strings.Split(strings.TrimSpace(out), "\n")
  577. lines = RemoveLinesForExistingElements(lines, existingContainers)
  578. assert.Equal(c, len(lines), 1)
  579. fields = strings.Fields(lines[0])
  580. assert.Equal(c, len(fields), 2)
  581. assert.Equal(c, fields[0], "bind-mount-test")
  582. assert.Equal(c, fields[1], bindMountSource)
  583. // empty results filtering by unknown mount point
  584. out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+prefix+slash+"this-path-was-never-mounted")
  585. assert.Equal(c, len(strings.TrimSpace(out)), 0)
  586. }
  587. func (s *DockerCLIPsSuite) TestPsListContainersFilterNetwork(c *testing.T) {
  588. existing := ExistingContainerIDs(c)
  589. // TODO default network on Windows is not called "bridge", and creating a
  590. // custom network fails on Windows fails with "Error response from daemon: plugin not found")
  591. testRequires(c, DaemonIsLinux)
  592. // create some containers
  593. runSleepingContainer(c, "--net=bridge", "--name=onbridgenetwork")
  594. runSleepingContainer(c, "--net=none", "--name=onnonenetwork")
  595. // Filter docker ps on non existing network
  596. out, _ := dockerCmd(c, "ps", "--filter", "network=doesnotexist")
  597. containerOut := strings.TrimSpace(out)
  598. lines := strings.Split(containerOut, "\n")
  599. // skip header
  600. lines = lines[1:]
  601. // ps output should have no containers
  602. assert.Equal(c, len(RemoveLinesForExistingElements(lines, existing)), 0)
  603. // Filter docker ps on network bridge
  604. out, _ = dockerCmd(c, "ps", "--filter", "network=bridge")
  605. containerOut = strings.TrimSpace(out)
  606. lines = strings.Split(containerOut, "\n")
  607. // skip header
  608. lines = lines[1:]
  609. // ps output should have only one container
  610. assert.Equal(c, len(RemoveLinesForExistingElements(lines, existing)), 1)
  611. // Making sure onbridgenetwork is on the output
  612. assert.Assert(c, strings.Contains(containerOut, "onbridgenetwork"), "Missing the container on network\n")
  613. // Filter docker ps on networks bridge and none
  614. out, _ = dockerCmd(c, "ps", "--filter", "network=bridge", "--filter", "network=none")
  615. containerOut = strings.TrimSpace(out)
  616. lines = strings.Split(containerOut, "\n")
  617. // skip header
  618. lines = lines[1:]
  619. // ps output should have both the containers
  620. assert.Equal(c, len(RemoveLinesForExistingElements(lines, existing)), 2)
  621. // Making sure onbridgenetwork and onnonenetwork is on the output
  622. assert.Assert(c, strings.Contains(containerOut, "onnonenetwork"), "Missing the container on none network\n")
  623. assert.Assert(c, strings.Contains(containerOut, "onbridgenetwork"), "Missing the container on bridge network\n")
  624. nwID, _ := dockerCmd(c, "network", "inspect", "--format", "{{.ID}}", "bridge")
  625. // Filter by network ID
  626. out, _ = dockerCmd(c, "ps", "--filter", "network="+nwID)
  627. containerOut = strings.TrimSpace(out)
  628. assert.Assert(c, is.Contains(containerOut, "onbridgenetwork"))
  629. // Filter by partial network ID
  630. partialnwID := nwID[0:4]
  631. out, _ = dockerCmd(c, "ps", "--filter", "network="+partialnwID)
  632. containerOut = strings.TrimSpace(out)
  633. lines = strings.Split(containerOut, "\n")
  634. // skip header
  635. lines = lines[1:]
  636. // ps output should have only one container
  637. assert.Equal(c, len(RemoveLinesForExistingElements(lines, existing)), 1)
  638. // Making sure onbridgenetwork is on the output
  639. assert.Assert(c, strings.Contains(containerOut, "onbridgenetwork"), "Missing the container on network\n")
  640. }
  641. func (s *DockerCLIPsSuite) TestPsByOrder(c *testing.T) {
  642. out := runSleepingContainer(c, "--name", "xyz-abc")
  643. container1 := strings.TrimSpace(out)
  644. out = runSleepingContainer(c, "--name", "xyz-123")
  645. container2 := strings.TrimSpace(out)
  646. runSleepingContainer(c, "--name", "789-abc")
  647. runSleepingContainer(c, "--name", "789-123")
  648. // Run multiple time should have the same result
  649. out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz").Combined()
  650. assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%s\n%s", container2, container1))
  651. // Run multiple time should have the same result
  652. out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz").Combined()
  653. assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%s\n%s", container2, container1))
  654. }
  655. func (s *DockerCLIPsSuite) TestPsListContainersFilterPorts(c *testing.T) {
  656. testRequires(c, DaemonIsLinux)
  657. existingContainers := ExistingContainerIDs(c)
  658. out, _ := dockerCmd(c, "run", "-d", "--publish=80", "busybox", "top")
  659. id1 := strings.TrimSpace(out)
  660. out, _ = dockerCmd(c, "run", "-d", "--expose=8080", "busybox", "top")
  661. id2 := strings.TrimSpace(out)
  662. out, _ = dockerCmd(c, "run", "-d", "-p", "1090:90", "busybox", "top")
  663. id3 := strings.TrimSpace(out)
  664. out, _ = dockerCmd(c, "ps", "--no-trunc", "-q")
  665. assert.Assert(c, strings.Contains(strings.TrimSpace(out), id1))
  666. assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
  667. assert.Assert(c, strings.Contains(strings.TrimSpace(out), id3))
  668. out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "publish=80-8080/udp")
  669. assert.Assert(c, strings.TrimSpace(out) != id1)
  670. assert.Assert(c, strings.TrimSpace(out) != id2)
  671. assert.Assert(c, strings.TrimSpace(out) != id3)
  672. out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=8081")
  673. assert.Assert(c, strings.TrimSpace(out) != id1)
  674. assert.Assert(c, strings.TrimSpace(out) != id2)
  675. assert.Assert(c, strings.TrimSpace(out) != id3)
  676. out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "publish=80-81")
  677. assert.Assert(c, strings.TrimSpace(out) != id1)
  678. assert.Assert(c, strings.TrimSpace(out) != id2)
  679. assert.Assert(c, strings.TrimSpace(out) != id3)
  680. out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=80/tcp")
  681. assert.Equal(c, strings.TrimSpace(out), id1)
  682. assert.Assert(c, strings.TrimSpace(out) != id2)
  683. assert.Assert(c, strings.TrimSpace(out) != id3)
  684. out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "publish=1090")
  685. assert.Assert(c, strings.TrimSpace(out) != id1)
  686. assert.Assert(c, strings.TrimSpace(out) != id2)
  687. assert.Equal(c, strings.TrimSpace(out), id3)
  688. out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=8080/tcp")
  689. out = RemoveOutputForExistingElements(out, existingContainers)
  690. assert.Assert(c, strings.TrimSpace(out) != id1)
  691. assert.Equal(c, strings.TrimSpace(out), id2)
  692. assert.Assert(c, strings.TrimSpace(out) != id3)
  693. }
  694. func (s *DockerCLIPsSuite) TestPsNotShowLinknamesOfDeletedContainer(c *testing.T) {
  695. testRequires(c, DaemonIsLinux, MinimumAPIVersion("1.31"))
  696. existingContainers := ExistingContainerNames(c)
  697. dockerCmd(c, "create", "--name=aaa", "busybox", "top")
  698. dockerCmd(c, "create", "--name=bbb", "--link=aaa", "busybox", "top")
  699. out, _ := dockerCmd(c, "ps", "--no-trunc", "-a", "--format", "{{.Names}}")
  700. lines := strings.Split(strings.TrimSpace(out), "\n")
  701. lines = RemoveLinesForExistingElements(lines, existingContainers)
  702. expected := []string{"bbb", "aaa,bbb/aaa"}
  703. var names []string
  704. names = append(names, lines...)
  705. assert.Assert(c, is.DeepEqual(names, expected), "Expected array with non-truncated names: %v, got: %v", expected, names)
  706. dockerCmd(c, "rm", "bbb")
  707. out, _ = dockerCmd(c, "ps", "--no-trunc", "-a", "--format", "{{.Names}}")
  708. out = RemoveOutputForExistingElements(out, existingContainers)
  709. assert.Equal(c, strings.TrimSpace(out), "aaa")
  710. }