From 72ff20f7e9207b7227dbe2a29d8d87a2f4bba87b Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Mon, 5 Jun 2023 15:21:31 -0600 Subject: [PATCH] filters: better handling of id= For filter=id=XXX (containers, pods) and =ctr-ids=XXX (pods): if XXX is only hex characters, treat it as a PREFIX otherwise, treat it as a REGEX Add tests. Update documentation. And fix an incorrect help message. Fixes: #18471 Signed-off-by: Ed Santiago --- cmd/podman/pods/ps.go | 2 +- docs/source/markdown/podman-pause.1.md.in | 28 +++--- docs/source/markdown/podman-pod-ps.1.md.in | 24 ++--- docs/source/markdown/podman-ps.1.md | 28 +++--- docs/source/markdown/podman-restart.1.md.in | 28 +++--- docs/source/markdown/podman-rm.1.md.in | 28 +++--- docs/source/markdown/podman-start.1.md.in | 28 +++--- docs/source/markdown/podman-stop.1.md.in | 28 +++--- docs/source/markdown/podman-unpause.1.md.in | 28 +++--- libpod/define/config.go | 4 + pkg/domain/filters/containers.go | 15 +++- pkg/domain/filters/pods.go | 35 +++++++- test/system/040-ps.bats | 87 ++++++++++++++---- test/system/200-pod.bats | 97 +++++++++++++++++++++ 14 files changed, 327 insertions(+), 133 deletions(-) diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 893058643e81..2b9683ae43e3 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -290,7 +290,7 @@ func sortPodPsOutput(sortBy string, lprs []*entities.ListPodsReport) error { case "status": sort.Sort(podPsSortedStatus{lprs}) default: - return errors.New("invalid option for --sort, options are: id, names, or number") + return errors.New("invalid option for --sort, options are: created, id, name, number, or status") } return nil } diff --git a/docs/source/markdown/podman-pause.1.md.in b/docs/source/markdown/podman-pause.1.md.in index 7194e8143a4f..bc26396583cf 100644 --- a/docs/source/markdown/podman-pause.1.md.in +++ b/docs/source/markdown/podman-pause.1.md.in @@ -28,20 +28,20 @@ Filters with the same key work inclusive with the only exception being Valid filters are listed below: -| **Filter** | **Description** | -| --------------- | -------------------------------------------------------------------------------- | -| id | [ID] Container's ID (accepts regex) | -| name | [Name] Container's name (accepts regex) | -| label | [Key] or [Key=Value] Label assigned to a container | -| exited | [Int] Container's exit code | -| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | -| ancestor | [ImageName] Image or descendant used to create container | -| before | [ID] or [Name] Containers created before this container | -| since | [ID] or [Name] Containers created since this container | -| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | -| health | [Status] healthy or unhealthy | -| pod | [Pod] name or full or partial ID of pod | -| network | [Network] name or full ID of network | +| **Filter** | **Description** | +|------------|----------------------------------------------------------------------------------| +| id | [ID] Container's ID (CID prefix match by default; accepts regex) | +| name | [Name] Container's name (accepts regex) | +| label | [Key] or [Key=Value] Label assigned to a container | +| exited | [Int] Container's exit code | +| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | +| ancestor | [ImageName] Image or descendant used to create container | +| before | [ID] or [Name] Containers created before this container | +| since | [ID] or [Name] Containers created since this container | +| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | +| health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | @@option latest diff --git a/docs/source/markdown/podman-pod-ps.1.md.in b/docs/source/markdown/podman-pod-ps.1.md.in index 117d904acd95..57b861c98f11 100644 --- a/docs/source/markdown/podman-pod-ps.1.md.in +++ b/docs/source/markdown/podman-pod-ps.1.md.in @@ -48,18 +48,18 @@ The *filters* argument format is of `key=value`. If there is more than one *filt Supported filters: -| Filter | Description | -| ---------- | -------------------------------------------------------------------------------------------------- | -| *ctr-ids* | Filter by container ID within the pod. | -| *ctr-names* | Filter by container name within the pod. | -| *ctr-number*| Filter by number of containers in the pod. | -| *ctr-status*| Filter by container status within the pod. | -| *id* | Filter by pod ID. | -| *label* | Filter by container with (or without, in the case of label!=[...] is used) the specified labels. | -| *name* | Filter by pod name. | -| *network* | Filter by network name or full ID of network. | -| *status* | Filter by pod status. | -| *until* | Filter by pods created before given timestamp. | +| Filter | Description | +|--------------|--------------------------------------------------------------------------------------------------| +| *ctr-ids* | Filter by container ID within the pod. (CID prefix match by default; accepts regex) | +| *ctr-names* | Filter by container name within the pod. | +| *ctr-number* | Filter by number of containers in the pod. | +| *ctr-status* | Filter by container status within the pod. | +| *id* | Filter by pod ID. (Prefix match by default; accepts regex) | +| *label* | Filter by container with (or without, in the case of label!=[...] is used) the specified labels. | +| *name* | Filter by pod name. | +| *network* | Filter by network name or full ID of network. | +| *status* | Filter by pod status. | +| *until* | Filter by pods created before given timestamp. | The `ctr-ids`, `ctr-names`, `id`, `name` filters accept `regex` format. diff --git a/docs/source/markdown/podman-ps.1.md b/docs/source/markdown/podman-ps.1.md index c6dd604836d8..fb5620e0a76e 100644 --- a/docs/source/markdown/podman-ps.1.md +++ b/docs/source/markdown/podman-ps.1.md @@ -45,20 +45,20 @@ Filters with the same key work inclusive with the only exception being Valid filters are listed below: -| **Filter** | **Description** | -| --------------- | -------------------------------------------------------------------------------- | -| id | [ID] Container's ID (accepts regex) | -| name | [Name] Container's name (accepts regex) | -| label | [Key] or [Key=Value] Label assigned to a container | -| exited | [Int] Container's exit code | -| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | -| ancestor | [ImageName] Image or descendant used to create container (accepts regex) | -| before | [ID] or [Name] Containers created before this container | -| since | [ID] or [Name] Containers created since this container | -| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | -| health | [Status] healthy or unhealthy | -| pod | [Pod] name or full or partial ID of pod | -| network | [Network] name or full ID of network | +| **Filter** | **Description** | +|------------|----------------------------------------------------------------------------------| +| id | [ID] Container's ID (CID prefix match by default; accepts regex) | +| name | [Name] Container's name (accepts regex) | +| label | [Key] or [Key=Value] Label assigned to a container | +| exited | [Int] Container's exit code | +| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | +| ancestor | [ImageName] Image or descendant used to create container (accepts regex) | +| before | [ID] or [Name] Containers created before this container | +| since | [ID] or [Name] Containers created since this container | +| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | +| health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | #### **--format**=*format* diff --git a/docs/source/markdown/podman-restart.1.md.in b/docs/source/markdown/podman-restart.1.md.in index dc57e1217551..167f5d7359f1 100644 --- a/docs/source/markdown/podman-restart.1.md.in +++ b/docs/source/markdown/podman-restart.1.md.in @@ -31,20 +31,20 @@ Filters with the same key work inclusive with the only exception being Valid filters are listed below: -| **Filter** | **Description** | -| --------------- | -------------------------------------------------------------------------------- | -| id | [ID] Container's ID (accepts regex) | -| name | [Name] Container's name (accepts regex) | -| label | [Key] or [Key=Value] Label assigned to a container | -| exited | [Int] Container's exit code | -| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | -| ancestor | [ImageName] Image or descendant used to create container | -| before | [ID] or [Name] Containers created before this container | -| since | [ID] or [Name] Containers created since this container | -| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | -| health | [Status] healthy or unhealthy | -| pod | [Pod] name or full or partial ID of pod | -| network | [Network] name or full ID of network | +| **Filter** | **Description** | +|------------|----------------------------------------------------------------------------------| +| id | [ID] Container's ID (CID prefix match by default; accepts regex) | +| name | [Name] Container's name (accepts regex) | +| label | [Key] or [Key=Value] Label assigned to a container | +| exited | [Int] Container's exit code | +| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | +| ancestor | [ImageName] Image or descendant used to create container | +| before | [ID] or [Name] Containers created before this container | +| since | [ID] or [Name] Containers created since this container | +| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | +| health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | @@option latest diff --git a/docs/source/markdown/podman-rm.1.md.in b/docs/source/markdown/podman-rm.1.md.in index b42c2ee5f8a6..30c53922a413 100644 --- a/docs/source/markdown/podman-rm.1.md.in +++ b/docs/source/markdown/podman-rm.1.md.in @@ -33,20 +33,20 @@ Filters with the same key work inclusive with the only exception being Valid filters are listed below: -| **Filter** | **Description** | -| --------------- | -------------------------------------------------------------------------------- | -| id | [ID] Container's ID (accepts regex) | -| name | [Name] Container's name (accepts regex) | -| label | [Key] or [Key=Value] Label assigned to a container | -| exited | [Int] Container's exit code | -| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | -| ancestor | [ImageName] Image or descendant used to create container | -| before | [ID] or [Name] Containers created before this container | -| since | [ID] or [Name] Containers created since this container | -| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | -| health | [Status] healthy or unhealthy | -| pod | [Pod] name or full or partial ID of pod | -| network | [Network] name or full ID of network | +| **Filter** | **Description** | +|------------|----------------------------------------------------------------------------------| +| id | [ID] Container's ID (CID prefix match by default; accepts regex) | +| name | [Name] Container's name (accepts regex) | +| label | [Key] or [Key=Value] Label assigned to a container | +| exited | [Int] Container's exit code | +| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | +| ancestor | [ImageName] Image or descendant used to create container | +| before | [ID] or [Name] Containers created before this container | +| since | [ID] or [Name] Containers created since this container | +| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | +| health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | #### **--force**, **-f** diff --git a/docs/source/markdown/podman-start.1.md.in b/docs/source/markdown/podman-start.1.md.in index 34ba0acf3b5d..23d7d5f7cfb8 100644 --- a/docs/source/markdown/podman-start.1.md.in +++ b/docs/source/markdown/podman-start.1.md.in @@ -36,20 +36,20 @@ Filters with the same key work inclusive with the only exception being Valid filters are listed below: -| **Filter** | **Description** | -| --------------- | -------------------------------------------------------------------------------- | -| id | [ID] Container's ID (accepts regex) | -| name | [Name] Container's name (accepts regex) | -| label | [Key] or [Key=Value] Label assigned to a container | -| exited | [Int] Container's exit code | -| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | -| ancestor | [ImageName] Image or descendant used to create container | -| before | [ID] or [Name] Containers created before this container | -| since | [ID] or [Name] Containers created since this container | -| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | -| health | [Status] healthy or unhealthy | -| pod | [Pod] name or full or partial ID of pod | -| network | [Network] name or full ID of network | +| **Filter** | **Description** | +|------------|----------------------------------------------------------------------------------| +| id | [ID] Container's ID (CID prefix match by default; accepts regex) | +| name | [Name] Container's name (accepts regex) | +| label | [Key] or [Key=Value] Label assigned to a container | +| exited | [Int] Container's exit code | +| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | +| ancestor | [ImageName] Image or descendant used to create container | +| before | [ID] or [Name] Containers created before this container | +| since | [ID] or [Name] Containers created since this container | +| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | +| health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | @@option interactive diff --git a/docs/source/markdown/podman-stop.1.md.in b/docs/source/markdown/podman-stop.1.md.in index c87cf7f5a169..4a6f09c7440e 100644 --- a/docs/source/markdown/podman-stop.1.md.in +++ b/docs/source/markdown/podman-stop.1.md.in @@ -32,20 +32,20 @@ Filters with the same key work inclusive with the only exception being Valid filters are listed below: -| **Filter** | **Description** | -| --------------- | -------------------------------------------------------------------------------- | -| id | [ID] Container's ID (accepts regex) | -| name | [Name] Container's name (accepts regex) | -| label | [Key] or [Key=Value] Label assigned to a container | -| exited | [Int] Container's exit code | -| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | -| ancestor | [ImageName] Image or descendant used to create container | -| before | [ID] or [Name] Containers created before this container | -| since | [ID] or [Name] Containers created since this container | -| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | -| health | [Status] healthy or unhealthy | -| pod | [Pod] name or full or partial ID of pod | -| network | [Network] name or full ID of network | +| **Filter** | **Description** | +|------------|----------------------------------------------------------------------------------| +| id | [ID] Container's ID (CID prefix match by default; accepts regex) | +| name | [Name] Container's name (accepts regex) | +| label | [Key] or [Key=Value] Label assigned to a container | +| exited | [Int] Container's exit code | +| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | +| ancestor | [ImageName] Image or descendant used to create container | +| before | [ID] or [Name] Containers created before this container | +| since | [ID] or [Name] Containers created since this container | +| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | +| health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | @@option ignore diff --git a/docs/source/markdown/podman-unpause.1.md.in b/docs/source/markdown/podman-unpause.1.md.in index 3ff40f7b184a..1078a23eb405 100644 --- a/docs/source/markdown/podman-unpause.1.md.in +++ b/docs/source/markdown/podman-unpause.1.md.in @@ -28,20 +28,20 @@ Filters with the same key work inclusive with the only exception being Valid filters are listed below: -| **Filter** | **Description** | -| --------------- | -------------------------------------------------------------------------------- | -| id | [ID] Container's ID (accepts regex) | -| name | [Name] Container's name (accepts regex) | -| label | [Key] or [Key=Value] Label assigned to a container | -| exited | [Int] Container's exit code | -| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | -| ancestor | [ImageName] Image or descendant used to create container | -| before | [ID] or [Name] Containers created before this container | -| since | [ID] or [Name] Containers created since this container | -| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | -| health | [Status] healthy or unhealthy | -| pod | [Pod] name or full or partial ID of pod | -| network | [Network] name or full ID of network | +| **Filter** | **Description** | +|------------|----------------------------------------------------------------------------------| +| id | [ID] Container's ID (CID prefix match by default; accepts regex) | +| name | [Name] Container's name (accepts regex) | +| label | [Key] or [Key=Value] Label assigned to a container | +| exited | [Int] Container's exit code | +| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | +| ancestor | [ImageName] Image or descendant used to create container | +| before | [ID] or [Name] Containers created before this container | +| since | [ID] or [Name] Containers created since this container | +| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | +| health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | @@option latest diff --git a/libpod/define/config.go b/libpod/define/config.go index 7295f1425e20..29801debb3c1 100644 --- a/libpod/define/config.go +++ b/libpod/define/config.go @@ -5,6 +5,7 @@ import ( "io" "github.com/containers/common/libnetwork/types" + "github.com/containers/storage/pkg/regexp" ) var ( @@ -17,6 +18,9 @@ var ( // This must NOT be changed from outside of Libpod. It should be a // constant, but Go won't let us do that. NameRegex = types.NameRegex + // NotHexRegex is a regular expression to check if a string is + // a hexadecimal string. + NotHexRegex = regexp.Delayed(`[^0-9a-fA-F]`) // RegexError is thrown in presence of an invalid container/pod name. RegexError = types.RegexError ) diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index 99b11f048f8f..fb46c0932805 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -3,6 +3,7 @@ package filters import ( "errors" "fmt" + "regexp" "strconv" "strings" "time" @@ -18,9 +19,19 @@ import ( func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { switch filter { case "id": - // we only have to match one ID return func(c *libpod.Container) bool { - return util.StringMatchRegexSlice(c.ID(), filterValues) + for _, want := range filterValues { + isRegex := define.NotHexRegex.MatchString(want) + if isRegex { + match, err := regexp.MatchString(want, c.ID()) + if err == nil && match { + return true + } + } else if strings.HasPrefix(c.ID(), strings.ToLower(want)) { + return true + } + } + return false }, nil case "label": // we have to match that all given labels exits on that container diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 7b0944292592..ef345b11a1e2 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -3,6 +3,7 @@ package filters import ( "errors" "fmt" + "regexp" "strconv" "strings" @@ -25,8 +26,25 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti if err != nil { return false } - for _, id := range ctrIds { - return util.StringMatchRegexSlice(id, filterValues) + for _, want := range filterValues { + isRegex := define.NotHexRegex.MatchString(want) + if isRegex { + re, err := regexp.Compile(want) + if err != nil { + return false + } + for _, id := range ctrIds { + if re.MatchString(id) { + return true + } + } + } else { + for _, id := range ctrIds { + if strings.HasPrefix(id, strings.ToLower(want)) { + return true + } + } + } } return false }, nil @@ -89,7 +107,18 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti }, nil case "id": return func(p *libpod.Pod) bool { - return util.StringMatchRegexSlice(p.ID(), filterValues) + for _, want := range filterValues { + isRegex := define.NotHexRegex.MatchString(want) + if isRegex { + match, err := regexp.MatchString(want, p.ID()) + if err == nil && match { + return true + } + } else if strings.HasPrefix(p.ID(), strings.ToLower(want)) { + return true + } + } + return false }, nil case "name": return func(p *libpod.Pod) bool { diff --git a/test/system/040-ps.bats b/test/system/040-ps.bats index 08bcd39f305c..80500a84c1a8 100644 --- a/test/system/040-ps.bats +++ b/test/system/040-ps.bats @@ -37,22 +37,37 @@ load helpers } @test "podman ps --filter" { - run_podman run -d --name runner $IMAGE top - cid_runner=$output - - run_podman run -d --name stopped $IMAGE true - cid_stopped=$output - run_podman wait stopped - - run_podman run -d --name failed $IMAGE false - cid_failed=$output - run_podman wait failed - + local -A cid + + # Create three containers, each of whose CID begins with a different char + run_podman run -d --name running $IMAGE top + cid[running]=$output + + cid[stopped]=$output + while [[ ${cid[stopped]:0:1} == ${cid[running]:0:1} ]]; do + run_podman rm -f stopped + run_podman run -d --name stopped $IMAGE true + cid[stopped]=$output + run_podman wait stopped + done + + cid[failed]=${cid[stopped]} + while [[ ${cid[failed]:0:1} == ${cid[running]:0:1} ]] || [[ ${cid[failed]:0:1} == ${cid[stopped]:0:1} ]]; do + run_podman rm -f failed + run_podman run -d --name failed $IMAGE false + cid[failed]=$output + run_podman wait failed + done + + # This one is never tested in the id filter, so its cid can be anything run_podman create --name created $IMAGE echo hi - cid_created=$output + cid[created]=$output - run_podman ps --filter name=runner --format '{{.ID}}' - is "$output" "${cid_runner:0:12}" "filter: name=runner" + # For debugging + run_podman ps -a + + run_podman ps --filter name=running --format '{{.ID}}' + is "$output" "${cid[running]:0:12}" "filter: name=running" # Stopped container should not appear (because we're not using -a) run_podman ps --filter name=stopped --format '{{.ID}}' @@ -60,7 +75,7 @@ load helpers # Again, but with -a run_podman ps -a --filter name=stopped --format '{{.ID}}' - is "$output" "${cid_stopped:0:12}" "filter: name=stopped (with -a)" + is "$output" "${cid[stopped]:0:12}" "filter: name=stopped (with -a)" run_podman ps --filter status=stopped --format '{{.Names}}' --sort names is "${lines[0]}" "failed" "status=stopped: 1 of 2" @@ -72,14 +87,52 @@ load helpers run_podman ps --filter status=exited --filter exited=1 --format '{{.Names}}' is "$output" "failed" "exited=1" + run_podman ps --filter status=created --format '{{.Names}}' + is "$output" "created" "state=created" + # Multiple statuses allowed; and test sort=created run_podman ps -a --filter status=exited --filter status=running \ --format '{{.Names}}' --sort created - is "${lines[0]}" "runner" "status=stopped: 1 of 3" + is "${lines[0]}" "running" "status=stopped: 1 of 3" is "${lines[1]}" "stopped" "status=stopped: 2 of 3" is "${lines[2]}" "failed" "status=stopped: 3 of 3" - run_podman stop -t 1 runner + # ID filtering: if filter is only hex chars, it's a prefix; if it has + # anything else, it's a regex + run_podman rm created + for state in running stopped failed; do + local test_cid=${cid[$state]} + for prefix in ${test_cid:0:1} ${test_cid:0:2} ${test_cid:0:13}; do + # Test lower-case (default), upper-case, and with '^' anchor + for uclc in ${prefix,,} ${prefix^^} "^$prefix"; do + run_podman ps -a --filter id=$uclc --format '{{.Names}}' + assert "$output" = "$state" "ps --filter id=$uclc" + done + done + + # Regex check + local f="^[^${test_cid:0:1}]" + run_podman ps -a --filter id="$f" --format '{{.Names}}' + assert "${#lines[*]}" == "2" "filter id=$f: number of lines" + assert "$output" !~ $state "filter id=$f: '$state' not in results" + done + + # All CIDs will have hex characters + run_podman ps -a --filter id="[0-9a-f]" --format '{{.Names}}' --sort names + assert "${lines[0]}" == "failed" "filter id=[0-9a-f], line 1" + assert "${lines[1]}" == "running" "filter id=[0-9a-f], line 2" + assert "${lines[2]}" == "stopped" "filter id=[0-9a-f], line 3" + + run_podman ps -a --filter id="[^0-9a-f]" --noheading + assert "$output" = "" "id=[^0-9a-f], should match no containers" + + # Finally, multiple filters + run_podman ps -a --filter id=${cid[running]} --filter id=${cid[failed]} \ + --format '{{.Names}}' --sort names + assert "${lines[0]}" == "failed" "filter id=running+failed, line 1" + assert "${lines[1]}" == "running" "filter id=running+failed, line 2" + + run_podman stop -t 1 running run_podman rm -a } diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index d0fbecd290f6..442517b46107 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -567,4 +567,101 @@ io.max | $lomajmin rbps=1048576 wbps=1048576 riops=max wiops=max run_podman 1 pod exists $podname } +# Helper used by pod ps --filter test. Creates one pod or container +# with a UNIQUE two-character CID prefix. +function thingy_with_unique_id() { + local what="$1"; shift # pod or container + local how="$1"; shift # e.g. "--name p1c1 --pod p1" + + while :;do + local try_again= + + run_podman $what create $how + # This is our return value; it propagates up to caller's namespace + id="$output" + + # Make sure the first two characters aren't already used in an ID + for existing_id in "$@"; do + if [[ -z "$try_again" ]]; then + if [[ "${existing_id:0:2}" == "${id:0:2}" ]]; then + run_podman $what rm $id + try_again=1 + fi + fi + done + + if [[ -z "$try_again" ]]; then + # Nope! groovy! caller gets $id + return + fi + done +} + +@test "podman pod ps --filter" { + local -A podid + local -A ctrid + + # Setup: create three pods, each with three containers, all of them with + # unique (distinct) first two characters of their pod/container ID. + for p in 1 2 3;do + # no infra, please! That creates an extra container with a CID + # that may collide with our other ones, and it's too hard to fix. + thingy_with_unique_id "pod" "--infra=false --name p${p}" \ + ${podid[*]} ${ctrid[*]} + podid[$p]=$id + + for c in 1 2 3; do + thingy_with_unique_id "container" \ + "--pod p${p} --name p${p}c${c} $IMAGE true" \ + ${podid[*]} ${ctrid[*]} + ctrid[$p$c]=$id + done + done + + # for debugging; without this, on test failure it's too hard to + # associate IDs with names + run_podman pod ps + run_podman ps -a + + # Test: ps and filter for each pod and container, by ID + for p in 1 2 3; do + local pid=${podid[$p]} + + # Search by short pod ID, longer pod ID, pod ID regex, and pod name + # ps by short ID, longer ID, regex, and name + for filter in "id=${pid:0:2}" "id=${pid:0:10}" "id=^${pid:0:2}" "name=p$p"; do + run_podman pod ps --filter=$filter --format '{{.Name}}:{{.Id}}' + assert "$output" == "p$p:${pid:0:12}" "pod $p, filter=$filter" + done + + # ps by negation (regex) of our pid, should find all other pods + f1="^[^${pid:0:1}]" + f2="^.[^${pid:1:1}]" + run_podman pod ps --filter=id="$f1" --filter=id="$f2" --format '{{.Name}}' + assert "${#lines[*]}" == "2" "filter=$f1 + $f2 finds 2 pods" + assert "$output" !~ "p$p" "filter=$f1 + $f2 does not find p$p" + + # Search by *container* ID + for c in 1 2 3;do + local cid=${ctrid[$p$c]} + for filter in "ctr-ids=${cid:0:2}" "ctr-ids=^${cid:0:2}.*"; do + run_podman pod ps --filter=$filter --format '{{.Name}}:{{.Id}}' + assert "$output" == "p${p}:${pid:0:12}" \ + "pod $p, container $c, filter=$filter" + done + done + done + + # Multiple filters, multiple pods + run_podman pod ps --filter=ctr-ids=${ctrid[12]} \ + --filter=ctr-ids=${ctrid[23]} \ + --filter=ctr-ids=${ctrid[31]} \ + --format='{{.Name}}' --sort=name + assert "$(echo $output)" == "p1 p2 p3" "multiple ctr-ids filters" + + # Clean up + run_podman pod rm -f -a + run_podman rm -f -a +} + # vim: filetype=sh