spiegel_podman/cmd/podman/shared/pod.go
Stefano Pogliani df568e4963
Support label filters for podman pod ps.
Update the podman pod ps command to support filtering by labels.
This brings the command in line with the documentation as well as
the functionality by the containers equivalent podman ps.

Signed-off-by: Stefano Pogliani <stefano@spogliani.net>
2020-03-26 23:11:07 +00:00

279 lines
8.2 KiB
Go

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"
)
// TODO GetPodStatus and CreatePodStatusResults should removed once the adapter
// and shared packages are reworked. It has now been duplicated in libpod proper.
// GetPodStatus determines the status of the pod based on the
// statuses of the containers in the pod.
// Returns a string representation of the pod status
func GetPodStatus(pod *libpod.Pod) (string, error) {
ctrStatuses, err := pod.Status()
if err != nil {
return define.PodStateErrored, err
}
return CreatePodStatusResults(ctrStatuses)
}
func CreatePodStatusResults(ctrStatuses map[string]define.ContainerStatus) (string, error) {
ctrNum := len(ctrStatuses)
if ctrNum == 0 {
return define.PodStateCreated, nil
}
statuses := map[string]int{
define.PodStateStopped: 0,
define.PodStateRunning: 0,
define.PodStatePaused: 0,
define.PodStateCreated: 0,
define.PodStateErrored: 0,
}
for _, ctrStatus := range ctrStatuses {
switch ctrStatus {
case define.ContainerStateExited:
fallthrough
case define.ContainerStateStopped:
statuses[define.PodStateStopped]++
case define.ContainerStateRunning:
statuses[define.PodStateRunning]++
case define.ContainerStatePaused:
statuses[define.PodStatePaused]++
case define.ContainerStateCreated, define.ContainerStateConfigured:
statuses[define.PodStateCreated]++
default:
statuses[define.PodStateErrored]++
}
}
switch {
case statuses[define.PodStateRunning] > 0:
return define.PodStateRunning, nil
case statuses[define.PodStatePaused] == ctrNum:
return define.PodStatePaused, nil
case statuses[define.PodStateStopped] == ctrNum:
return define.PodStateExited, nil
case statuses[define.PodStateStopped] > 0:
return define.PodStateStopped, nil
case statuses[define.PodStateErrored] > 0:
return define.PodStateErrored, nil
default:
return define.PodStateCreated, nil
}
}
// GetNamespaceOptions transforms a slice of kernel namespaces
// into a slice of pod create options. Currently, not all
// kernel namespaces are supported, and they will be returned in an error
func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) {
var options []libpod.PodCreateOption
var erroredOptions []libpod.PodCreateOption
for _, toShare := range ns {
switch toShare {
case "cgroup":
options = append(options, libpod.WithPodCgroups())
case "net":
options = append(options, libpod.WithPodNet())
case "mnt":
return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level")
case "pid":
options = append(options, libpod.WithPodPID())
case "user":
return erroredOptions, errors.Errorf("User sharing functionality not supported on pod level")
case "ipc":
options = append(options, libpod.WithPodIPC())
case "uts":
options = append(options, libpod.WithPodUTS())
case "":
case "none":
return erroredOptions, nil
default:
return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: net, pid, ipc, uts or none", toShare)
}
}
return options, nil
}
// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
// The conversion from []string to natBindings is temporary while mheon reworks the port
// deduplication code. Eventually that step will not be required.
_, natBindings, err := nat.ParsePortSpecs(ports)
if err != nil {
return nil, err
}
for containerPb, hostPb := range natBindings {
var pm ocicni.PortMapping
pm.ContainerPort = int32(containerPb.Int())
for _, i := range hostPb {
var hostPort int
var err error
pm.HostIP = i.HostIP
if i.HostPort == "" {
hostPort = containerPb.Int()
} else {
hostPort, err = strconv.Atoi(i.HostPort)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert host port to integer")
}
}
pm.HostPort = int32(hostPort)
pm.Protocol = containerPb.Proto()
portBindings = append(portBindings, pm)
}
}
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.SplitN(f, "=", 2)
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
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) {
return true
}
}
return false
}, nil
}
return nil, errors.Errorf("%s is an invalid filter", filter)
}
var DefaultKernelNamespaces = "cgroup,ipc,net,uts"