Browse code

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>

Yong Tang authored on 2016/08/04 11:02:39
Showing 2 changed files
... ...
@@ -3,6 +3,7 @@ package daemon
3 3
 import (
4 4
 	"errors"
5 5
 	"fmt"
6
+	"sort"
6 7
 	"strconv"
7 8
 	"strings"
8 9
 
... ...
@@ -86,6 +87,15 @@ type listContext struct {
86 86
 	*types.ContainerListOptions
87 87
 }
88 88
 
89
+// byContainerCreated is a temporary type used to sort a list of containers by creation time.
90
+type byContainerCreated []*container.Container
91
+
92
+func (r byContainerCreated) Len() int      { return len(r) }
93
+func (r byContainerCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
94
+func (r byContainerCreated) Less(i, j int) bool {
95
+	return r[i].Created.UnixNano() < r[j].Created.UnixNano()
96
+}
97
+
89 98
 // Containers returns the list of containers to show given the user's filtering.
90 99
 func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
91 100
 	return daemon.reduceContainers(config, daemon.transformContainer)
... ...
@@ -149,6 +159,11 @@ func (daemon *Daemon) filterByNameIDMatches(ctx *listContext) []*container.Conta
149 149
 	for id := range matches {
150 150
 		cntrs = append(cntrs, daemon.containers.Get(id))
151 151
 	}
152
+
153
+	// Restore sort-order after filtering
154
+	// Created gives us nanosec resolution for sorting
155
+	sort.Sort(sort.Reverse(byContainerCreated(cntrs)))
156
+
152 157
 	return cntrs
153 158
 }
154 159
 
... ...
@@ -864,3 +864,37 @@ func (s *DockerSuite) TestPsListContainersFilterNetwork(c *check.C) {
864 864
 
865 865
 	c.Assert(containerOut, checker.Contains, "onbridgenetwork")
866 866
 }
867
+
868
+func (s *DockerSuite) TestPsByOrder(c *check.C) {
869
+	name1 := "xyz-abc"
870
+	out, err := runSleepingContainer(c, "--name", name1)
871
+	c.Assert(err, checker.NotNil)
872
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
873
+	container1 := strings.TrimSpace(out)
874
+
875
+	name2 := "xyz-123"
876
+	out, err = runSleepingContainer(c, "--name", name2)
877
+	c.Assert(err, checker.NotNil)
878
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
879
+	container2 := strings.TrimSpace(out)
880
+
881
+	name3 := "789-abc"
882
+	out, err = runSleepingContainer(c, "--name", name3)
883
+	c.Assert(err, checker.NotNil)
884
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
885
+
886
+	name4 := "789-123"
887
+	out, err = runSleepingContainer(c, "--name", name4)
888
+	c.Assert(err, checker.NotNil)
889
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
890
+
891
+	// Run multiple time should have the same result
892
+	out, err = dockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz")
893
+	c.Assert(err, checker.NotNil)
894
+	c.Assert(strings.TrimSpace(out), checker.Equals, fmt.Sprintf("%s\n%s", container2, container1))
895
+
896
+	// Run multiple time should have the same result
897
+	out, err = dockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz")
898
+	c.Assert(err, checker.NotNil)
899
+	c.Assert(strings.TrimSpace(out), checker.Equals, fmt.Sprintf("%s\n%s", container2, container1))
900
+}