diff --git a/api/handlers.go b/api/handlers.go index 0385cdc596..45682a61d1 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -106,28 +106,104 @@ func getContainersJSON(c *context, w http.ResponseWriter, r *http.Request) { return } + // Parse flags. all := r.Form.Get("all") == "1" limit, _ := strconv.Atoi(r.Form.Get("limit")) - out := []*dockerclient.Container{} + // Parse filters. + filters, err := dockerfilters.FromParam(r.Form.Get("filters")) + if err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + filtExited := []int{} + if i, ok := filters["exited"]; ok { + for _, value := range i { + code, err := strconv.Atoi(value) + if err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + filtExited = append(filtExited, code) + } + } + if i, ok := filters["status"]; ok { + for _, value := range i { + if value == "exited" { + all = true + } + } + } + + // Filtering: select the containers we want to return. + candidates := []*cluster.Container{} for _, container := range c.cluster.Containers() { - tmp := (*container).Container // Skip stopped containers unless -a was specified. - if !strings.Contains(tmp.Status, "Up") && !all && limit <= 0 { + if !container.Info.State.Running && !all && limit <= 0 { continue } + // Skip swarm containers unless -a was specified. - if strings.Split(tmp.Image, ":")[0] == "swarm" && !all { + if strings.Split(container.Image, ":")[0] == "swarm" && !all { continue } + + // Apply filters. + if !filters.Match("name", strings.TrimPrefix(container.Names[0], "/")) { + continue + } + if !filters.Match("id", container.Id) { + continue + } + if !filters.MatchKVList("label", container.Config.Labels) { + continue + } + if !filters.Match("status", container.StateString()) { + continue + } + + if len(filtExited) > 0 { + shouldSkip := true + for _, code := range filtExited { + if code == container.Info.State.ExitCode && !container.Info.State.Running { + shouldSkip = false + break + } + } + if shouldSkip { + continue + } + } + + candidates = append(candidates, container) + } + + // Sort the candidates and apply limits. + sort.Sort(sort.Reverse(ContainerSorter(candidates))) + if limit > 0 && limit < len(candidates) { + candidates = candidates[:limit] + } + + // Convert cluster.Container back into dockerclient.Container. + out := []*dockerclient.Container{} + for _, container := range candidates { + // Create a copy of the underlying dockerclient.Container so we can + // make changes without messing with cluster.Container. + tmp := (*container).Container + + // Update the Status. The one we have is stale from the last `docker ps` the engine sent. + // `Status()` will generate a new one + tmp.Status = container.Status() if !container.Engine.IsHealthy() { tmp.Status = "Pending" } + // TODO remove the Node Name in the name when we have a good solution tmp.Names = make([]string, len(container.Names)) for i, name := range container.Names { tmp.Names[i] = "/" + container.Engine.Name + name } + // insert node IP tmp.Ports = make([]dockerclient.Port, len(container.Ports)) for i, port := range container.Ports { @@ -139,14 +215,9 @@ func getContainersJSON(c *context, w http.ResponseWriter, r *http.Request) { out = append(out, &tmp) } - sort.Sort(sort.Reverse(ContainerSorter(out))) - + // Finally, send them back to the CLI. w.Header().Set("Content-Type", "application/json") - if limit > 0 && limit < len(out) { - json.NewEncoder(w).Encode(out[:limit]) - } else { - json.NewEncoder(w).Encode(out) - } + json.NewEncoder(w).Encode(out) } // GET /containers/{name:.*}/json diff --git a/api/sorter.go b/api/sorter.go index 9c2b066346..b16350eeab 100644 --- a/api/sorter.go +++ b/api/sorter.go @@ -1,12 +1,10 @@ package api -import ( - "github.com/samalba/dockerclient" -) +import "github.com/docker/swarm/cluster" // ContainerSorter implements the Sort interface to sort Docker containers. // It is not guaranteed to be a stable sort. -type ContainerSorter []*dockerclient.Container +type ContainerSorter []*cluster.Container // Len returns the number of containers to be sorted. func (s ContainerSorter) Len() int { @@ -21,5 +19,5 @@ func (s ContainerSorter) Swap(i, j int) { // Less reports whether the container with index i should sort before the container with index j. // Containers are sorted chronologically by when they were created. func (s ContainerSorter) Less(i, j int) bool { - return s[i].Created < s[j].Created + return s[i].Info.Created < s[j].Info.Created } diff --git a/test/integration/api/ps.bats b/test/integration/api/ps.bats index 42d72af72b..31eeb7ea9c 100644 --- a/test/integration/api/ps.bats +++ b/test/integration/api/ps.bats @@ -44,3 +44,57 @@ function teardown() { [ "${#lines[@]}" -eq 2 ] [[ "${lines[1]}" == *"false"* ]] } + +@test "docker ps --filter" { + start_docker_with_busybox 2 + swarm_manage + + # Running + firstID=$(docker_swarm run -d --name name1 --label "match=me" --label "second=tag" busybox sleep 10000) + # Exited - successfull + secondID=$(docker_swarm run -d --name name2 --label "match=me too" busybox true) + # Exited - error + thirdID=$(docker_swarm run -d --name name3 --label "nomatch=me" busybox false) + + # status + run docker_swarm ps -q --no-trunc --filter=status=exited + [ "${#lines[@]}" -eq 2 ] + [[ "$output" != *"$firstID"* ]] + [[ "$output" == *"$secondID"* ]] + [[ "$output" == *"$thirdID"* ]] + run docker_swarm ps -q -a --no-trunc --filter=status=running + [[ "$output" == "$firstID" ]] + + # id + run docker_swarm ps -a -q --no-trunc --filter=id="$secondID" + [[ "$output" == "$secondID" ]] + run docker_swarm ps -a -q --no-trunc --filter=id="bogusID" + [ "${#lines[@]}" -eq 0 ] + + # name + run docker_swarm ps -a -q --no-trunc --filter=name=name3 + [[ "$output" == "$thirdID" ]] + run docker_swarm ps -a -q --no-trunc --filter=name=badname + [ "${#lines[@]}" -eq 0 ] + + # exit code + run docker_swarm ps -a -q --no-trunc --filter=exited=0 + [[ "$output" == "$secondID" ]] + run docker_swarm ps -a -q --no-trunc --filter=exited=1 + [[ "$output" == "$thirdID" ]] + run docker_swarm ps -a -q --no-trunc --filter=exited=99 + [ "${#lines[@]}" -eq 0 ] + + # labels + run docker_swarm ps -a -q --no-trunc --filter=label=match=me + [[ "$output" == "$firstID" ]] + run docker_swarm ps -a -q --no-trunc --filter=label=match=me --filter=label=second=tag + [[ "$output" == "$firstID" ]] + run docker_swarm ps -a -q --no-trunc --filter=label=match=me --filter=label=second=tag-no + [ "${#lines[@]}" -eq 0 ] + run docker_swarm ps -a -q --no-trunc --filter=label=match + [ "${#lines[@]}" -eq 2 ] + [[ "$output" == *"$firstID"* ]] + [[ "$output" == *"$secondID"* ]] + [[ "$output" != *"$thirdID"* ]] +}