diff --git a/api/client/commands.go b/api/client/commands.go index a6e7defc79..ba2e416d70 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -1485,7 +1485,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { last := cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.") flFilter := opts.NewListOpts(nil) - cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited= - containers with exit code of ") + cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited= - containers with exit code of \nstatus=(restarting|running|paused|exited)") if err := cmd.Parse(args); err != nil { return nil diff --git a/daemon/list.go b/daemon/list.go index 6501626b97..35effa4344 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -28,6 +28,7 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status { size = job.GetenvBool("size") psFilters filters.Args filt_exited []int + filt_status []string ) outs := engine.NewTable("Created", 0) @@ -45,6 +46,8 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status { } } + filt_status, _ = psFilters["status"] + names := map[string][]string{} daemon.ContainerGraph().Walk("/", func(p string, e *graphdb.Entity) error { names[e.ID()] = append(names[e.ID()], p) @@ -99,6 +102,11 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status { return nil } } + for _, status := range filt_status { + if container.State.StateString() != strings.ToLower(status) { + return nil + } + } displayed++ out := &engine.Env{} out.Set("Id", container.ID) diff --git a/daemon/state.go b/daemon/state.go index 05e0af2b94..b7dc149959 100644 --- a/daemon/state.go +++ b/daemon/state.go @@ -46,6 +46,20 @@ func (s *State) String() string { return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) } +// StateString returns a single string to describe state +func (s *State) StateString() string { + if s.Running { + if s.Paused { + return "paused" + } + if s.Restarting { + return "restarting" + } + return "running" + } + return "exited" +} + func wait(waitChan <-chan struct{}, timeout time.Duration) error { if timeout < 0 { <-waitChan diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index d02c1a860d..9859b9b31f 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -241,14 +241,15 @@ as the root. Wildcards are allowed but the search is not recursive. temp? The first line above `*/temp*`, would ignore all files with names starting with -`temp` from any subdirectory below the root directory, for example file named -`/somedir/temporary.txt` will be ignored. The second line `*/*/temp*`, will +`temp` from any subdirectory below the root directory. For example, a file named +`/somedir/temporary.txt` would be ignored. The second line `*/*/temp*`, will ignore files starting with name `temp` from any subdirectory that is two levels -below the root directory, for example a file `/somedir/subdir/temporary.txt` is -ignored in this case. The last line in the above example `temp?`, will ignore -the files that match the pattern from the root directory, for example files -`tempa`, `tempb` are ignored from the root directory. Currently there is no -support for regular expressions, formats like `[^temp*]` are ignored. +below the root directory. For example, the file `/somedir/subdir/temporary.txt` +would get ignored in this case. The last line in the above example `temp?` +will ignore the files that match the pattern from the root directory. +For example, the files `tempa`, `tempb` are ignored from the root directory. +Currently there is no support for regular expressions. Formats +like `[^temp*]` are ignored. See also: @@ -943,6 +944,7 @@ further details. --before="" Show only container created before Id or Name, include non-running ones. -f, --filter=[] Provide filter values. Valid filters: exited= - containers with exit code of + status=(restarting|running|paused|exited) -l, --latest=false Show only the latest created container, include non-running ones. -n=-1 Show n last created containers, include non-running ones. --no-trunc=false Don't truncate output diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go index 93075140fa..82a88c2d17 100644 --- a/integration-cli/docker_cli_ps_test.go +++ b/integration-cli/docker_cli_ps_test.go @@ -235,4 +235,50 @@ func TestPsListContainersSize(t *testing.T) { if foundSize != expectedSize { t.Fatalf("Expected size %q, got %q", expectedSize, foundSize) } + + deleteAllContainers() + logDone("ps - test ps size") +} + +func TestPsListContainersFilterStatus(t *testing.T) { + // FIXME: this should test paused, but it makes things hang and its wonky + // this is because paused containers can't be controlled by signals + + // start exited container + runCmd := exec.Command(dockerBinary, "run", "-d", "busybox") + out, _, err := runCommandWithOutput(runCmd) + errorOut(err, t, out) + firstID := stripTrailingCharacters(out) + + // make sure the exited cintainer is not running + runCmd = exec.Command(dockerBinary, "wait", firstID) + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + // start running container + runCmd = exec.Command(dockerBinary, "run", "-d", "busybox", "sh", "-c", "sleep 360") + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + secondID := stripTrailingCharacters(out) + + // filter containers by exited + runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--filter=status=exited") + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + containerOut := strings.TrimSpace(out) + if containerOut != firstID[:12] { + t.Fatalf("Expected id %s, got %s for exited filter, output: %q", firstID[:12], containerOut, out) + } + + runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--filter=status=running") + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + containerOut = strings.TrimSpace(out) + if containerOut != secondID[:12] { + t.Fatalf("Expected id %s, got %s for running filter, output: %q", secondID[:12], containerOut, out) + } + + deleteAllContainers() + + logDone("ps - test ps filter status") }