Merge pull request #18798 from edsantiago/fix_filters

filters: better handling of id=
This commit is contained in:
OpenShift Merge Robot
2023-06-07 12:31:11 -04:00
committed by GitHub
14 changed files with 324 additions and 130 deletions

View File

@ -290,7 +290,7 @@ func sortPodPsOutput(sortBy string, lprs []*entities.ListPodsReport) error {
case "status": case "status":
sort.Sort(podPsSortedStatus{lprs}) sort.Sort(podPsSortedStatus{lprs})
default: 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 return nil
} }

View File

@ -29,8 +29,8 @@ Filters with the same key work inclusive with the only exception being
Valid filters are listed below: Valid filters are listed below:
| **Filter** | **Description** | | **Filter** | **Description** |
| --------------- | -------------------------------------------------------------------------------- | |------------|----------------------------------------------------------------------------------|
| id | [ID] Container's ID (accepts regex) | | id | [ID] Container's ID (CID prefix match by default; accepts regex) |
| name | [Name] Container's name (accepts regex) | | name | [Name] Container's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container | | label | [Key] or [Key=Value] Label assigned to a container |
| exited | [Int] Container's exit code | | exited | [Int] Container's exit code |

View File

@ -49,12 +49,12 @@ The *filters* argument format is of `key=value`. If there is more than one *filt
Supported filters: Supported filters:
| Filter | Description | | Filter | Description |
| ---------- | -------------------------------------------------------------------------------------------------- | |--------------|--------------------------------------------------------------------------------------------------|
| *ctr-ids* | Filter by container ID within the pod. | | *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-names* | Filter by container name within the pod. |
| *ctr-number*| Filter by number of containers in the pod. | | *ctr-number* | Filter by number of containers in the pod. |
| *ctr-status*| Filter by container status within the pod. | | *ctr-status* | Filter by container status within the pod. |
| *id* | Filter by pod ID. | | *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. | | *label* | Filter by container with (or without, in the case of label!=[...] is used) the specified labels. |
| *name* | Filter by pod name. | | *name* | Filter by pod name. |
| *network* | Filter by network name or full ID of network. | | *network* | Filter by network name or full ID of network. |

View File

@ -46,8 +46,8 @@ Filters with the same key work inclusive with the only exception being
Valid filters are listed below: Valid filters are listed below:
| **Filter** | **Description** | | **Filter** | **Description** |
| --------------- | -------------------------------------------------------------------------------- | |------------|----------------------------------------------------------------------------------|
| id | [ID] Container's ID (accepts regex) | | id | [ID] Container's ID (CID prefix match by default; accepts regex) |
| name | [Name] Container's name (accepts regex) | | name | [Name] Container's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container | | label | [Key] or [Key=Value] Label assigned to a container |
| exited | [Int] Container's exit code | | exited | [Int] Container's exit code |

View File

@ -32,8 +32,8 @@ Filters with the same key work inclusive with the only exception being
Valid filters are listed below: Valid filters are listed below:
| **Filter** | **Description** | | **Filter** | **Description** |
| --------------- | -------------------------------------------------------------------------------- | |------------|----------------------------------------------------------------------------------|
| id | [ID] Container's ID (accepts regex) | | id | [ID] Container's ID (CID prefix match by default; accepts regex) |
| name | [Name] Container's name (accepts regex) | | name | [Name] Container's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container | | label | [Key] or [Key=Value] Label assigned to a container |
| exited | [Int] Container's exit code | | exited | [Int] Container's exit code |

View File

@ -34,8 +34,8 @@ Filters with the same key work inclusive with the only exception being
Valid filters are listed below: Valid filters are listed below:
| **Filter** | **Description** | | **Filter** | **Description** |
| --------------- | -------------------------------------------------------------------------------- | |------------|----------------------------------------------------------------------------------|
| id | [ID] Container's ID (accepts regex) | | id | [ID] Container's ID (CID prefix match by default; accepts regex) |
| name | [Name] Container's name (accepts regex) | | name | [Name] Container's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container | | label | [Key] or [Key=Value] Label assigned to a container |
| exited | [Int] Container's exit code | | exited | [Int] Container's exit code |

View File

@ -37,8 +37,8 @@ Filters with the same key work inclusive with the only exception being
Valid filters are listed below: Valid filters are listed below:
| **Filter** | **Description** | | **Filter** | **Description** |
| --------------- | -------------------------------------------------------------------------------- | |------------|----------------------------------------------------------------------------------|
| id | [ID] Container's ID (accepts regex) | | id | [ID] Container's ID (CID prefix match by default; accepts regex) |
| name | [Name] Container's name (accepts regex) | | name | [Name] Container's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container | | label | [Key] or [Key=Value] Label assigned to a container |
| exited | [Int] Container's exit code | | exited | [Int] Container's exit code |

View File

