Merge pull request #8395 from Luap99/podman-pod-ps-filters

Align the podman pod ps --filter behavior with podman ps
This commit is contained in:
OpenShift Merge Robot 2020-11-18 21:16:21 +01:00 committed by GitHub
commit 4434bd7978
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 117 additions and 75 deletions

View file

@ -80,19 +80,23 @@ 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 |
| label | [Key] or [Key=Value] Label assigned to a container |
| ctr-names | Container name within the pod |
| ctr-ids | Container ID within the pod |
| ctr-status | Container status within the pod |
| ctr-number | Number of containers in the pod |
| **Filter** | **Description** |
| ---------- | ------------------------------------------------------------------------------------- |
| id | [ID] Pod's ID (accepts regex) |
| name | [Name] Pod's name (accepts regex) |
| label | [Key] or [Key=Value] Label assigned to a container |
| 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 |
#### **--help**, **-h**

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,18 +44,22 @@ func GeneratePodFilterFunc(filter, filterValue string) (
if err != nil {
return false
}
fVint, err2 := strconv.Atoi(filterValue)
if err2 != nil {
return false
for _, filterValue := range filterValues {
fVint, err2 := strconv.Atoi(filterValue)
if err2 != nil {
return false
}
if len(ctrIds) == fVint {
return true
}
}
return len(ctrIds) == fVint
return false
}, nil
case "ctr-status":
if !util.StringInSlice(filterValue,
[]string{"created", "restarting", "running", "paused",
"exited", "unknown"}) {
return nil, errors.Errorf("%s is not a valid status", filterValue)
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()
@ -67,56 +70,70 @@ func GeneratePodFilterFunc(filter, filterValue string) (
state := ctrStatus.String()
if ctrStatus == define.ContainerStateConfigured {
state = "created"
} else if ctrStatus == define.ContainerStateStopped {
state = "exited"
}
if state == filterValue {
return true
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":
if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created", "degraded"}) {
return nil, errors.Errorf("%s is not a valid pod status", filterValue)
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
}
if strings.ToLower(status) == filterValue {
return true
}
return false
}, nil
case "label":
var filterArray = strings.SplitN(filterValue, "=", 2)
var filterKey = filterArray[0]
if len(filterArray) > 1 {
filterValue = filterArray[1]
} else {
filterValue = ""
}
return func(p *libpod.Pod) bool {
for labelKey, labelValue := range p.Labels() {
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
for _, filterValue := range filterValues {
if strings.ToLower(status) == filterValue {
return true
}
}
return false
}, nil
case "label":
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 = ""
}
for labelKey, labelValue := range labels {
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
matched = true
break
}
}
if !matched {
return false
}
}
return true
}, nil
}
return nil, errors.Errorf("%s is an invalid filter", filter)
}

View file

@ -11,8 +11,7 @@ import (
func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) {
var (
pods []*libpod.Pod
filters []libpod.PodFilter
pods []*libpod.Pod
)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@ -30,14 +29,13 @@ 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)
if err != nil {
return nil, err
}
filters = append(filters, f)
f, err := lpfilters.GeneratePodFilterFunc(k, v)
if err != nil {
return nil, err
}
filters = append(filters, f)
}
pods, err := runtime.Pods(filters...)
if err != nil {

View file

@ -282,20 +282,17 @@ 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{}
err error
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)
if err != nil {
return nil, err
}
filters = append(filters, f)
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))