Browse code

Filter containers by status.

A continuation of #7616.
Adds `docker ps --filter=status=(restarting|running|paused|stopped)` option.

Docker-DCO-1.1-Signed-off-by: Jessica Frazelle <jess@docker.com> (github: jfrazelle)

Jessica Frazelle authored on 2014/09/27 08:25:50
Showing 5 changed files
... ...
@@ -1485,7 +1485,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1485 1485
 	last := cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.")
1486 1486
 
1487 1487
 	flFilter := opts.NewListOpts(nil)
1488
-	cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited=<int> - containers with exit code of <int>")
1488
+	cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited=<int> - containers with exit code of <int>\nstatus=(restarting|running|paused|exited)")
1489 1489
 
1490 1490
 	if err := cmd.Parse(args); err != nil {
1491 1491
 		return nil
... ...
@@ -28,6 +28,7 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
28 28
 		size        = job.GetenvBool("size")
29 29
 		psFilters   filters.Args
30 30
 		filt_exited []int
31
+		filt_status []string
31 32
 	)
32 33
 	outs := engine.NewTable("Created", 0)
33 34
 
... ...
@@ -45,6 +46,8 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
45 45
 		}
46 46
 	}
47 47
 
48
+	filt_status, _ = psFilters["status"]
49
+
48 50
 	names := map[string][]string{}
49 51
 	daemon.ContainerGraph().Walk("/", func(p string, e *graphdb.Entity) error {
50 52
 		names[e.ID()] = append(names[e.ID()], p)
... ...
@@ -99,6 +102,11 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
99 99
 				return nil
100 100
 			}
101 101
 		}
102
+		for _, status := range filt_status {
103
+			if container.State.StateString() != strings.ToLower(status) {
104
+				return nil
105
+			}
106
+		}
102 107
 		displayed++
103 108
 		out := &engine.Env{}
104 109
 		out.Set("Id", container.ID)
... ...
@@ -46,6 +46,20 @@ func (s *State) String() string {
46 46
 	return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
47 47
 }
48 48
 
49
+// StateString returns a single string to describe state
50
+func (s *State) StateString() string {
51
+	if s.Running {
52
+		if s.Paused {
53
+			return "paused"
54
+		}
55
+		if s.Restarting {
56
+			return "restarting"
57
+		}
58
+		return "running"
59
+	}
60
+	return "exited"
61
+}
62
+
49 63
 func wait(waitChan <-chan struct{}, timeout time.Duration) error {
50 64
 	if timeout < 0 {
51 65
 		<-waitChan
... ...
@@ -241,14 +241,15 @@ as the root. Wildcards are allowed but the search is not recursive.
241 241
     temp?
242 242
 
243 243
 The first line above `*/temp*`, would ignore all files with names starting with
244
-`temp` from any subdirectory below the root directory, for example file named 
245
-`/somedir/temporary.txt` will be ignored. The second line `*/*/temp*`, will  
244
+`temp` from any subdirectory below the root directory. For example, a file named
245
+`/somedir/temporary.txt` would be ignored. The second line `*/*/temp*`, will
246 246
 ignore files starting with name `temp` from any subdirectory that is two levels
247
-below the root directory, for example a file `/somedir/subdir/temporary.txt` is 
248
-ignored in this case. The last line in the above example `temp?`, will ignore 
249
-the files that match the pattern from the root directory, for example files 
250
-`tempa`, `tempb` are ignored from the root directory. Currently  there is no
251
-support for regular expressions, formats like `[^temp*]` are ignored.
247
+below the root directory. For example, the file `/somedir/subdir/temporary.txt`
248
+would get ignored in this case. The last line in the above example `temp?`
249
+will ignore the files that match the pattern from the root directory.
250
+For example, the files `tempa`, `tempb` are ignored from the root directory.
251
+Currently there is no support for regular expressions. Formats
252
+like `[^temp*]` are ignored.
252 253
 
253 254
 
254 255
 See also:
... ...
@@ -943,6 +944,7 @@ further details.
943 943
       --before=""           Show only container created before Id or Name, include non-running ones.
944 944
       -f, --filter=[]       Provide filter values. Valid filters:
945 945
                               exited=<int> - containers with exit code of <int>
946
+                              status=(restarting|running|paused|exited)
946 947
       -l, --latest=false    Show only the latest created container, include non-running ones.
947 948
       -n=-1                 Show n last created containers, include non-running ones.
948 949
       --no-trunc=false      Don't truncate output
... ...
@@ -235,4 +235,50 @@ func TestPsListContainersSize(t *testing.T) {
235 235
 	if foundSize != expectedSize {
236 236
 		t.Fatalf("Expected size %q, got %q", expectedSize, foundSize)
237 237
 	}
238
+
239
+	deleteAllContainers()
240
+	logDone("ps - test ps size")
241
+}
242
+
243
+func TestPsListContainersFilterStatus(t *testing.T) {
244
+	// FIXME: this should test paused, but it makes things hang and its wonky
245
+	// this is because paused containers can't be controlled by signals
246
+
247
+	// start exited container
248
+	runCmd := exec.Command(dockerBinary, "run", "-d", "busybox")
249
+	out, _, err := runCommandWithOutput(runCmd)
250
+	errorOut(err, t, out)
251
+	firstID := stripTrailingCharacters(out)
252
+
253
+	// make sure the exited cintainer is not running
254
+	runCmd = exec.Command(dockerBinary, "wait", firstID)
255
+	out, _, err = runCommandWithOutput(runCmd)
256
+	errorOut(err, t, out)
257
+
258
+	// start running container
259
+	runCmd = exec.Command(dockerBinary, "run", "-d", "busybox", "sh", "-c", "sleep 360")
260
+	out, _, err = runCommandWithOutput(runCmd)
261
+	errorOut(err, t, out)
262
+	secondID := stripTrailingCharacters(out)
263
+
264
+	// filter containers by exited
265
+	runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--filter=status=exited")
266
+	out, _, err = runCommandWithOutput(runCmd)
267
+	errorOut(err, t, out)
268
+	containerOut := strings.TrimSpace(out)
269
+	if containerOut != firstID[:12] {
270
+		t.Fatalf("Expected id %s, got %s for exited filter, output: %q", firstID[:12], containerOut, out)
271
+	}
272
+
273
+	runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--filter=status=running")
274
+	out, _, err = runCommandWithOutput(runCmd)
275
+	errorOut(err, t, out)
276
+	containerOut = strings.TrimSpace(out)
277
+	if containerOut != secondID[:12] {
278
+		t.Fatalf("Expected id %s, got %s for running filter, output: %q", secondID[:12], containerOut, out)
279
+	}
280
+
281
+	deleteAllContainers()
282
+
283
+	logDone("ps - test ps filter status")
238 284
 }