@ -33,8 +33,8 @@ Filters with the same key work inclusive with the only exception being
Valid filters are listed below: Valid filters are listed below:
| **Filter** | **Description** | | **Filter** | **Description** |
| --------------- | -------------------------------------------------------------------------------- | |------------|----------------------------------------------------------------------------------|
| id | [ID] Container's ID (accepts regex) | | id | [ID] Container's ID (CID prefix match by default; accepts regex) |
| name | [Name] Container's name (accepts regex) | | name | [Name] Container's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container | | label | [Key] or [Key=Value] Label assigned to a container |
| exited | [Int] Container's exit code | | exited | [Int] Container's exit code |

View File

@ -29,8 +29,8 @@ Filters with the same key work inclusive with the only exception being
Valid filters are listed below: Valid filters are listed below:
| **Filter** | **Description** | | **Filter** | **Description** |
| --------------- | -------------------------------------------------------------------------------- | |------------|----------------------------------------------------------------------------------|
| id | [ID] Container's ID (accepts regex) | | id | [ID] Container's ID (CID prefix match by default; accepts regex) |
| name | [Name] Container's name (accepts regex) | | name | [Name] Container's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container | | label | [Key] or [Key=Value] Label assigned to a container |
| exited | [Int] Container's exit code | | exited | [Int] Container's exit code |

View File

@ -5,6 +5,7 @@ import (
"io" "io"
"github.com/containers/common/libnetwork/types" "github.com/containers/common/libnetwork/types"
"github.com/containers/storage/pkg/regexp"
) )
var ( var (
@ -17,6 +18,9 @@ var (
// This must NOT be changed from outside of Libpod. It should be a // This must NOT be changed from outside of Libpod. It should be a
// constant, but Go won't let us do that. // constant, but Go won't let us do that.
NameRegex = types.NameRegex 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 is thrown in presence of an invalid container/pod name.
RegexError = types.RegexError RegexError = types.RegexError
) )

View File

@ -3,6 +3,7 @@ package filters
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -18,9 +19,19 @@ import (
func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) {
switch filter { switch filter {
case "id": case "id":
// we only have to match one ID
return func(c *libpod.Container) bool { 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 }, nil
case "label": case "label":
// we have to match that all given labels exits on that container // we have to match that all given labels exits on that container

View File

@ -3,6 +3,7 @@ package filters
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
@ -25,8 +26,25 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti
if err != nil { if err != nil {
return false return false
} }
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 { for _, id := range ctrIds {
return util.StringMatchRegexSlice(id, filterValues) if re.MatchString(id) {
return true
}
}
} else {
for _, id := range ctrIds {
if strings.HasPrefix(id, strings.ToLower(want)) {
return true
}
}
}
} }
return false return false
}, nil }, nil
@ -89,7 +107,18 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti
}, nil }, nil
case "id": case "id":
return func(p *libpod.Pod) bool { 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 }, nil
case "name": case "name":
return func(p *libpod.Pod) bool { return func(p *libpod.Pod) bool {

View File

@ -37,22 +37,37 @@ load helpers
} }
@test "podman ps --filter" { @test "podman ps --filter" {
run_podman run -d --name runner $IMAGE top local -A cid
cid_runner=$output
# 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 run_podman run -d --name stopped $IMAGE true
cid_stopped=$output cid[stopped]=$output
run_podman wait stopped 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 run_podman run -d --name failed $IMAGE false
cid_failed=$output cid[failed]=$output
run_podman wait failed 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 run_podman create --name created $IMAGE echo hi
cid_created=$output cid[created]=$output
run_podman ps --filter name=runner --format '{{.ID}}' # For debugging
is "$output" "${cid_runner:0:12}" "filter: name=runner" 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) # Stopped container should not appear (because we're not using -a)
run_podman ps --filter name=stopped --format '{{.ID}}' run_podman ps --filter name=stopped --format '{{.ID}}'
@ -60,7 +75,7 @@ load helpers
# Again, but with -a # Again, but with -a
run_podman ps -a --filter name=stopped --format '{{.ID}}' 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 run_podman ps --filter status=stopped --format '{{.Names}}' --sort names
is "${lines[0]}" "failed" "status=stopped: 1 of 2" 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}}' run_podman ps --filter status=exited --filter exited=1 --format '{{.Names}}'
is "$output" "failed" "exited=1" 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 # Multiple statuses allowed; and test sort=created
run_podman ps -a --filter status=exited --filter status=running \ run_podman ps -a --filter status=exited --filter status=running \
--format '{{.Names}}' --sort created --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[1]}" "stopped" "status=stopped: 2 of 3"
is "${lines[2]}" "failed" "status=stopped: 3 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 run_podman rm -a
} }

View File

@ -567,4 +567,101 @@ io.max | $lomajmin rbps=1048576 wbps=1048576 riops=max wiops=max
run_podman 1 pod exists $podname 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 # vim: filetype=sh