Browse Source

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 <yong.tang.github@outlook.com>
(cherry picked from commit 3f971335463b99a7caedcf597ffc544845b37a21)
Signed-off-by: Tibor Vass <tibor@docker.com>
Yong Tang 9 years ago
parent
commit
8b9d96d208
2 changed files with 49 additions and 0 deletions
  1. 15 0
      daemon/list.go
  2. 34 0
      integration-cli/docker_cli_ps_test.go

+ 15 - 0
daemon/list.go

@@ -3,6 +3,7 @@ package daemon
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"sort"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
@@ -86,6 +87,15 @@ type listContext struct {
 	*types.ContainerListOptions
 	*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.
 // Containers returns the list of containers to show given the user's filtering.
 func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
 func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
 	return daemon.reduceContainers(config, daemon.transformContainer)
 	return daemon.reduceContainers(config, daemon.transformContainer)
@@ -149,6 +159,11 @@ func (daemon *Daemon) filterByNameIDMatches(ctx *listContext) []*container.Conta
 	for id := range matches {
 	for id := range matches {
 		cntrs = append(cntrs, daemon.containers.Get(id))
 		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
 	return cntrs
 }
 }
 
 

+ 34 - 0
integration-cli/docker_cli_ps_test.go

@@ -864,3 +864,37 @@ func (s *DockerSuite) TestPsListContainersFilterNetwork(c *check.C) {
 
 
 	c.Assert(containerOut, checker.Contains, "onbridgenetwork")
 	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))
+}