From 3f971335463b99a7caedcf597ffc544845b37a21 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 3 Aug 2016 19:02:39 -0700 Subject: [PATCH] Sort output of `docker ps --filter` with order by creation time This fix tries to address the issue raised in 25374 where the output of `docker ps --filter` is in random order and not deterministic. This fix sorts the list of containers by creation time so that the output is deterministic. An integration test has been added. This fix fixes 25374. Signed-off-by: Yong Tang --- daemon/list.go | 15 ++++++++++++ integration-cli/docker_cli_ps_test.go | 34 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/daemon/list.go b/daemon/list.go index 128d9eda9c..6d86cd3b48 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -3,6 +3,7 @@ package daemon import ( "errors" "fmt" + "sort" "strconv" "strings" @@ -86,6 +87,15 @@ type listContext struct { *types.ContainerListOptions } +// byContainerCreated is a temporary type used to sort a list of containers by creation time. +type byContainerCreated []*container.Container + +func (r byContainerCreated) Len() int { return len(r) } +func (r byContainerCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] } +func (r byContainerCreated) Less(i, j int) bool { + return r[i].Created.UnixNano() < r[j].Created.UnixNano() +} + // Containers returns the list of containers to show given the user's filtering. func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) { return daemon.reduceContainers(config, daemon.transformContainer) @@ -149,6 +159,11 @@ func (daemon *Daemon) filterByNameIDMatches(ctx *listContext) []*container.Conta for id := range matches { cntrs = append(cntrs, daemon.containers.Get(id)) } + + // Restore sort-order after filtering + // Created gives us nanosec resolution for sorting + sort.Sort(sort.Reverse(byContainerCreated(cntrs))) + return cntrs } diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go index a4ee9a9f65..6e1756f06f 100644 --- a/integration-cli/docker_cli_ps_test.go +++ b/integration-cli/docker_cli_ps_test.go @@ -864,3 +864,37 @@ func (s *DockerSuite) TestPsListContainersFilterNetwork(c *check.C) { c.Assert(containerOut, checker.Contains, "onbridgenetwork") } + +func (s *DockerSuite) TestPsByOrder(c *check.C) { + name1 := "xyz-abc" + out, err := runSleepingContainer(c, "--name", name1) + c.Assert(err, checker.NotNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") + container1 := strings.TrimSpace(out) + + name2 := "xyz-123" + out, err = runSleepingContainer(c, "--name", name2) + c.Assert(err, checker.NotNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") + container2 := strings.TrimSpace(out) + + name3 := "789-abc" + out, err = runSleepingContainer(c, "--name", name3) + c.Assert(err, checker.NotNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") + + name4 := "789-123" + out, err = runSleepingContainer(c, "--name", name4) + c.Assert(err, checker.NotNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") + + // Run multiple time should have the same result + out, err = dockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz") + c.Assert(err, checker.NotNil) + c.Assert(strings.TrimSpace(out), checker.Equals, fmt.Sprintf("%s\n%s", container2, container1)) + + // Run multiple time should have the same result + out, err = dockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz") + c.Assert(err, checker.NotNil) + c.Assert(strings.TrimSpace(out), checker.Equals, fmt.Sprintf("%s\n%s", container2, container1)) +}