mirror of
https://github.com/containers/podman.git
synced 2025-06-19 00:06:43 +08:00
389 lines
11 KiB
Go
389 lines
11 KiB
Go
// +build varlink
|
|
|
|
package varlinkapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/containers/libpod/libpod/define"
|
|
iopodman "github.com/containers/libpod/pkg/varlink"
|
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
|
"github.com/docker/go-connections/nat"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// CreatePod ...
|
|
func (i *VarlinkAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error {
|
|
var options []libpod.PodCreateOption
|
|
if create.Infra {
|
|
options = append(options, libpod.WithInfraContainer())
|
|
nsOptions, err := GetNamespaceOptions(create.Share)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options = append(options, nsOptions...)
|
|
}
|
|
if create.CgroupParent != "" {
|
|
options = append(options, libpod.WithPodCgroupParent(create.CgroupParent))
|
|
}
|
|
if len(create.Labels) > 0 {
|
|
options = append(options, libpod.WithPodLabels(create.Labels))
|
|
}
|
|
if create.Name != "" {
|
|
options = append(options, libpod.WithPodName(create.Name))
|
|
}
|
|
if len(create.Share) > 0 && !create.Infra {
|
|
return call.ReplyErrorOccurred("You cannot share kernel namespaces on the pod level without an infra container")
|
|
}
|
|
if len(create.Share) == 0 && create.Infra {
|
|
return call.ReplyErrorOccurred("You must share kernel namespaces to run an infra container")
|
|
}
|
|
|
|
if len(create.Publish) > 0 {
|
|
if !create.Infra {
|
|
return call.ReplyErrorOccurred("you must have an infra container to publish port bindings to the host")
|
|
}
|
|
portBindings, err := CreatePortBindings(create.Publish)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
options = append(options, libpod.WithInfraContainerPorts(portBindings))
|
|
|
|
}
|
|
options = append(options, libpod.WithPodCgroups())
|
|
|
|
pod, err := i.Runtime.NewPod(getContext(), options...)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
return call.ReplyCreatePod(pod.ID())
|
|
}
|
|
|
|
// ListPods ...
|
|
func (i *VarlinkAPI) ListPods(call iopodman.VarlinkCall) error {
|
|
var (
|
|
listPods []iopodman.ListPodData
|
|
)
|
|
|
|
pods, err := i.Runtime.GetAllPods()
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
opts := PsOptions{}
|
|
for _, pod := range pods {
|
|
listPod, err := makeListPod(pod, opts)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
listPods = append(listPods, listPod)
|
|
}
|
|
return call.ReplyListPods(listPods)
|
|
}
|
|
|
|
// GetPod ...
|
|
func (i *VarlinkAPI) GetPod(call iopodman.VarlinkCall, name string) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
opts := PsOptions{}
|
|
|
|
listPod, err := makeListPod(pod, opts)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
|
|
return call.ReplyGetPod(listPod)
|
|
}
|
|
|
|
// GetPodsByStatus returns a slice of pods filtered by a libpod status
|
|
func (i *VarlinkAPI) GetPodsByStatus(call iopodman.VarlinkCall, statuses []string) error {
|
|
filterFuncs := func(p *libpod.Pod) bool {
|
|
state, _ := p.GetPodStatus()
|
|
for _, status := range statuses {
|
|
if state == status {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
filteredPods, err := i.Runtime.Pods(filterFuncs)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
podIDs := make([]string, 0, len(filteredPods))
|
|
for _, p := range filteredPods {
|
|
podIDs = append(podIDs, p.ID())
|
|
}
|
|
return call.ReplyGetPodsByStatus(podIDs)
|
|
}
|
|
|
|
// InspectPod ...
|
|
func (i *VarlinkAPI) InspectPod(call iopodman.VarlinkCall, name string) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
inspectData, err := pod.Inspect()
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
b, err := json.Marshal(&inspectData)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred("unable to serialize")
|
|
}
|
|
return call.ReplyInspectPod(string(b))
|
|
}
|
|
|
|
// StartPod ...
|
|
func (i *VarlinkAPI) StartPod(call iopodman.VarlinkCall, name string) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
ctnrs, err := pod.AllContainers()
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
if 0 == len(ctnrs) {
|
|
return call.ReplyNoContainersInPod(name)
|
|
}
|
|
ctrErrs, err := pod.Start(getContext())
|
|
callErr := handlePodCall(call, pod, ctrErrs, err)
|
|
if callErr != nil {
|
|
return err
|
|
}
|
|
return call.ReplyStartPod(pod.ID())
|
|
}
|
|
|
|
// StopPod ...
|
|
func (i *VarlinkAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int64) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
ctrErrs, err := pod.StopWithTimeout(getContext(), true, int(timeout))
|
|
callErr := handlePodCall(call, pod, ctrErrs, err)
|
|
if callErr != nil {
|
|
return err
|
|
}
|
|
return call.ReplyStopPod(pod.ID())
|
|
}
|
|
|
|
// RestartPod ...
|
|
func (i *VarlinkAPI) RestartPod(call iopodman.VarlinkCall, name string) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
ctnrs, err := pod.AllContainers()
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
if 0 == len(ctnrs) {
|
|
return call.ReplyNoContainersInPod(name)
|
|
}
|
|
ctrErrs, err := pod.Restart(getContext())
|
|
callErr := handlePodCall(call, pod, ctrErrs, err)
|
|
if callErr != nil {
|
|
return err
|
|
}
|
|
return call.ReplyRestartPod(pod.ID())
|
|
}
|
|
|
|
// KillPod kills the running containers in a pod. If you want to use the default SIGTERM signal,
|
|
// just send a -1 for the signal arg.
|
|
func (i *VarlinkAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64) error {
|
|
killSignal := uint(syscall.SIGTERM)
|
|
if signal != -1 {
|
|
killSignal = uint(signal)
|
|
}
|
|
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
ctrErrs, err := pod.Kill(killSignal)
|
|
callErr := handlePodCall(call, pod, ctrErrs, err)
|
|
if callErr != nil {
|
|
return err
|
|
}
|
|
return call.ReplyKillPod(pod.ID())
|
|
}
|
|
|
|
// PausePod ...
|
|
func (i *VarlinkAPI) PausePod(call iopodman.VarlinkCall, name string) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
ctrErrs, err := pod.Pause()
|
|
callErr := handlePodCall(call, pod, ctrErrs, err)
|
|
if callErr != nil {
|
|
return err
|
|
}
|
|
return call.ReplyPausePod(pod.ID())
|
|
}
|
|
|
|
// UnpausePod ...
|
|
func (i *VarlinkAPI) UnpausePod(call iopodman.VarlinkCall, name string) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
ctrErrs, err := pod.Unpause()
|
|
callErr := handlePodCall(call, pod, ctrErrs, err)
|
|
if callErr != nil {
|
|
return err
|
|
}
|
|
return call.ReplyUnpausePod(pod.ID())
|
|
}
|
|
|
|
// RemovePod ...
|
|
func (i *VarlinkAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool) error {
|
|
ctx := getContext()
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
if err = i.Runtime.RemovePod(ctx, pod, true, force); err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
|
|
return call.ReplyRemovePod(pod.ID())
|
|
}
|
|
|
|
// GetPodStats ...
|
|
func (i *VarlinkAPI) GetPodStats(call iopodman.VarlinkCall, name string) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
prevStats := make(map[string]*define.ContainerStats)
|
|
podStats, err := pod.GetPodStats(prevStats)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
if len(podStats) == 0 {
|
|
return call.ReplyNoContainerRunning()
|
|
}
|
|
containersStats := make([]iopodman.ContainerStats, 0)
|
|
for ctrID, containerStats := range podStats {
|
|
cs := iopodman.ContainerStats{
|
|
Id: ctrID,
|
|
Name: containerStats.Name,
|
|
Cpu: containerStats.CPU,
|
|
Cpu_nano: int64(containerStats.CPUNano),
|
|
System_nano: int64(containerStats.SystemNano),
|
|
Mem_usage: int64(containerStats.MemUsage),
|
|
Mem_limit: int64(containerStats.MemLimit),
|
|
Mem_perc: containerStats.MemPerc,
|
|
Net_input: int64(containerStats.NetInput),
|
|
Net_output: int64(containerStats.NetOutput),
|
|
Block_input: int64(containerStats.BlockInput),
|
|
Block_output: int64(containerStats.BlockOutput),
|
|
Pids: int64(containerStats.PIDs),
|
|
}
|
|
containersStats = append(containersStats, cs)
|
|
}
|
|
return call.ReplyGetPodStats(pod.ID(), containersStats)
|
|
}
|
|
|
|
// getPodsByContext returns a slice of pod ids based on all, latest, or a list
|
|
func (i *VarlinkAPI) GetPodsByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error {
|
|
var podids []string
|
|
|
|
pods, err := getPodsByContext(all, latest, input, i.Runtime)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
for _, p := range pods {
|
|
podids = append(podids, p.ID())
|
|
}
|
|
return call.ReplyGetPodsByContext(podids)
|
|
}
|
|
|
|
// PodStateData returns a container's state data in string format
|
|
func (i *VarlinkAPI) PodStateData(call iopodman.VarlinkCall, name string) error {
|
|
pod, err := i.Runtime.LookupPod(name)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
data, err := pod.Inspect()
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred("unable to obtain pod state")
|
|
}
|
|
b, err := json.Marshal(data)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred("unable to serialize pod inspect data")
|
|
}
|
|
return call.ReplyPodStateData(string(b))
|
|
}
|
|
|
|
// TopPod provides the top stats for a given or latest pod
|
|
func (i *VarlinkAPI) TopPod(call iopodman.VarlinkCall, name string, latest bool, descriptors []string) error {
|
|
var (
|
|
pod *libpod.Pod
|
|
err error
|
|
)
|
|
if latest {
|
|
name = "latest"
|
|
pod, err = i.Runtime.GetLatestPod()
|
|
} else {
|
|
pod, err = i.Runtime.LookupPod(name)
|
|
}
|
|
if err != nil {
|
|
return call.ReplyPodNotFound(name, err.Error())
|
|
}
|
|
|
|
podStatus, err := pod.GetPodStatus()
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(fmt.Sprintf("unable to get status for pod %s", pod.ID()))
|
|
}
|
|
if podStatus != "Running" {
|
|
return call.ReplyErrorOccurred("pod top can only be used on pods with at least one running container")
|
|
}
|
|
reply, err := pod.GetPodPidInformation(descriptors)
|
|
if err != nil {
|
|
return call.ReplyErrorOccurred(err.Error())
|
|
}
|
|
return call.ReplyTopPod(reply)
|
|
}
|
|
|
|
// 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
|
|
}
|