mirror of
https://github.com/containers/podman.git
synced 2025-06-26 12:56:45 +08:00
Merge pull request #18798 from edsantiago/fix_filters
filters: better handling of id=
This commit is contained in:
@ -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
|
||||
}
|
||||
|
@ -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 |
|
||||
|
@ -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. |
|
||||
| *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. |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user