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":
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
}

View File

@ -29,8 +29,8 @@ 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) |
|------------|----------------------------------------------------------------------------------|
| 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 |

View File

@ -49,12 +49,12 @@ 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-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. |
| *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. |

View File

@ -46,8 +46,8 @@ 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) |
|------------|----------------------------------------------------------------------------------|
| 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 |

View File

@ -32,8 +32,8 @@ 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) |
|------------|----------------------------------------------------------------------------------|
| 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 |

View File

@ -34,8 +34,8 @@ 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) |
|------------|----------------------------------------------------------------------------------|
| 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 |

View File

@ -37,8 +37,8 @@ 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) |
|------------|----------------------------------------------------------------------------------|
| 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 |

View File

@ -33,8 +33,8 @@ 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) |
|------------|----------------------------------------------------------------------------------|
| 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 |

View File

@ -29,8 +29,8 @@ 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) |
|------------|----------------------------------------------------------------------------------|
| 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 |

View File

@ -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
)

View File

@ -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

View File

@ -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 _, want := range filterValues {
isRegex := define.NotHexRegex.MatchString(want)
if isRegex {
re, err := regexp.Compile(want)
if err != nil {
return false
}
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
}, 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 {

View File

@ -37,22 +37,37 @@ load helpers
}
@test "podman ps --filter" {
run_podman run -d --name runner $IMAGE top
cid_runner=$output
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
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
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
}

View File

@ -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