mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +08:00
Filter pods through pod list api
Refactored current filter pods flow through the shared pod functions so filter pod functionalities can be shared between api and cmd. Signed-off-by: Sujil02 <sushah@redhat.com>
This commit is contained in:
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -13,7 +12,6 @@ import (
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/adapter"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
@ -29,8 +27,6 @@ const (
|
||||
NUM_CTR_INFO = 10
|
||||
)
|
||||
|
||||
type PodFilter func(*adapter.Pod) bool
|
||||
|
||||
var (
|
||||
bc_opts shared.PsOptions
|
||||
)
|
||||
@ -174,29 +170,23 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
|
||||
|
||||
opts.Format = genPodPsFormat(c)
|
||||
|
||||
var filterFuncs []PodFilter
|
||||
if c.Filter != "" {
|
||||
filters := strings.Split(c.Filter, ",")
|
||||
for _, f := range filters {
|
||||
filterSplit := strings.Split(f, "=")
|
||||
if len(filterSplit) < 2 {
|
||||
return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
|
||||
}
|
||||
generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1])
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "invalid filter")
|
||||
}
|
||||
filterFuncs = append(filterFuncs, generatedFunc)
|
||||
}
|
||||
}
|
||||
|
||||
var pods []*adapter.Pod
|
||||
|
||||
// If latest is set true filters are ignored.
|
||||
if c.Latest {
|
||||
pod, err := runtime.GetLatestPod()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pods = append(pods, pod)
|
||||
return generatePodPsOutput(pods, opts)
|
||||
}
|
||||
|
||||
if c.Filter != "" {
|
||||
pods, err = runtime.GetPodsWithFilters(c.Filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
pods, err = runtime.GetAllPods()
|
||||
if err != nil {
|
||||
@ -204,19 +194,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
|
||||
}
|
||||
}
|
||||
|
||||
podsFiltered := make([]*adapter.Pod, 0, len(pods))
|
||||
for _, pod := range pods {
|
||||
include := true
|
||||
for _, filter := range filterFuncs {
|
||||
include = include && filter(pod)
|
||||
}
|
||||
|
||||
if include {
|
||||
podsFiltered = append(podsFiltered, pod)
|
||||
}
|
||||
}
|
||||
|
||||
return generatePodPsOutput(podsFiltered, opts)
|
||||
return generatePodPsOutput(pods, opts)
|
||||
}
|
||||
|
||||
// podPsCheckFlagsPassed checks if mutually exclusive flags are passed together
|
||||
@ -235,88 +213,6 @@ func podPsCheckFlagsPassed(c *cliconfig.PodPsValues) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func generatePodFilterFuncs(filter, filterValue string) (func(pod *adapter.Pod) bool, error) {
|
||||
switch filter {
|
||||
case "ctr-ids":
|
||||
return func(p *adapter.Pod) bool {
|
||||
ctrIds, err := p.AllContainersByID()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return util.StringInSlice(filterValue, ctrIds)
|
||||
}, nil
|
||||
case "ctr-names":
|
||||
return func(p *adapter.Pod) bool {
|
||||
ctrs, err := p.AllContainers()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, ctr := range ctrs {
|
||||
if filterValue == ctr.Name() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, nil
|
||||
case "ctr-number":
|
||||
return func(p *adapter.Pod) bool {
|
||||
ctrIds, err := p.AllContainersByID()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
fVint, err2 := strconv.Atoi(filterValue)
|
||||
if err2 != nil {
|
||||
return false
|
||||
}
|
||||
return len(ctrIds) == fVint
|
||||
}, 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)
|
||||
}
|
||||
return func(p *adapter.Pod) bool {
|
||||
ctr_statuses, err := p.Status()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, ctr_status := range ctr_statuses {
|
||||
state := ctr_status.String()
|
||||
if ctr_status == define.ContainerStateConfigured {
|
||||
state = "created"
|
||||
}
|
||||
if state == filterValue {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, nil
|
||||
case "id":
|
||||
return func(p *adapter.Pod) bool {
|
||||
return strings.Contains(p.ID(), filterValue)
|
||||
}, nil
|
||||
case "name":
|
||||
return func(p *adapter.Pod) bool {
|
||||
return strings.Contains(p.Name(), filterValue)
|
||||
}, nil
|
||||
case "status":
|
||||
if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
|
||||
return nil, errors.Errorf("%s is not a valid pod status", filterValue)
|
||||
}
|
||||
return func(p *adapter.Pod) bool {
|
||||
status, err := p.GetPodStatus()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if strings.ToLower(status) == filterValue {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.Errorf("%s is an invalid filter", filter)
|
||||
}
|
||||
|
||||
// generate the template based on conditions given
|
||||
func genPodPsFormat(c *cliconfig.PodPsValues) string {
|
||||
format := ""
|
||||
|
@ -2,9 +2,11 @@ package shared
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/pkg/errors"
|
||||
@ -134,4 +136,128 @@ func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) {
|
||||
return portBindings, nil
|
||||
}
|
||||
|
||||
// GetPodsWithFilters uses the cliconfig to categorize if the latest pod is required.
|
||||
func GetPodsWithFilters(r *libpod.Runtime, filters string) ([]*libpod.Pod, error) {
|
||||
filterFuncs, err := GenerateFilterFunction(r, strings.Split(filters, ","))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FilterAllPodsWithFilterFunc(r, filterFuncs...)
|
||||
}
|
||||
|
||||
// FilterAllPodsWithFilterFunc retrieves all pods
|
||||
// Filters can be provided which will determine which pods are included in the
|
||||
// output. Multiple filters are handled by ANDing their output, so only pods
|
||||
// matching all filters are returned
|
||||
func FilterAllPodsWithFilterFunc(r *libpod.Runtime, filters ...libpod.PodFilter) ([]*libpod.Pod, error) {
|
||||
pods, err := r.Pods(filters...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
// GenerateFilterFunction basically gets the filters based on the input by the user
|
||||
// and filter the pod list based on the criteria.
|
||||
func GenerateFilterFunction(r *libpod.Runtime, filters []string) ([]libpod.PodFilter, error) {
|
||||
var filterFuncs []libpod.PodFilter
|
||||
for _, f := range filters {
|
||||
filterSplit := strings.Split(f, "=")
|
||||
if len(filterSplit) < 2 {
|
||||
return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
|
||||
}
|
||||
generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid filter")
|
||||
}
|
||||
filterFuncs = append(filterFuncs, generatedFunc)
|
||||
}
|
||||
|
||||
return filterFuncs, nil
|
||||
}
|
||||
func generatePodFilterFuncs(filter, filterValue string) (
|
||||
func(pod *libpod.Pod) bool, error) {
|
||||
switch filter {
|
||||
case "ctr-ids":
|
||||
return func(p *libpod.Pod) bool {
|
||||
ctrIds, err := p.AllContainersByID()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return util.StringInSlice(filterValue, ctrIds)
|
||||
}, nil
|
||||
case "ctr-names":
|
||||
return func(p *libpod.Pod) bool {
|
||||
ctrs, err := p.AllContainers()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, ctr := range ctrs {
|
||||
if filterValue == ctr.Name() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, nil
|
||||
case "ctr-number":
|
||||
return func(p *libpod.Pod) bool {
|
||||
ctrIds, err := p.AllContainersByID()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
fVint, err2 := strconv.Atoi(filterValue)
|
||||
if err2 != nil {
|
||||
return false
|
||||
}
|
||||
return len(ctrIds) == fVint
|
||||
}, 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)
|
||||
}
|
||||
return func(p *libpod.Pod) bool {
|
||||
ctr_statuses, err := p.Status()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, ctr_status := range ctr_statuses {
|
||||
state := ctr_status.String()
|
||||
if ctr_status == define.ContainerStateConfigured {
|
||||
state = "created"
|
||||
}
|
||||
if state == filterValue {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, nil
|
||||
case "id":
|
||||
return func(p *libpod.Pod) bool {
|
||||
return strings.Contains(p.ID(), filterValue)
|
||||
}, nil
|
||||
case "name":
|
||||
return func(p *libpod.Pod) bool {
|
||||
return strings.Contains(p.Name(), filterValue)
|
||||
}, nil
|
||||
case "status":
|
||||
if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
|
||||
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
|
||||
}
|
||||
return nil, errors.Errorf("%s is an invalid filter", filter)
|
||||
}
|
||||
|
||||
var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
|
||||
|
@ -90,18 +90,10 @@ func (r *Runtime) LookupPod(idOrName string) (*Pod, error) {
|
||||
// output. Multiple filters are handled by ANDing their output, so only pods
|
||||
// matching all filters are returned
|
||||
func (r *Runtime) Pods(filters ...PodFilter) ([]*Pod, error) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
if !r.valid {
|
||||
return nil, define.ErrRuntimeStopped
|
||||
}
|
||||
|
||||
pods, err := r.state.AllPods()
|
||||
pods, err := r.GetAllPods()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podsFiltered := make([]*Pod, 0, len(pods))
|
||||
for _, pod := range pods {
|
||||
include := true
|
||||
|
@ -122,19 +122,31 @@ func (r *LocalRuntime) GetLatestPod() (*Pod, error) {
|
||||
return &pod, err
|
||||
}
|
||||
|
||||
// GetPodsWithFilters gets the filtered list of pods based on the filter parameters provided.
|
||||
func (r *LocalRuntime) GetPodsWithFilters(filters string) ([]*Pod, error) {
|
||||
pods, err := shared.GetPodsWithFilters(r.Runtime, filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.podstoAdapterPods(pods)
|
||||
}
|
||||
|
||||
func (r *LocalRuntime) podstoAdapterPods(pod []*libpod.Pod) ([]*Pod, error) {
|
||||
var pods []*Pod
|
||||
for _, i := range pod {
|
||||
|
||||
pods = append(pods, &Pod{i})
|
||||
}
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
// GetAllPods gets all pods and wraps it in an adapter pod
|
||||
func (r *LocalRuntime) GetAllPods() ([]*Pod, error) {
|
||||
var pods []*Pod
|
||||
allPods, err := r.Runtime.GetAllPods()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range allPods {
|
||||
pod := Pod{}
|
||||
pod.Pod = p
|
||||
pods = append(pods, &pod)
|
||||
}
|
||||
return pods, nil
|
||||
return r.podstoAdapterPods(allPods)
|
||||
}
|
||||
|
||||
// LookupPod gets a pod by name or id and wraps it in an adapter pod
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/cmd/podman/varlink"
|
||||
iopodman "github.com/containers/libpod/cmd/podman/varlink"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/varlinkapi"
|
||||
@ -208,6 +208,11 @@ func (r *LocalRuntime) GetAllPods() ([]*Pod, error) {
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
// This is a empty implementation stating remoteclient not yet implemented
|
||||
func (r *LocalRuntime) GetPodsWithFilters(filters string) ([]*Pod, error) {
|
||||
return nil, define.ErrNotImplemented
|
||||
}
|
||||
|
||||
// GetPodsByStatus returns a slice of pods filtered by a libpod status
|
||||
func (r *LocalRuntime) GetPodsByStatus(statuses []string) ([]*Pod, error) {
|
||||
podIDs, err := iopodman.GetPodsByStatus().Call(r.Conn, statuses)
|
||||
|
@ -103,7 +103,6 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func Pods(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
runtime = r.Context().Value("runtime").(*libpod.Runtime)
|
||||
podInspectData []*libpod.PodInspect
|
||||
)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
@ -118,12 +117,8 @@ func Pods(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(query.Filters) > 0 {
|
||||
utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented)
|
||||
return
|
||||
}
|
||||
pods, err := utils.GetPods(w, r)
|
||||
|
||||
pods, err := runtime.GetAllPods()
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
|
||||
return
|
||||
|
45
pkg/api/handlers/utils/pods.go
Normal file
45
pkg/api/handlers/utils/pods.go
Normal file
@ -0,0 +1,45 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/gorilla/schema"
|
||||
)
|
||||
|
||||
func GetPods(w http.ResponseWriter, r *http.Request) ([]*libpod.Pod, error) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
|
||||
query := struct {
|
||||
All bool
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
Digests bool
|
||||
}{}
|
||||
|
||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var filters = []string{}
|
||||
if _, found := r.URL.Query()["digests"]; found && query.Digests {
|
||||
UnSupportedParameter("digests")
|
||||
}
|
||||
|
||||
if len(query.Filters) > 0 {
|
||||
for k, v := range query.Filters {
|
||||
for _, val := range v {
|
||||
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
|
||||
}
|
||||
}
|
||||
filterFuncs, err := shared.GenerateFilterFunction(runtime, filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return shared.FilterAllPodsWithFilterFunc(runtime, filterFuncs...)
|
||||
}
|
||||
|
||||
return runtime.GetAllPods()
|
||||
|
||||
}
|
@ -76,15 +76,66 @@ var _ = Describe("Podman pods", func() {
|
||||
}
|
||||
Expect(StringInSlice(newpod, names)).To(BeTrue())
|
||||
Expect(StringInSlice("newpod2", names)).To(BeTrue())
|
||||
})
|
||||
|
||||
// TODO not working Because: code to list based on filter
|
||||
// "not yet implemented",
|
||||
// Validate list pod with filters
|
||||
//filters := make(map[string][]string)
|
||||
//filters["name"] = []string{newpod}
|
||||
//filteredPods, err := pods.List(bt.conn, filters)
|
||||
//Expect(err).To(BeNil())
|
||||
//Expect(len(filteredPods)).To(BeNumerically("==", 1))
|
||||
// The test validates the list pod endpoint with passing filters as the params.
|
||||
It("List pods with filters", func() {
|
||||
var newpod2 string = "newpod2"
|
||||
bt.Podcreate(&newpod2)
|
||||
_, err = bt.RunTopContainer(nil, &trueFlag, &newpod)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
// Expected err with invalid filter params
|
||||
filters := make(map[string][]string)
|
||||
filters["dummy"] = []string{"dummy"}
|
||||
filteredPods, err := pods.List(bt.conn, filters)
|
||||
Expect(err).ToNot(BeNil())
|
||||
code, _ := bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
|
||||
|
||||
// Expected empty response with invalid filters
|
||||
filters = make(map[string][]string)
|
||||
filters["name"] = []string{"dummy"}
|
||||
filteredPods, err = pods.List(bt.conn, filters)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(filteredPods)).To(BeNumerically("==", 0))
|
||||
|
||||
// Validate list pod with name filter
|
||||
filters = make(map[string][]string)
|
||||
filters["name"] = []string{newpod2}
|
||||
filteredPods, err = pods.List(bt.conn, filters)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(filteredPods)).To(BeNumerically("==", 1))
|
||||
var names []string
|
||||
for _, i := range filteredPods {
|
||||
names = append(names, i.Config.Name)
|
||||
}
|
||||
Expect(StringInSlice("newpod2", names)).To(BeTrue())
|
||||
|
||||
// Validate list pod with id filter
|
||||
filters = make(map[string][]string)
|
||||
response, err := pods.Inspect(bt.conn, newpod)
|
||||
id := response.Config.ID
|
||||
filters["id"] = []string{id}
|
||||
filteredPods, err = pods.List(bt.conn, filters)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(filteredPods)).To(BeNumerically("==", 1))
|
||||
names = names[:0]
|
||||
for _, i := range filteredPods {
|
||||
names = append(names, i.Config.Name)
|
||||
}
|
||||
Expect(StringInSlice("newpod", names)).To(BeTrue())
|
||||
|
||||
// Using multiple filters
|
||||
filters["name"] = []string{newpod}
|
||||
filteredPods, err = pods.List(bt.conn, filters)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(filteredPods)).To(BeNumerically("==", 1))
|
||||
names = names[:0]
|
||||
for _, i := range filteredPods {
|
||||
names = append(names, i.Config.Name)
|
||||
}
|
||||
Expect(StringInSlice("newpod", names)).To(BeTrue())
|
||||
})
|
||||
|
||||
// The test validates if the exists responds
|
||||
|
Reference in New Issue
Block a user