Browse Source

Adding capability to filter by name, id or status to list containers api
Closes #7599

Signed-off-by: Srini Brahmaroutu <srbrahma@us.ibm.com>

Srini Brahmaroutu 10 năm trước cách đây
mục cha
commit
1634625353
3 tập tin đã thay đổi với 92 bổ sung7 xóa
  1. 12 7
      daemon/list.go
  2. 60 0
      integration-cli/docker_cli_ps_test.go
  3. 20 0
      pkg/parsers/filters/parse.go

+ 12 - 7
daemon/list.go

@@ -28,7 +28,6 @@ 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)
 
@@ -46,8 +45,6 @@ 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)
@@ -76,6 +73,15 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
 		if !container.Running && !all && n <= 0 && since == "" && before == "" {
 			return nil
 		}
+
+		if !psFilters.Match("name", container.Name) {
+			return nil
+		}
+
+		if !psFilters.Match("id", container.ID) {
+			return nil
+		}
+
 		if before != "" && !foundBefore {
 			if container.ID == beforeCont.ID {
 				foundBefore = true
@@ -102,10 +108,9 @@ 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
-			}
+
+		if !psFilters.Match("status", container.State.StateString()) {
+			return nil
 		}
 		displayed++
 		out := &engine.Env{}

+ 60 - 0
integration-cli/docker_cli_ps_test.go

@@ -336,3 +336,63 @@ func TestPsListContainersFilterStatus(t *testing.T) {
 
 	logDone("ps - test ps filter status")
 }
+
+func TestPsListContainersFilterID(t *testing.T) {
+	// start container
+	runCmd := exec.Command(dockerBinary, "run", "-d", "busybox")
+	out, _, err := runCommandWithOutput(runCmd)
+	if err != nil {
+		t.Fatal(out, err)
+	}
+	firstID := stripTrailingCharacters(out)
+
+	// start another container
+	runCmd = exec.Command(dockerBinary, "run", "-d", "busybox", "sh", "-c", "sleep 360")
+	if out, _, err = runCommandWithOutput(runCmd); err != nil {
+		t.Fatal(out, err)
+	}
+
+	// filter containers by id
+	runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--filter=id="+firstID)
+	if out, _, err = runCommandWithOutput(runCmd); err != nil {
+		t.Fatal(out, err)
+	}
+	containerOut := strings.TrimSpace(out)
+	if containerOut != firstID[:12] {
+		t.Fatalf("Expected id %s, got %s for exited filter, output: %q", firstID[:12], containerOut, out)
+	}
+
+	deleteAllContainers()
+
+	logDone("ps - test ps filter id")
+}
+
+func TestPsListContainersFilterName(t *testing.T) {
+	// start container
+	runCmd := exec.Command(dockerBinary, "run", "-d", "--name=a_name_to_match", "busybox")
+	out, _, err := runCommandWithOutput(runCmd)
+	if err != nil {
+		t.Fatal(out, err)
+	}
+	firstID := stripTrailingCharacters(out)
+
+	// start another container
+	runCmd = exec.Command(dockerBinary, "run", "-d", "--name=b_name_to_match", "busybox", "sh", "-c", "sleep 360")
+	if out, _, err = runCommandWithOutput(runCmd); err != nil {
+		t.Fatal(out, err)
+	}
+
+	// filter containers by name
+	runCmd = exec.Command(dockerBinary, "ps", "-a", "-q", "--filter=name=a_name_to_match")
+	if out, _, err = runCommandWithOutput(runCmd); err != nil {
+		t.Fatal(out, err)
+	}
+	containerOut := strings.TrimSpace(out)
+	if containerOut != firstID[:12] {
+		t.Fatalf("Expected id %s, got %s for exited filter, output: %q", firstID[:12], containerOut, out)
+	}
+
+	deleteAllContainers()
+
+	logDone("ps - test ps filter name")
+}

+ 20 - 0
pkg/parsers/filters/parse.go

@@ -3,6 +3,7 @@ package filters
 import (
 	"encoding/json"
 	"errors"
+	"regexp"
 	"strings"
 )
 
@@ -61,3 +62,22 @@ func FromParam(p string) (Args, error) {
 	}
 	return args, nil
 }
+
+func (filters Args) Match(field, source string) bool {
+	fieldValues := filters[field]
+
+	//do not filter if there is no filter set or cannot determine filter
+	if len(fieldValues) == 0 {
+		return true
+	}
+	for _, name2match := range fieldValues {
+		match, err := regexp.MatchString(name2match, source)
+		if err != nil {
+			continue
+		}
+		if match {
+			return true
+		}
+	}
+	return false
+}