mirror of https://github.com/docker/docs.git
docker ps: Support for filters and improve state management.
- `docker ps` now fully supports `--filter` flags - Generate `Status` in `docker ps` dynamically. "Up X seconds" is now real-time. - Misc cleanup. Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
parent
8a00a5ff10
commit
c69f0db71f
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"* ]]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue