Align the podman pod ps --filter behavior with podman ps

Filters with the same key work inclusive with the only exception being
`label` which is exclusive. Filters with different keys always work exclusive.

Also update the documentation with the new behavior.

Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
This commit is contained in:
Paul Holzinger 2020-11-18 11:23:30 +01:00
parent 6ece1d97c4
commit e7fd9234cd
6 changed files with 117 additions and 75 deletions

View File

@ -80,17 +80,21 @@ Default: created
#### **--filter**, **-f**=*filter*
Filter output based on conditions given
Filter output based on conditions given.
Multiple filters can be given with multiple uses of the --filter flag.
Filters with the same key work inclusive with the only exception being
`label` which is exclusive. Filters with different keys always work exclusive.
Valid filters are listed below:
| **Filter** | **Description** |
| --------------- | ------------------------------------------------------------------- |
| id | [ID] Pod's ID |
| name | [Name] Pod's name |
| ---------- | ------------------------------------------------------------------------------------- |
| id | [ID] Pod's ID (accepts regex) |
| name | [Name] Pod's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container |
| ctr-names | Container name within the pod |
| ctr-ids | Container ID within the pod |
| status | Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded` |
| ctr-names | Container name within the pod (accepts regex) |
| ctr-ids | Container ID within the pod (accepts regex) |
| ctr-status | Container status within the pod |
| ctr-number | Number of containers in the pod |

View File

@ -44,15 +44,15 @@ Display external containers that are not controlled by Podman but are stored in
Filter what containers are shown in the output.
Multiple filters can be given with multiple uses of the --filter flag.
If multiple filters are given, only containers which match all of the given filters will be shown.
Results will be drawn from all containers, regardless of whether --all was given.
Filters with the same key work inclusive with the only exception being
`label` which is exclusive. Filters with different keys always work exclusive.
Valid filters are listed below:
| **Filter** | **Description** |
| --------------- | -------------------------------------------------------------------------------- |
| id | [ID] Container's ID |
| name | [Name] Container's name |
| 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' |

View File

@ -1,7 +1,6 @@
package lpfilters
import (
"regexp"
"strconv"
"strings"
@ -9,13 +8,12 @@ import (
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// GeneratePodFilterFunc takes a filter and filtervalue (key, value)
// and generates a libpod function that can be used to filter
// pods
func GeneratePodFilterFunc(filter, filterValue string) (
func GeneratePodFilterFunc(filter string, filterValues []string) (
func(pod *libpod.Pod) bool, error) {
switch filter {
case "ctr-ids":
@ -24,7 +22,10 @@ func GeneratePodFilterFunc(filter, filterValue string) (
if err != nil {
return false
}
return util.StringInSlice(filterValue, ctrIds)
for _, id := range ctrIds {
return util.StringMatchRegexSlice(id, filterValues)
}
return false
}, nil
case "ctr-names":
return func(p *libpod.Pod) bool {
@ -33,9 +34,7 @@ func GeneratePodFilterFunc(filter, filterValue string) (
return false
}
for _, ctr := range ctrs {
if filterValue == ctr.Name() {
return true
}
return util.StringMatchRegexSlice(ctr.Name(), filterValues)
}
return false
}, nil
@ -45,19 +44,23 @@ func GeneratePodFilterFunc(filter, filterValue string) (
if err != nil {
return false
}
for _, filterValue := range filterValues {
fVint, err2 := strconv.Atoi(filterValue)
if err2 != nil {
return false
}
return len(ctrIds) == fVint
if len(ctrIds) == fVint {
return true
}
}
return false
}, nil
case "ctr-status":
if !util.StringInSlice(filterValue,
[]string{"created", "restarting", "running", "paused",
"exited", "unknown"}) {
for _, filterValue := range filterValues {
if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) {
return nil, errors.Errorf("%s is not a valid status", filterValue)
}
}
return func(p *libpod.Pod) bool {
ctrStatuses, err := p.Status()
if err != nil {
@ -67,55 +70,69 @@ func GeneratePodFilterFunc(filter, filterValue string) (
state := ctrStatus.String()
if ctrStatus == define.ContainerStateConfigured {
state = "created"
} else if ctrStatus == define.ContainerStateStopped {
state = "exited"
}
for _, filterValue := range filterValues {
if filterValue == "stopped" {
filterValue = "exited"
}
if state == filterValue {
return true
}
}
}
return false
}, nil
case "id":
return func(p *libpod.Pod) bool {
return strings.Contains(p.ID(), filterValue)
return util.StringMatchRegexSlice(p.ID(), filterValues)
}, nil
case "name":
return func(p *libpod.Pod) bool {
match, err := regexp.MatchString(filterValue, p.Name())
if err != nil {
logrus.Errorf("Failed to compile regex for 'name' filter: %v", err)
return false
}
return match
return util.StringMatchRegexSlice(p.Name(), filterValues)
}, nil
case "status":
for _, filterValue := range filterValues {
if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created", "degraded"}) {
return nil, errors.Errorf("%s is not a valid pod status", filterValue)
}
}
return func(p *libpod.Pod) bool {
status, err := p.GetPodStatus()
if err != nil {
return false
}
for _, filterValue := range filterValues {
if strings.ToLower(status) == filterValue {
return true
}
}
return false
}, nil
case "label":
var filterArray = strings.SplitN(filterValue, "=", 2)
var filterKey = filterArray[0]
return func(p *libpod.Pod) bool {
labels := p.Labels()
for _, filterValue := range filterValues {
matched := false
filterArray := strings.SplitN(filterValue, "=", 2)
filterKey := filterArray[0]
if len(filterArray) > 1 {
filterValue = filterArray[1]
} else {
filterValue = ""
}
return func(p *libpod.Pod) bool {
for labelKey, labelValue := range p.Labels() {
for labelKey, labelValue := range labels {
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
return true
matched = true
break
}
}
if !matched {
return false
}
}
return true
}, nil
}
return nil, errors.Errorf("%s is an invalid filter", filter)

View File

@ -12,7 +12,6 @@ import (
func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) {
var (
pods []*libpod.Pod
filters []libpod.PodFilter
)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@ -30,15 +29,14 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport
UnSupportedParameter("digests")
}
filters := make([]libpod.PodFilter, 0, len(query.Filters))
for k, v := range query.Filters {
for _, filter := range v {
f, err := lpfilters.GeneratePodFilterFunc(k, filter)
f, err := lpfilters.GeneratePodFilterFunc(k, v)
if err != nil {
return nil, err
}
filters = append(filters, f)
}
}
pods, err := runtime.Pods(filters...)
if err != nil {
return nil, err

View File

@ -283,19 +283,16 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp
func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) {
var (
err error
filters = []libpod.PodFilter{}
pds = []*libpod.Pod{}
)
filters := make([]libpod.PodFilter, 0, len(options.Filters))
for k, v := range options.Filters {
for _, filter := range v {
f, err := lpfilters.GeneratePodFilterFunc(k, filter)
f, err := lpfilters.GeneratePodFilterFunc(k, v)
if err != nil {
return nil, err
}
filters = append(filters, f)
}
}
if options.Latest {
pod, err := ic.Libpod.GetLatestPod()

View File

@ -194,12 +194,24 @@ var _ = Describe("Podman ps", func() {
Expect(session.OutputToString()).To(ContainSubstring(podid1))
Expect(session.OutputToString()).To(Not(ContainSubstring(podid2)))
session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-names=test", "--filter", "ctr-status=running"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(podid1))
Expect(session.OutputToString()).To(Not(ContainSubstring(podid2)))
session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", fmt.Sprintf("ctr-ids=%s", cid)})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(podid2))
Expect(session.OutputToString()).To(Not(ContainSubstring(podid1)))
session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-ids=" + cid[:40], "--filter", "ctr-ids=" + cid + "$"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(podid2))
Expect(session.OutputToString()).To(Not(ContainSubstring(podid1)))
_, ec3, podid3 := podmanTest.CreatePod("")
Expect(ec3).To(Equal(0))
@ -210,6 +222,13 @@ var _ = Describe("Podman ps", func() {
Expect(session.OutputToString()).To(ContainSubstring(podid2))
Expect(session.OutputToString()).To(Not(ContainSubstring(podid3)))
session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-number=1", "--filter", "ctr-number=0"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(podid1))
Expect(session.OutputToString()).To(ContainSubstring(podid2))
Expect(session.OutputToString()).To(ContainSubstring(podid3))
session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=running"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@ -224,6 +243,13 @@ var _ = Describe("Podman ps", func() {
Expect(session.OutputToString()).To(Not(ContainSubstring(podid1)))
Expect(session.OutputToString()).To(Not(ContainSubstring(podid3)))
session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=exited", "--filter", "ctr-status=running"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(podid1))
Expect(session.OutputToString()).To(ContainSubstring(podid2))
Expect(session.OutputToString()).To(Not(ContainSubstring(podid3)))
session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=created"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))