aboutsummaryrefslogtreecommitdiff
path: root/pkg/varlinkapi
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/varlinkapi')
-rw-r--r--pkg/varlinkapi/container.go928
-rw-r--r--pkg/varlinkapi/containers.go24
-rw-r--r--pkg/varlinkapi/containers_create.go5
-rw-r--r--pkg/varlinkapi/create.go1154
-rw-r--r--pkg/varlinkapi/funcs.go121
-rw-r--r--pkg/varlinkapi/generate.go3
-rw-r--r--pkg/varlinkapi/images.go5
-rw-r--r--pkg/varlinkapi/intermediate.go289
-rw-r--r--pkg/varlinkapi/intermediate_varlink.go457
-rw-r--r--pkg/varlinkapi/pods.go56
-rw-r--r--pkg/varlinkapi/shortcuts.go66
-rw-r--r--pkg/varlinkapi/util.go17
-rw-r--r--pkg/varlinkapi/volumes.go47
13 files changed, 3129 insertions, 43 deletions
diff --git a/pkg/varlinkapi/container.go b/pkg/varlinkapi/container.go
new file mode 100644
index 000000000..eae54dfeb
--- /dev/null
+++ b/pkg/varlinkapi/container.go
@@ -0,0 +1,928 @@
+package varlinkapi
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/timetype"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-units"
+ "github.com/google/shlex"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ v1 "k8s.io/api/core/v1"
+)
+
+const (
+ cidTruncLength = 12
+ podTruncLength = 12
+ iidTruncLength = 12
+ cmdTruncLength = 17
+)
+
+// PsOptions describes the struct being formed for ps.
+type PsOptions struct {
+ All bool
+ Format string
+ Last int
+ Latest bool
+ NoTrunc bool
+ Pod bool
+ Quiet bool
+ Size bool
+ Sort string
+ Namespace bool
+ Sync bool
+}
+
+// BatchContainerStruct is the return object from BatchContainer and contains
+// container related information.
+type BatchContainerStruct struct {
+ ConConfig *libpod.ContainerConfig
+ ConState define.ContainerStatus
+ ExitCode int32
+ Exited bool
+ Pid int
+ StartedTime time.Time
+ ExitedTime time.Time
+ Size *ContainerSize
+}
+
+// PsContainerOutput is the struct being returned from a parallel
+// batch operation.
+type PsContainerOutput struct {
+ ID string
+ Image string
+ ImageID string
+ Command string
+ Created string
+ Ports string
+ Names string
+ IsInfra bool
+ Status string
+ State define.ContainerStatus
+ Pid int
+ Size *ContainerSize
+ Pod string
+ PodName string
+ CreatedAt time.Time
+ ExitedAt time.Time
+ StartedAt time.Time
+ Labels map[string]string
+ PID string
+ Cgroup string
+ IPC string
+ MNT string
+ NET string
+ PIDNS string
+ User string
+ UTS string
+ Mounts string
+}
+
+// Namespace describes output for ps namespace.
+type Namespace struct {
+ PID string `json:"pid,omitempty"`
+ Cgroup string `json:"cgroup,omitempty"`
+ IPC string `json:"ipc,omitempty"`
+ MNT string `json:"mnt,omitempty"`
+ NET string `json:"net,omitempty"`
+ PIDNS string `json:"pidns,omitempty"`
+ User string `json:"user,omitempty"`
+ UTS string `json:"uts,omitempty"`
+}
+
+// ContainerSize holds the size of the container's root filesystem and top
+// read-write layer.
+type ContainerSize struct {
+ RootFsSize int64 `json:"rootFsSize"`
+ RwSize int64 `json:"rwSize"`
+}
+
+// NewBatchContainer runs a batch process under one lock to get container information and only
+// be called in PBatch.
+func NewBatchContainer(r *libpod.Runtime, ctr *libpod.Container, opts PsOptions) (PsContainerOutput, error) {
+ var (
+ conState define.ContainerStatus
+ command string
+ created string
+ status string
+ exitedAt time.Time
+ startedAt time.Time
+ exitCode int32
+ err error
+ pid int
+ size *ContainerSize
+ ns *Namespace
+ pso PsContainerOutput
+ )
+ batchErr := ctr.Batch(func(c *libpod.Container) error {
+ if opts.Sync {
+ if err := c.Sync(); err != nil {
+ return err
+ }
+ }
+
+ conState, err = c.State()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container state")
+ }
+ command = strings.Join(c.Command(), " ")
+ created = units.HumanDuration(time.Since(c.CreatedTime())) + " ago"
+
+ exitCode, _, err = c.ExitCode()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container exit code")
+ }
+ startedAt, err = c.StartedTime()
+ if err != nil {
+ logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
+ }
+ exitedAt, err = c.FinishedTime()
+ if err != nil {
+ logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
+ }
+ if opts.Namespace {
+ pid, err = c.PID()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container pid")
+ }
+ ns = GetNamespaces(pid)
+ }
+ if opts.Size {
+ size = new(ContainerSize)
+
+ rootFsSize, err := c.RootFsSize()
+ if err != nil {
+ logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
+ }
+
+ rwSize, err := c.RWSize()
+ if err != nil {
+ logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
+ }
+
+ size.RootFsSize = rootFsSize
+ size.RwSize = rwSize
+ }
+
+ return nil
+ })
+
+ if batchErr != nil {
+ return pso, batchErr
+ }
+
+ switch conState.String() {
+ case define.ContainerStateExited.String():
+ fallthrough
+ case define.ContainerStateStopped.String():
+ exitedSince := units.HumanDuration(time.Since(exitedAt))
+ status = fmt.Sprintf("Exited (%d) %s ago", exitCode, exitedSince)
+ case define.ContainerStateRunning.String():
+ status = "Up " + units.HumanDuration(time.Since(startedAt)) + " ago"
+ case define.ContainerStatePaused.String():
+ status = "Paused"
+ case define.ContainerStateCreated.String(), define.ContainerStateConfigured.String():
+ status = "Created"
+ case define.ContainerStateRemoving.String():
+ status = "Removing"
+ default:
+ status = "Error"
+ }
+
+ imageID, imageName := ctr.Image()
+ cid := ctr.ID()
+ podID := ctr.PodID()
+ if !opts.NoTrunc {
+ cid = cid[0:cidTruncLength]
+ if len(podID) > podTruncLength {
+ podID = podID[0:podTruncLength]
+ }
+ if len(command) > cmdTruncLength {
+ command = command[0:cmdTruncLength] + "..."
+ }
+ if len(imageID) > iidTruncLength {
+ imageID = imageID[0:iidTruncLength]
+ }
+ }
+
+ ports, err := ctr.PortMappings()
+ if err != nil {
+ logrus.Errorf("unable to lookup namespace container for %s", ctr.ID())
+ }
+
+ pso.ID = cid
+ pso.Image = imageName
+ pso.ImageID = imageID
+ pso.Command = command
+ pso.Created = created
+ pso.Ports = portsToString(ports)
+ pso.Names = ctr.Name()
+ pso.IsInfra = ctr.IsInfra()
+ pso.Status = status
+ pso.State = conState
+ pso.Pid = pid
+ pso.Size = size
+ pso.ExitedAt = exitedAt
+ pso.CreatedAt = ctr.CreatedTime()
+ pso.StartedAt = startedAt
+ pso.Labels = ctr.Labels()
+ pso.Mounts = strings.Join(ctr.UserVolumes(), " ")
+
+ // Add pod name and pod ID if requested by user.
+ // No need to look up the pod if its ID is empty.
+ if opts.Pod && len(podID) > 0 {
+ // The pod name is not in the container definition
+ // so we need to retrieve it using the pod ID.
+ var podName string
+ pod, err := r.LookupPod(podID)
+ if err != nil {
+ logrus.Errorf("unable to lookup pod for container %s", ctr.ID())
+ } else {
+ podName = pod.Name()
+ }
+
+ pso.Pod = podID
+ pso.PodName = podName
+ }
+
+ if opts.Namespace {
+ pso.Cgroup = ns.Cgroup
+ pso.IPC = ns.IPC
+ pso.MNT = ns.MNT
+ pso.NET = ns.NET
+ pso.User = ns.User
+ pso.UTS = ns.UTS
+ pso.PIDNS = ns.PIDNS
+ }
+
+ return pso, nil
+}
+
+type batchFunc func() (PsContainerOutput, error)
+
+type workerInput struct {
+ parallelFunc batchFunc
+ opts PsOptions
+ cid string
+ job int
+}
+
+// worker is a "threaded" worker that takes jobs from the channel "queue".
+func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContainerOutput, errors chan<- error) {
+ for j := range jobs {
+ r, err := j.parallelFunc()
+ // If we find an error, we return just the error.
+ if err != nil {
+ errors <- err
+ } else {
+ // Return the result.
+ results <- r
+ }
+ wg.Done()
+ }
+}
+
+// GenerateContainerFilterFuncs return ContainerFilter functions based of filter.
+func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) {
+ switch filter {
+ case "id":
+ return func(c *libpod.Container) bool {
+ return strings.Contains(c.ID(), filterValue)
+ }, nil
+ case "label":
+ var filterArray = strings.SplitN(filterValue, "=", 2)
+ var filterKey = filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ return func(c *libpod.Container) bool {
+ for labelKey, labelValue := range c.Labels() {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "name":
+ return func(c *libpod.Container) bool {
+ match, err := regexp.MatchString(filterValue, c.Name())
+ if err != nil {
+ return false
+ }
+ return match
+ }, nil
+ case "exited":
+ exitCode, err := strconv.ParseInt(filterValue, 10, 32)
+ if err != nil {
+ return nil, errors.Wrapf(err, "exited code out of range %q", filterValue)
+ }
+ return func(c *libpod.Container) bool {
+ ec, exited, err := c.ExitCode()
+ if ec == int32(exitCode) && err == nil && exited {
+ return true
+ }
+ return false
+ }, nil
+ case "status":
+ if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) {
+ return nil, errors.Errorf("%s is not a valid status", filterValue)
+ }
+ return func(c *libpod.Container) bool {
+ status, err := c.State()
+ if err != nil {
+ return false
+ }
+ if filterValue == "stopped" {
+ filterValue = "exited"
+ }
+ state := status.String()
+ if status == define.ContainerStateConfigured {
+ state = "created"
+ } else if status == define.ContainerStateStopped {
+ state = "exited"
+ }
+ return state == filterValue
+ }, nil
+ case "ancestor":
+ // This needs to refine to match docker
+ // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant.
+ return func(c *libpod.Container) bool {
+ containerConfig := c.Config()
+ if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) {
+ return true
+ }
+ return false
+ }, nil
+ case "before":
+ ctr, err := r.LookupContainer(filterValue)
+ if err != nil {
+ return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
+ }
+ containerConfig := ctr.Config()
+ createTime := containerConfig.CreatedTime
+ return func(c *libpod.Container) bool {
+ cc := c.Config()
+ return createTime.After(cc.CreatedTime)
+ }, nil
+ case "since":
+ ctr, err := r.LookupContainer(filterValue)
+ if err != nil {
+ return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
+ }
+ containerConfig := ctr.Config()
+ createTime := containerConfig.CreatedTime
+ return func(c *libpod.Container) bool {
+ cc := c.Config()
+ return createTime.Before(cc.CreatedTime)
+ }, nil
+ case "volume":
+ //- volume=(<volume-name>|<mount-point-destination>)
+ return func(c *libpod.Container) bool {
+ containerConfig := c.Config()
+ var dest string
+ arr := strings.Split(filterValue, ":")
+ source := arr[0]
+ if len(arr) == 2 {
+ dest = arr[1]
+ }
+ for _, mount := range containerConfig.Spec.Mounts {
+ if dest != "" && (mount.Source == source && mount.Destination == dest) {
+ return true
+ }
+ if dest == "" && mount.Source == source {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "health":
+ return func(c *libpod.Container) bool {
+ hcStatus, err := c.HealthCheckStatus()
+ if err != nil {
+ return false
+ }
+ return hcStatus == filterValue
+ }, nil
+ case "until":
+ ts, err := timetype.GetTimestamp(filterValue, time.Now())
+ if err != nil {
+ return nil, err
+ }
+ seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
+ if err != nil {
+ return nil, err
+ }
+ until := time.Unix(seconds, nanoseconds)
+ return func(c *libpod.Container) bool {
+ if !until.IsZero() && c.CreatedTime().After((until)) {
+ return true
+ }
+ return false
+ }, nil
+ }
+ return nil, errors.Errorf("%s is an invalid filter", filter)
+}
+
+// GetPsContainerOutput returns a slice of containers specifically for ps output.
+func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, maxWorkers int) ([]PsContainerOutput, error) {
+ var (
+ filterFuncs []libpod.ContainerFilter
+ outputContainers []*libpod.Container
+ )
+
+ if len(filters) > 0 {
+ 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 := GenerateContainerFilterFuncs(filterSplit[0], filterSplit[1], r)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid filter")
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
+ }
+ }
+ if !opts.Latest {
+ // Get all containers.
+ containers, err := r.GetContainers(filterFuncs...)
+ if err != nil {
+ return nil, err
+ }
+
+ // We only want the last few containers.
+ if opts.Last > 0 && opts.Last <= len(containers) {
+ return nil, errors.Errorf("--last not yet supported")
+ } else {
+ outputContainers = containers
+ }
+ } else {
+ // Get just the latest container.
+ // Ignore filters.
+ latestCtr, err := r.GetLatestContainer()
+ if err != nil {
+ return nil, err
+ }
+
+ outputContainers = []*libpod.Container{latestCtr}
+ }
+
+ pss := PBatch(r, outputContainers, maxWorkers, opts)
+ return pss, nil
+}
+
+// PBatch performs batch operations on a container in parallel. It spawns the
+// number of workers relative to the number of parallel operations desired.
+func PBatch(r *libpod.Runtime, containers []*libpod.Container, workers int, opts PsOptions) []PsContainerOutput {
+ var wg sync.WaitGroup
+ psResults := []PsContainerOutput{}
+
+ // If the number of containers in question is less than the number of
+ // proposed parallel operations, we shouldn't spawn so many workers.
+ if workers > len(containers) {
+ workers = len(containers)
+ }
+
+ jobs := make(chan workerInput, len(containers))
+ results := make(chan PsContainerOutput, len(containers))
+ batchErrors := make(chan error, len(containers))
+
+ // Create the workers.
+ for w := 1; w <= workers; w++ {
+ go worker(&wg, jobs, results, batchErrors)
+ }
+
+ // Add jobs to the workers.
+ for i, j := range containers {
+ j := j
+ wg.Add(1)
+ f := func() (PsContainerOutput, error) {
+ return NewBatchContainer(r, j, opts)
+ }
+ jobs <- workerInput{
+ parallelFunc: f,
+ opts: opts,
+ cid: j.ID(),
+ job: i,
+ }
+ }
+ close(jobs)
+ wg.Wait()
+ close(results)
+ close(batchErrors)
+ for err := range batchErrors {
+ logrus.Errorf("unable to get container info: %q", err)
+ }
+ for res := range results {
+ // We sort out running vs non-running here to save lots of copying
+ // later.
+ if !opts.All && !opts.Latest && opts.Last < 1 {
+ if !res.IsInfra && res.State == define.ContainerStateRunning {
+ psResults = append(psResults, res)
+ }
+ } else {
+ psResults = append(psResults, res)
+ }
+ }
+ return psResults
+}
+
+// BatchContainerOp is used in ps to reduce performance hits by "batching"
+// locks.
+func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) {
+ var (
+ conConfig *libpod.ContainerConfig
+ conState define.ContainerStatus
+ err error
+ exitCode int32
+ exited bool
+ pid int
+ size *ContainerSize
+ startedTime time.Time
+ exitedTime time.Time
+ )
+
+ batchErr := ctr.Batch(func(c *libpod.Container) error {
+ conConfig = c.Config()
+ conState, err = c.State()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container state")
+ }
+
+ exitCode, exited, err = c.ExitCode()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container exit code")
+ }
+ startedTime, err = c.StartedTime()
+ if err != nil {
+ logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
+ }
+ exitedTime, err = c.FinishedTime()
+ if err != nil {
+ logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
+ }
+
+ if !opts.Size && !opts.Namespace {
+ return nil
+ }
+
+ if opts.Namespace {
+ pid, err = c.PID()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container pid")
+ }
+ }
+ if opts.Size {
+ size = new(ContainerSize)
+
+ rootFsSize, err := c.RootFsSize()
+ if err != nil {
+ logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
+ }
+
+ rwSize, err := c.RWSize()
+ if err != nil {
+ logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
+ }
+
+ size.RootFsSize = rootFsSize
+ size.RwSize = rwSize
+ }
+ return nil
+ })
+ if batchErr != nil {
+ return BatchContainerStruct{}, batchErr
+ }
+ return BatchContainerStruct{
+ ConConfig: conConfig,
+ ConState: conState,
+ ExitCode: exitCode,
+ Exited: exited,
+ Pid: pid,
+ StartedTime: startedTime,
+ ExitedTime: exitedTime,
+ Size: size,
+ }, nil
+}
+
+// GetNamespaces returns a populated namespace struct.
+func GetNamespaces(pid int) *Namespace {
+ ctrPID := strconv.Itoa(pid)
+ cgroup, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
+ ipc, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
+ mnt, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
+ net, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
+ pidns, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
+ user, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
+ uts, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
+
+ return &Namespace{
+ PID: ctrPID,
+ Cgroup: cgroup,
+ IPC: ipc,
+ MNT: mnt,
+ NET: net,
+ PIDNS: pidns,
+ User: user,
+ UTS: uts,
+ }
+}
+
+// GetNamespaceInfo is an exported wrapper for getNamespaceInfo
+func GetNamespaceInfo(path string) (string, error) {
+ return getNamespaceInfo(path)
+}
+
+func getNamespaceInfo(path string) (string, error) {
+ val, err := os.Readlink(path)
+ if err != nil {
+ return "", errors.Wrapf(err, "error getting info from %q", path)
+ }
+ return getStrFromSquareBrackets(val), nil
+}
+
+// getStrFromSquareBrackets gets the string inside [] from a string.
+func getStrFromSquareBrackets(cmd string) string {
+ reg := regexp.MustCompile(`.*\[|\].*`)
+ arr := strings.Split(reg.ReplaceAllLiteralString(cmd, ""), ",")
+ return strings.Join(arr, ",")
+}
+
+func comparePorts(i, j ocicni.PortMapping) bool {
+ if i.ContainerPort != j.ContainerPort {
+ return i.ContainerPort < j.ContainerPort
+ }
+
+ if i.HostIP != j.HostIP {
+ return i.HostIP < j.HostIP
+ }
+
+ if i.HostPort != j.HostPort {
+ return i.HostPort < j.HostPort
+ }
+
+ return i.Protocol < j.Protocol
+}
+
+// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto>
+// e.g 0.0.0.0:1000-1006->1000-1006/tcp.
+func formatGroup(key string, start, last int32) string {
+ parts := strings.Split(key, "/")
+ groupType := parts[0]
+ var ip string
+ if len(parts) > 1 {
+ ip = parts[0]
+ groupType = parts[1]
+ }
+ group := strconv.Itoa(int(start))
+ if start != last {
+ group = fmt.Sprintf("%s-%d", group, last)
+ }
+ if ip != "" {
+ group = fmt.Sprintf("%s:%s->%s", ip, group, group)
+ }
+ return fmt.Sprintf("%s/%s", group, groupType)
+}
+
+// portsToString converts the ports used to a string of the from "port1, port2"
+// and also groups a continuous list of ports into a readable format.
+func portsToString(ports []ocicni.PortMapping) string {
+ type portGroup struct {
+ first int32
+ last int32
+ }
+ var portDisplay []string
+ if len(ports) == 0 {
+ return ""
+ }
+ //Sort the ports, so grouping continuous ports become easy.
+ sort.Slice(ports, func(i, j int) bool {
+ return comparePorts(ports[i], ports[j])
+ })
+
+ // portGroupMap is used for grouping continuous ports.
+ portGroupMap := make(map[string]*portGroup)
+ var groupKeyList []string
+
+ for _, v := range ports {
+
+ hostIP := v.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ // If hostPort and containerPort are not same, consider as individual port.
+ if v.ContainerPort != v.HostPort {
+ portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
+ continue
+ }
+
+ portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol)
+
+ portgroup, ok := portGroupMap[portMapKey]
+ if !ok {
+ portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort}
+ // This list is required to traverse portGroupMap.
+ groupKeyList = append(groupKeyList, portMapKey)
+ continue
+ }
+
+ if portgroup.last == (v.ContainerPort - 1) {
+ portgroup.last = v.ContainerPort
+ continue
+ }
+ }
+ // For each portMapKey, format group list and appned to output string.
+ for _, portKey := range groupKeyList {
+ group := portGroupMap[portKey]
+ portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last))
+ }
+ return strings.Join(portDisplay, ", ")
+}
+
+// GetRunlabel is a helper function for runlabel; it gets the image if needed and begins the
+// construction of the runlabel output and environment variables.
+func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtime *libpod.Runtime, pull bool, inputCreds string, dockerRegistryOptions image.DockerRegistryOptions, authfile string, signaturePolicyPath string, output io.Writer) (string, string, error) {
+ var (
+ newImage *image.Image
+ err error
+ imageName string
+ )
+ if pull {
+ var registryCreds *types.DockerAuthConfig
+ if inputCreds != "" {
+ creds, err := util.ParseRegistryCreds(inputCreds)
+ if err != nil {
+ return "", "", err
+ }
+ registryCreds = creds
+ }
+ dockerRegistryOptions.DockerRegistryCreds = registryCreds
+ newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, signaturePolicyPath, authfile, output, &dockerRegistryOptions, image.SigningOptions{}, &label, util.PullImageMissing)
+ } else {
+ newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage)
+ }
+ if err != nil {
+ return "", "", errors.Wrapf(err, "unable to find image")
+ }
+
+ if len(newImage.Names()) < 1 {
+ imageName = newImage.ID()
+ } else {
+ imageName = newImage.Names()[0]
+ }
+
+ runLabel, err := newImage.GetLabel(ctx, label)
+ return runLabel, imageName, err
+}
+
+// GenerateRunlabelCommand generates the command that will eventually be execucted by Podman.
+func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]string, extraArgs []string, globalOpts string) ([]string, []string, error) {
+ // If no name is provided, we use the image's basename instead.
+ if name == "" {
+ baseName, err := image.GetImageBaseName(imageName)
+ if err != nil {
+ return nil, nil, err
+ }
+ name = baseName
+ }
+ // The user provided extra arguments that need to be tacked onto the label's command.
+ if len(extraArgs) > 0 {
+ runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(extraArgs, " "))
+ }
+ cmd, err := GenerateCommand(runLabel, imageName, name, globalOpts)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "unable to generate command")
+ }
+ env := GenerateRunEnvironment(name, imageName, opts)
+ env = append(env, "PODMAN_RUNLABEL_NESTED=1")
+
+ envmap := envSliceToMap(env)
+
+ envmapper := func(k string) string {
+ switch k {
+ case "OPT1":
+ return envmap["OPT1"]
+ case "OPT2":
+ return envmap["OPT2"]
+ case "OPT3":
+ return envmap["OPT3"]
+ case "PWD":
+ // I would prefer to use os.getenv but it appears PWD is not in the os env list.
+ d, err := os.Getwd()
+ if err != nil {
+ logrus.Error("unable to determine current working directory")
+ return ""
+ }
+ return d
+ }
+ return ""
+ }
+ newS := os.Expand(strings.Join(cmd, " "), envmapper)
+ cmd, err = shlex.Split(newS)
+ if err != nil {
+ return nil, nil, err
+ }
+ return cmd, env, nil
+}
+
+func envSliceToMap(env []string) map[string]string {
+ m := make(map[string]string)
+ for _, i := range env {
+ split := strings.Split(i, "=")
+ m[split[0]] = strings.Join(split[1:], " ")
+ }
+ return m
+}
+
+// GenerateKube generates kubernetes yaml based on a pod or container.
+func GenerateKube(name string, service bool, r *libpod.Runtime) (*v1.Pod, *v1.Service, error) {
+ var (
+ pod *libpod.Pod
+ podYAML *v1.Pod
+ err error
+ container *libpod.Container
+ servicePorts []v1.ServicePort
+ serviceYAML v1.Service
+ )
+ // Get the container in question.
+ container, err = r.LookupContainer(name)
+ if err != nil {
+ pod, err = r.LookupPod(name)
+ if err != nil {
+ return nil, nil, err
+ }
+ podYAML, servicePorts, err = pod.GenerateForKube()
+ } else {
+ if len(container.Dependencies()) > 0 {
+ return nil, nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies")
+ }
+ podYAML, err = container.GenerateForKube()
+ }
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if service {
+ serviceYAML = libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts)
+ }
+ return podYAML, &serviceYAML, nil
+}
+
+// Parallelize provides the maximum number of parallel workers (int) as calculated by a basic
+// heuristic. This can be overridden by the --max-workers primary switch to podman.
+func Parallelize(job string) int {
+ numCpus := runtime.NumCPU()
+ switch job {
+ case "kill":
+ if numCpus <= 3 {
+ return numCpus * 3
+ }
+ return numCpus * 4
+ case "pause":
+ if numCpus <= 3 {
+ return numCpus * 3
+ }
+ return numCpus * 4
+ case "ps":
+ return 8
+ case "restart":
+ return numCpus * 2
+ case "rm":
+ if numCpus <= 3 {
+ return numCpus * 3
+ } else {
+ return numCpus * 4
+ }
+ case "stop":
+ if numCpus <= 2 {
+ return 4
+ } else {
+ return numCpus * 3
+ }
+ case "unpause":
+ if numCpus <= 3 {
+ return numCpus * 3
+ }
+ return numCpus * 4
+ }
+ return 3
+}
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 66b3e4095..8fba07c18 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -14,11 +14,9 @@ import (
"syscall"
"time"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
- "github.com/containers/libpod/pkg/adapter/shortcuts"
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/rootless"
iopodman "github.com/containers/libpod/pkg/varlink"
@@ -39,12 +37,12 @@ func (i *VarlinkAPI) ListContainers(call iopodman.VarlinkCall) error {
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
- opts := shared.PsOptions{
+ opts := PsOptions{
Namespace: true,
Size: true,
}
for _, ctr := range containers {
- batchInfo, err := shared.BatchContainerOp(ctr, opts)
+ batchInfo, err := BatchContainerOp(ctr, opts)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -58,13 +56,13 @@ func (i *VarlinkAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error {
var (
containers []iopodman.PsContainer
)
- maxWorkers := shared.Parallelize("ps")
+ maxWorkers := Parallelize("ps")
psOpts := makePsOpts(opts)
filters := []string{}
if opts.Filters != nil {
filters = *opts.Filters
}
- psContainerOutputs, err := shared.GetPsContainerOutput(i.Runtime, psOpts, filters, maxWorkers)
+ psContainerOutputs, err := GetPsContainerOutput(i.Runtime, psOpts, filters, maxWorkers)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -111,22 +109,22 @@ func (i *VarlinkAPI) GetContainer(call iopodman.VarlinkCall, id string) error {
if err != nil {
return call.ReplyContainerNotFound(id, err.Error())
}
- opts := shared.PsOptions{
+ opts := PsOptions{
Namespace: true,
Size: true,
}
- batchInfo, err := shared.BatchContainerOp(ctr, opts)
+ batchInfo, err := BatchContainerOp(ctr, opts)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo))
}
-// GetContainersByContext returns a slice of container ids based on all, latest, or a list
+// getContainersByContext returns a slice of container ids based on all, latest, or a list
func (i *VarlinkAPI) GetContainersByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error {
var ids []string
- ctrs, err := shortcuts.GetContainersByContext(all, latest, input, i.Runtime)
+ ctrs, err := getContainersByContext(all, latest, input, i.Runtime)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
return call.ReplyContainerNotFound("", err.Error())
@@ -160,9 +158,9 @@ func (i *VarlinkAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses [
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
- opts := shared.PsOptions{Size: true, Namespace: true}
+ opts := PsOptions{Size: true, Namespace: true}
for _, ctr := range filteredContainers {
- batchInfo, err := shared.BatchContainerOp(ctr, opts)
+ batchInfo, err := BatchContainerOp(ctr, opts)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -752,7 +750,7 @@ func (i *VarlinkAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string
tailLen = 0
}
logChannel := make(chan *logs.LogLine, tailLen*len(names)+1)
- containers, err := shortcuts.GetContainersByContext(false, latest, names, i.Runtime)
+ containers, err := getContainersByContext(false, latest, names, i.Runtime)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go
index c1c1f6674..f0a87491a 100644
--- a/pkg/varlinkapi/containers_create.go
+++ b/pkg/varlinkapi/containers_create.go
@@ -3,14 +3,13 @@
package varlinkapi
import (
- "github.com/containers/libpod/cmd/podman/shared"
iopodman "github.com/containers/libpod/pkg/varlink"
)
// CreateContainer ...
func (i *VarlinkAPI) CreateContainer(call iopodman.VarlinkCall, config iopodman.Create) error {
- generic := shared.VarlinkCreateToGeneric(config)
- ctr, _, err := shared.CreateContainer(getContext(), &generic, i.Runtime)
+ generic := VarlinkCreateToGeneric(config)
+ ctr, _, err := CreateContainer(getContext(), &generic, i.Runtime)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
diff --git a/pkg/varlinkapi/create.go b/pkg/varlinkapi/create.go
new file mode 100644
index 000000000..63d5072c6
--- /dev/null
+++ b/pkg/varlinkapi/create.go
@@ -0,0 +1,1154 @@
+package varlinkapi
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ goruntime "runtime"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/libpod/cmd/podman/parse"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/libpod/image"
+ ann "github.com/containers/libpod/pkg/annotations"
+ "github.com/containers/libpod/pkg/autoupdate"
+ "github.com/containers/libpod/pkg/cgroups"
+ envLib "github.com/containers/libpod/pkg/env"
+ "github.com/containers/libpod/pkg/errorhandling"
+ "github.com/containers/libpod/pkg/inspect"
+ ns "github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/seccomp"
+ cc "github.com/containers/libpod/pkg/spec"
+ "github.com/containers/libpod/pkg/sysinfo"
+ systemdGen "github.com/containers/libpod/pkg/systemd/generate"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/docker/go-connections/nat"
+ "github.com/docker/go-units"
+ "github.com/opentracing/opentracing-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
+
+func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) {
+ var (
+ healthCheck *manifest.Schema2HealthConfig
+ err error
+ cidFile *os.File
+ )
+ if c.Bool("trace") {
+ span, _ := opentracing.StartSpanFromContext(ctx, "createContainer")
+ defer span.Finish()
+ }
+ if c.Bool("rm") && c.String("restart") != "" && c.String("restart") != "no" {
+ return nil, nil, errors.Errorf("the --rm option conflicts with --restart")
+ }
+
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ return nil, nil, err
+ }
+ rootfs := ""
+ if c.Bool("rootfs") {
+ rootfs = c.InputArgs[0]
+ }
+
+ if c.IsSet("cidfile") {
+ cidFile, err = util.OpenExclusiveFile(c.String("cidfile"))
+ if err != nil && os.IsExist(err) {
+ return nil, nil, errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", c.String("cidfile"))
+ }
+ if err != nil {
+ return nil, nil, errors.Errorf("error opening cidfile %s", c.String("cidfile"))
+ }
+ defer errorhandling.CloseQuiet(cidFile)
+ defer errorhandling.SyncQuiet(cidFile)
+ }
+
+ imageName := ""
+ rawImageName := ""
+ var imageData *inspect.ImageData = nil
+
+ // Set the storage if there is no rootfs specified
+ if rootfs == "" {
+ var writer io.Writer
+ if !c.Bool("quiet") {
+ writer = os.Stderr
+ }
+
+ if len(c.InputArgs) != 0 {
+ rawImageName = c.InputArgs[0]
+ } else {
+ return nil, nil, errors.Errorf("error, image name not provided")
+ }
+
+ pullType, err := util.ValidatePullType(c.String("pull"))
+ if err != nil {
+ return nil, nil, err
+ }
+
+ overrideOS := c.String("override-os")
+ overrideArch := c.String("override-arch")
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ OSChoice: overrideOS,
+ ArchitectureChoice: overrideArch,
+ }
+
+ newImage, err := runtime.ImageRuntime().New(ctx, rawImageName, rtc.Engine.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType)
+ if err != nil {
+ return nil, nil, err
+ }
+ imageData, err = newImage.InspectNoSize(ctx)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if overrideOS == "" && imageData.Os != goruntime.GOOS {
+ logrus.Infof("Using %q (OS) image on %q host", imageData.Os, goruntime.GOOS)
+ }
+
+ if overrideArch == "" && imageData.Architecture != goruntime.GOARCH {
+ logrus.Infof("Using %q (architecture) on %q host", imageData.Architecture, goruntime.GOARCH)
+ }
+
+ names := newImage.Names()
+ if len(names) > 0 {
+ imageName = names[0]
+ } else {
+ imageName = newImage.ID()
+ }
+
+ // if the user disabled the healthcheck with "none" or the no-healthcheck
+ // options is provided, we skip adding it
+ healthCheckCommandInput := c.String("healthcheck-command")
+
+ // the user didn't disable the healthcheck but did pass in a healthcheck command
+ // now we need to make a healthcheck from the commandline input
+ if healthCheckCommandInput != "none" && !c.Bool("no-healthcheck") {
+ if len(healthCheckCommandInput) > 0 {
+ healthCheck, err = makeHealthCheckFromCli(c)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "unable to create healthcheck")
+ }
+ } else {
+ // the user did not disable the health check and did not pass in a healthcheck
+ // command as input. so now we add healthcheck if it exists AND is correct mediatype
+ _, mediaType, err := newImage.Manifest(ctx)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "unable to determine mediatype of image %s", newImage.ID())
+ }
+ if mediaType == manifest.DockerV2Schema2MediaType {
+ healthCheck, err = newImage.GetHealthCheck(ctx)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "unable to get healthcheck for %s", c.InputArgs[0])
+ }
+
+ if healthCheck != nil {
+ hcCommand := healthCheck.Test
+ if len(hcCommand) < 1 || hcCommand[0] == "" || hcCommand[0] == "NONE" {
+ // disable health check
+ healthCheck = nil
+ } else {
+ // apply defaults if image doesn't override them
+ if healthCheck.Interval == 0 {
+ healthCheck.Interval = 30 * time.Second
+ }
+ if healthCheck.Timeout == 0 {
+ healthCheck.Timeout = 30 * time.Second
+ }
+ /* Docker default is 0s, so the following would be a no-op
+ if healthCheck.StartPeriod == 0 {
+ healthCheck.StartPeriod = 0 * time.Second
+ }
+ */
+ if healthCheck.Retries == 0 {
+ healthCheck.Retries = 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, rawImageName, imageData)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // (VR): Ideally we perform the checks _before_ pulling the image but that
+ // would require some bigger code refactoring of `ParseCreateOpts` and the
+ // logic here. But as the creation code will be consolidated in the future
+ // and given auto updates are experimental, we can live with that for now.
+ // In the end, the user may only need to correct the policy or the raw image
+ // name.
+ autoUpdatePolicy, autoUpdatePolicySpecified := createConfig.Labels[autoupdate.Label]
+ if autoUpdatePolicySpecified {
+ if _, err := autoupdate.LookupPolicy(autoUpdatePolicy); err != nil {
+ return nil, nil, err
+ }
+ // Now we need to make sure we're having a fully-qualified image reference.
+ if rootfs != "" {
+ return nil, nil, errors.Errorf("auto updates do not work with --rootfs")
+ }
+ // Make sure the input image is a docker.
+ if err := autoupdate.ValidateImageReference(rawImageName); err != nil {
+ return nil, nil, err
+ }
+ }
+
+ // Because parseCreateOpts does derive anything from the image, we add health check
+ // at this point. The rest is done by WithOptions.
+ createConfig.HealthCheck = healthCheck
+
+ // TODO: Should be able to return this from ParseCreateOpts
+ var pod *libpod.Pod
+ if createConfig.Pod != "" {
+ pod, err = runtime.LookupPod(createConfig.Pod)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "error looking up pod to join")
+ }
+ }
+
+ ctr, err := CreateContainerFromCreateConfig(runtime, createConfig, ctx, pod)
+ if err != nil {
+ return nil, nil, err
+ }
+ if cidFile != nil {
+ _, err = cidFile.WriteString(ctr.ID())
+ if err != nil {
+ logrus.Error(err)
+ }
+
+ }
+
+ logrus.Debugf("New container created %q", ctr.ID())
+ return ctr, createConfig, nil
+}
+
+func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string {
+ entrypoint := []string{}
+ if c.IsSet("entrypoint") {
+ // Force entrypoint to ""
+ if c.String("entrypoint") == "" {
+ return entrypoint
+ }
+ // Check if entrypoint specified is json
+ if err := json.Unmarshal([]byte(c.String("entrypoint")), &entrypoint); err == nil {
+ return entrypoint
+ }
+ // Return entrypoint as a single command
+ return []string{c.String("entrypoint")}
+ }
+ if data != nil {
+ return data.Config.Entrypoint
+ }
+ return entrypoint
+}
+
+func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, string, error) {
+ pod, err := runtime.LookupPod(podName)
+ if err != nil {
+ return namespaces, "", err
+ }
+ podInfraID, err := pod.InfraContainerID()
+ if err != nil {
+ return namespaces, "", err
+ }
+ hasUserns := false
+ if podInfraID != "" {
+ podCtr, err := runtime.GetContainer(podInfraID)
+ if err != nil {
+ return namespaces, "", err
+ }
+ mappings, err := podCtr.IDMappings()
+ if err != nil {
+ return namespaces, "", err
+ }
+ hasUserns = len(mappings.UIDMap) > 0
+ }
+
+ if (namespaces["pid"] == cc.Pod) || (!c.IsSet("pid") && pod.SharesPID()) {
+ namespaces["pid"] = fmt.Sprintf("container:%s", podInfraID)
+ }
+ if (namespaces["net"] == cc.Pod) || (!c.IsSet("net") && !c.IsSet("network") && pod.SharesNet()) {
+ namespaces["net"] = fmt.Sprintf("container:%s", podInfraID)
+ }
+ if hasUserns && (namespaces["user"] == cc.Pod) || (!c.IsSet("user") && pod.SharesUser()) {
+ namespaces["user"] = fmt.Sprintf("container:%s", podInfraID)
+ }
+ if (namespaces["ipc"] == cc.Pod) || (!c.IsSet("ipc") && pod.SharesIPC()) {
+ namespaces["ipc"] = fmt.Sprintf("container:%s", podInfraID)
+ }
+ if (namespaces["uts"] == cc.Pod) || (!c.IsSet("uts") && pod.SharesUTS()) {
+ namespaces["uts"] = fmt.Sprintf("container:%s", podInfraID)
+ }
+ return namespaces, podInfraID, nil
+}
+
+// Parses CLI options related to container creation into a config which can be
+// parsed into an OCI runtime spec
+func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, rawImageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
+ var (
+ inputCommand, command []string
+ memoryLimit, memoryReservation, memorySwap, memoryKernel int64
+ blkioWeight uint16
+ namespaces map[string]string
+ )
+
+ idmappings, err := util.ParseIDMapping(ns.UsernsMode(c.String("userns")), c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
+ if err != nil {
+ return nil, err
+ }
+
+ imageID := ""
+
+ inputCommand = c.InputArgs[1:]
+ if data != nil {
+ imageID = data.ID
+ }
+
+ rootfs := ""
+ if c.Bool("rootfs") {
+ rootfs = c.InputArgs[0]
+ }
+
+ if c.String("memory") != "" {
+ memoryLimit, err = units.RAMInBytes(c.String("memory"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid value for memory")
+ }
+ }
+ if c.String("memory-reservation") != "" {
+ memoryReservation, err = units.RAMInBytes(c.String("memory-reservation"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid value for memory-reservation")
+ }
+ }
+ if c.String("memory-swap") != "" {
+ if c.String("memory-swap") == "-1" {
+ memorySwap = -1
+ } else {
+ memorySwap, err = units.RAMInBytes(c.String("memory-swap"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid value for memory-swap")
+ }
+ }
+ }
+ if c.String("kernel-memory") != "" {
+ memoryKernel, err = units.RAMInBytes(c.String("kernel-memory"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid value for kernel-memory")
+ }
+ }
+ if c.String("blkio-weight") != "" {
+ u, err := strconv.ParseUint(c.String("blkio-weight"), 10, 16)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid value for blkio-weight")
+ }
+ blkioWeight = uint16(u)
+ }
+
+ tty := c.Bool("tty")
+
+ if c.Changed("cpu-period") && c.Changed("cpus") {
+ return nil, errors.Errorf("--cpu-period and --cpus cannot be set together")
+ }
+ if c.Changed("cpu-quota") && c.Changed("cpus") {
+ return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together")
+ }
+
+ if c.Bool("no-hosts") && c.Changed("add-host") {
+ return nil, errors.Errorf("--no-hosts and --add-host cannot be set together")
+ }
+
+ // EXPOSED PORTS
+ var portBindings map[nat.Port][]nat.PortBinding
+ if data != nil {
+ portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.Config.ExposedPorts)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Kernel Namespaces
+ // TODO Fix handling of namespace from pod
+ // Instead of integrating here, should be done in libpod
+ // However, that also involves setting up security opts
+ // when the pod's namespace is integrated
+ namespaces = map[string]string{
+ "cgroup": c.String("cgroupns"),
+ "pid": c.String("pid"),
+ "net": c.String("network"),
+ "ipc": c.String("ipc"),
+ "user": c.String("userns"),
+ "uts": c.String("uts"),
+ }
+
+ originalPodName := c.String("pod")
+ podName := strings.Replace(originalPodName, "new:", "", 1)
+ // after we strip out :new, make sure there is something left for a pod name
+ if len(podName) < 1 && c.IsSet("pod") {
+ return nil, errors.Errorf("new pod name must be at least one character")
+ }
+
+ // If we are adding a container to a pod, we would like to add an annotation for the infra ID
+ // so kata containers can share VMs inside the pod
+ var podInfraID string
+ if c.IsSet("pod") {
+ if strings.HasPrefix(originalPodName, "new:") {
+ // pod does not exist; lets make it
+ var podOptions []libpod.PodCreateOption
+ podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups())
+ if len(portBindings) > 0 {
+ ociPortBindings, err := cc.NatToOCIPortBindings(portBindings)
+ if err != nil {
+ return nil, err
+ }
+ podOptions = append(podOptions, libpod.WithInfraContainerPorts(ociPortBindings))
+ }
+
+ podNsOptions, err := GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ","))
+ if err != nil {
+ return nil, err
+ }
+ podOptions = append(podOptions, podNsOptions...)
+ // make pod
+ pod, err := runtime.NewPod(ctx, podOptions...)
+ if err != nil {
+ return nil, err
+ }
+ logrus.Debugf("pod %s created by new container request", pod.ID())
+
+ // The container now cannot have port bindings; so we reset the map
+ portBindings = make(map[nat.Port][]nat.PortBinding)
+ }
+ namespaces, podInfraID, err = configurePod(c, runtime, namespaces, podName)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ pidMode := ns.PidMode(namespaces["pid"])
+ if !cc.Valid(string(pidMode), pidMode) {
+ return nil, errors.Errorf("--pid %q is not valid", c.String("pid"))
+ }
+
+ usernsMode := ns.UsernsMode(namespaces["user"])
+ if !cc.Valid(string(usernsMode), usernsMode) {
+ return nil, errors.Errorf("--userns %q is not valid", namespaces["user"])
+ }
+
+ utsMode := ns.UTSMode(namespaces["uts"])
+ if !cc.Valid(string(utsMode), utsMode) {
+ return nil, errors.Errorf("--uts %q is not valid", namespaces["uts"])
+ }
+
+ cgroupMode := ns.CgroupMode(namespaces["cgroup"])
+ if !cgroupMode.Valid() {
+ return nil, errors.Errorf("--cgroup %q is not valid", namespaces["cgroup"])
+ }
+
+ ipcMode := ns.IpcMode(namespaces["ipc"])
+ if !cc.Valid(string(ipcMode), ipcMode) {
+ return nil, errors.Errorf("--ipc %q is not valid", ipcMode)
+ }
+
+ // Make sure if network is set to container namespace, port binding is not also being asked for
+ netMode := ns.NetworkMode(namespaces["net"])
+ if netMode.IsContainer() {
+ if len(portBindings) > 0 {
+ return nil, errors.Errorf("cannot set port bindings on an existing container network namespace")
+ }
+ }
+
+ // USER
+ user := c.String("user")
+ if user == "" {
+ switch {
+ case usernsMode.IsKeepID():
+ user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
+ case data == nil:
+ user = "0"
+ default:
+ user = data.Config.User
+ }
+ }
+
+ // STOP SIGNAL
+ stopSignal := syscall.SIGTERM
+ signalString := ""
+ if data != nil {
+ signalString = data.Config.StopSignal
+ }
+ if c.IsSet("stop-signal") {
+ signalString = c.String("stop-signal")
+ }
+ if signalString != "" {
+ stopSignal, err = util.ParseSignal(signalString)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // ENVIRONMENT VARIABLES
+ //
+ // Precedence order (higher index wins):
+ // 1) env-host, 2) image data, 3) env-file, 4) env
+ env := map[string]string{
+ "container": "podman",
+ }
+
+ // First transform the os env into a map. We need it for the labels later in
+ // any case.
+ osEnv, err := envLib.ParseSlice(os.Environ())
+ if err != nil {
+ return nil, errors.Wrap(err, "error parsing host environment variables")
+ }
+
+ // Start with env-host
+
+ if c.Bool("env-host") {
+ env = envLib.Join(env, osEnv)
+ }
+
+ // Image data overrides any previous variables
+ if data != nil {
+ configEnv, err := envLib.ParseSlice(data.Config.Env)
+ if err != nil {
+ return nil, errors.Wrap(err, "error passing image environment variables")
+ }
+ env = envLib.Join(env, configEnv)
+ }
+
+ // env-file overrides any previous variables
+ if c.IsSet("env-file") {
+ for _, f := range c.StringSlice("env-file") {
+ fileEnv, err := envLib.ParseFile(f)
+ if err != nil {
+ return nil, err
+ }
+ // File env is overridden by env.
+ env = envLib.Join(env, fileEnv)
+ }
+ }
+
+ if c.IsSet("env") {
+ // env overrides any previous variables
+ cmdlineEnv := c.StringSlice("env")
+ if len(cmdlineEnv) > 0 {
+ parsedEnv, err := envLib.ParseSlice(cmdlineEnv)
+ if err != nil {
+ return nil, err
+ }
+ env = envLib.Join(env, parsedEnv)
+ }
+ }
+
+ // LABEL VARIABLES
+ labels, err := parse.GetAllLabels(c.StringSlice("label-file"), c.StringArray("label"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to process labels")
+ }
+ if data != nil {
+ for key, val := range data.Config.Labels {
+ if _, ok := labels[key]; !ok {
+ labels[key] = val
+ }
+ }
+ }
+
+ if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists {
+ labels[systemdGen.EnvVariable] = systemdUnit
+ }
+
+ // ANNOTATIONS
+ annotations := make(map[string]string)
+
+ // First, add our default annotations
+ annotations[ann.TTY] = "false"
+ if tty {
+ annotations[ann.TTY] = "true"
+ }
+
+ // in the event this container is in a pod, and the pod has an infra container
+ // we will want to configure it as a type "container" instead defaulting to
+ // the behavior of a "sandbox" container
+ // In Kata containers:
+ // - "sandbox" is the annotation that denotes the container should use its own
+ // VM, which is the default behavior
+ // - "container" denotes the container should join the VM of the SandboxID
+ // (the infra container)
+ if podInfraID != "" {
+ annotations[ann.SandboxID] = podInfraID
+ annotations[ann.ContainerType] = ann.ContainerTypeContainer
+ }
+
+ if data != nil {
+ // Next, add annotations from the image
+ for key, value := range data.Annotations {
+ annotations[key] = value
+ }
+ }
+ // Last, add user annotations
+ for _, annotation := range c.StringSlice("annotation") {
+ splitAnnotation := strings.SplitN(annotation, "=", 2)
+ if len(splitAnnotation) < 2 {
+ return nil, errors.Errorf("Annotations must be formatted KEY=VALUE")
+ }
+ annotations[splitAnnotation[0]] = splitAnnotation[1]
+ }
+
+ // WORKING DIRECTORY
+ workDir := "/"
+ if c.IsSet("workdir") {
+ workDir = c.String("workdir")
+ } else if data != nil && data.Config.WorkingDir != "" {
+ workDir = data.Config.WorkingDir
+ }
+
+ userCommand := []string{}
+ entrypoint := configureEntrypoint(c, data)
+ // Build the command
+ // If we have an entry point, it goes first
+ if len(entrypoint) > 0 {
+ command = entrypoint
+ }
+ if len(inputCommand) > 0 {
+ // User command overrides data CMD
+ command = append(command, inputCommand...)
+ userCommand = append(userCommand, inputCommand...)
+ } else if data != nil && len(data.Config.Cmd) > 0 && !c.IsSet("entrypoint") {
+ // If not user command, add CMD
+ command = append(command, data.Config.Cmd...)
+ userCommand = append(userCommand, data.Config.Cmd...)
+ }
+
+ if data != nil && len(command) == 0 {
+ return nil, errors.Errorf("No command specified on command line or as CMD or ENTRYPOINT in this image")
+ }
+
+ // SHM Size
+ shmSize, err := units.FromHumanSize(c.String("shm-size"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to translate --shm-size")
+ }
+
+ if c.IsSet("add-host") {
+ // Verify the additional hosts are in correct format
+ for _, host := range c.StringSlice("add-host") {
+ if _, err := parse.ValidateExtraHost(host); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ var (
+ dnsSearches []string
+ dnsServers []string
+ dnsOptions []string
+ )
+ if c.Changed("dns-search") {
+ dnsSearches = c.StringSlice("dns-search")
+ // Check for explicit dns-search domain of ''
+ if len(dnsSearches) == 0 {
+ return nil, errors.Errorf("'' is not a valid domain")
+ }
+ // Validate domains are good
+ for _, dom := range dnsSearches {
+ if dom == "." {
+ if len(dnsSearches) > 1 {
+ return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
+ }
+ continue
+ }
+ if _, err := parse.ValidateDomain(dom); err != nil {
+ return nil, err
+ }
+ }
+ }
+ if c.IsSet("dns") {
+ dnsServers = append(dnsServers, c.StringSlice("dns")...)
+ }
+ if c.IsSet("dns-opt") {
+ dnsOptions = c.StringSlice("dns-opt")
+ }
+
+ var ImageVolumes map[string]struct{}
+ if data != nil && c.String("image-volume") != "ignore" {
+ ImageVolumes = data.Config.Volumes
+ }
+
+ var imageVolType = map[string]string{
+ "bind": "",
+ "tmpfs": "",
+ "ignore": "",
+ }
+ if _, ok := imageVolType[c.String("image-volume")]; !ok {
+ return nil, errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.String("image-volume"))
+ }
+
+ systemd := c.String("systemd") == "always"
+ if !systemd && command != nil {
+ x, err := strconv.ParseBool(c.String("systemd"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot parse bool %s", c.String("systemd"))
+ }
+ if x && (command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd")) {
+ systemd = true
+ }
+ }
+ if systemd {
+ if signalString == "" {
+ stopSignal, err = util.ParseSignal("RTMIN+3")
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing systemd signal")
+ }
+ }
+ }
+ // This is done because cobra cannot have two aliased flags. So we have to check
+ // both
+ memorySwappiness := c.Int64("memory-swappiness")
+
+ logDriver := define.KubernetesLogging
+ if c.Changed("log-driver") {
+ logDriver = c.String("log-driver")
+ }
+
+ pidsLimit := c.Int64("pids-limit")
+ if c.String("cgroups") == "disabled" && !c.Changed("pids-limit") {
+ pidsLimit = -1
+ }
+
+ pid := &cc.PidConfig{
+ PidMode: pidMode,
+ }
+ ipc := &cc.IpcConfig{
+ IpcMode: ipcMode,
+ }
+
+ cgroup := &cc.CgroupConfig{
+ Cgroups: c.String("cgroups"),
+ Cgroupns: c.String("cgroupns"),
+ CgroupParent: c.String("cgroup-parent"),
+ CgroupMode: cgroupMode,
+ }
+
+ userns := &cc.UserConfig{
+ GroupAdd: c.StringSlice("group-add"),
+ IDMappings: idmappings,
+ UsernsMode: usernsMode,
+ User: user,
+ }
+
+ uts := &cc.UtsConfig{
+ UtsMode: utsMode,
+ NoHosts: c.Bool("no-hosts"),
+ HostAdd: c.StringSlice("add-host"),
+ Hostname: c.String("hostname"),
+ }
+ net := &cc.NetworkConfig{
+ DNSOpt: dnsOptions,
+ DNSSearch: dnsSearches,
+ DNSServers: dnsServers,
+ HTTPProxy: c.Bool("http-proxy"),
+ MacAddress: c.String("mac-address"),
+ Network: c.String("network"),
+ NetMode: netMode,
+ IPAddress: c.String("ip"),
+ Publish: c.StringSlice("publish"),
+ PublishAll: c.Bool("publish-all"),
+ PortBindings: portBindings,
+ }
+
+ sysctl := map[string]string{}
+ if c.Changed("sysctl") {
+ sysctl, err = util.ValidateSysctls(c.StringSlice("sysctl"))
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid value for sysctl")
+ }
+ }
+
+ secConfig := &cc.SecurityConfig{
+ CapAdd: c.StringSlice("cap-add"),
+ CapDrop: c.StringSlice("cap-drop"),
+ Privileged: c.Bool("privileged"),
+ ReadOnlyRootfs: c.Bool("read-only"),
+ ReadOnlyTmpfs: c.Bool("read-only-tmpfs"),
+ Sysctl: sysctl,
+ }
+
+ var securityOpt []string
+ if c.Changed("security-opt") {
+ securityOpt = c.StringArray("security-opt")
+ }
+ if err := secConfig.SetSecurityOpts(runtime, securityOpt); err != nil {
+ return nil, err
+ }
+
+ // SECCOMP
+ if data != nil {
+ if value, exists := labels[seccomp.ContainerImageLabel]; exists {
+ secConfig.SeccompProfileFromImage = value
+ }
+ }
+ if policy, err := seccomp.LookupPolicy(c.String("seccomp-policy")); err != nil {
+ return nil, err
+ } else {
+ secConfig.SeccompPolicy = policy
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ volumes := rtc.Containers.Volumes
+ if c.Changed("volume") {
+ volumes = append(volumes, c.StringSlice("volume")...)
+ }
+
+ devices := rtc.Containers.Devices
+ if c.Changed("device") {
+ devices = append(devices, c.StringSlice("device")...)
+ }
+
+ config := &cc.CreateConfig{
+ Annotations: annotations,
+ BuiltinImgVolumes: ImageVolumes,
+ ConmonPidFile: c.String("conmon-pidfile"),
+ ImageVolumeType: c.String("image-volume"),
+ CidFile: c.String("cidfile"),
+ Command: command,
+ UserCommand: userCommand,
+ Detach: c.Bool("detach"),
+ Devices: devices,
+ Entrypoint: entrypoint,
+ Env: env,
+ // ExposedPorts: ports,
+ Init: c.Bool("init"),
+ InitPath: c.String("init-path"),
+ Image: imageName,
+ RawImageName: rawImageName,
+ ImageID: imageID,
+ Interactive: c.Bool("interactive"),
+ // IP6Address: c.String("ipv6"), // Not implemented yet - needs CNI support for static v6
+ Labels: labels,
+ // LinkLocalIP: c.StringSlice("link-local-ip"), // Not implemented yet
+ LogDriver: logDriver,
+ LogDriverOpt: c.StringSlice("log-opt"),
+ Name: c.String("name"),
+ // NetworkAlias: c.StringSlice("network-alias"), // Not implemented - does this make sense in Podman?
+ Pod: podName,
+ Quiet: c.Bool("quiet"),
+ Resources: cc.CreateResourceConfig{
+ BlkioWeight: blkioWeight,
+ BlkioWeightDevice: c.StringSlice("blkio-weight-device"),
+ CPUShares: c.Uint64("cpu-shares"),
+ CPUPeriod: c.Uint64("cpu-period"),
+ CPUsetCPUs: c.String("cpuset-cpus"),
+ CPUsetMems: c.String("cpuset-mems"),
+ CPUQuota: c.Int64("cpu-quota"),
+ CPURtPeriod: c.Uint64("cpu-rt-period"),
+ CPURtRuntime: c.Int64("cpu-rt-runtime"),
+ CPUs: c.Float64("cpus"),
+ DeviceCgroupRules: c.StringSlice("device-cgroup-rule"),
+ DeviceReadBps: c.StringSlice("device-read-bps"),
+ DeviceReadIOps: c.StringSlice("device-read-iops"),
+ DeviceWriteBps: c.StringSlice("device-write-bps"),
+ DeviceWriteIOps: c.StringSlice("device-write-iops"),
+ DisableOomKiller: c.Bool("oom-kill-disable"),
+ ShmSize: shmSize,
+ Memory: memoryLimit,
+ MemoryReservation: memoryReservation,
+ MemorySwap: memorySwap,
+ MemorySwappiness: int(memorySwappiness),
+ KernelMemory: memoryKernel,
+ OomScoreAdj: c.Int("oom-score-adj"),
+ PidsLimit: pidsLimit,
+ Ulimit: c.StringSlice("ulimit"),
+ },
+ RestartPolicy: c.String("restart"),
+ Rm: c.Bool("rm"),
+ Security: *secConfig,
+ StopSignal: stopSignal,
+ StopTimeout: c.Uint("stop-timeout"),
+ Systemd: systemd,
+ Tmpfs: c.StringArray("tmpfs"),
+ Tty: tty,
+ MountsFlag: c.StringArray("mount"),
+ Volumes: volumes,
+ WorkDir: workDir,
+ Rootfs: rootfs,
+ VolumesFrom: c.StringSlice("volumes-from"),
+ Syslog: c.Bool("syslog"),
+
+ Pid: *pid,
+ Ipc: *ipc,
+ Cgroup: *cgroup,
+ User: *userns,
+ Uts: *uts,
+ Network: *net,
+ }
+
+ warnings, err := verifyContainerResources(config, false)
+ if err != nil {
+ return nil, err
+ }
+ for _, warning := range warnings {
+ fmt.Fprintln(os.Stderr, warning)
+ }
+ return config, nil
+}
+
+func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateConfig, ctx context.Context, pod *libpod.Pod) (*libpod.Container, error) {
+ runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod)
+ if err != nil {
+ return nil, err
+ }
+
+ // Set the CreateCommand explicitly. Some (future) consumers of libpod
+ // might not want to set it.
+ options = append(options, libpod.WithCreateCommand())
+
+ ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
+ if err != nil {
+ return nil, err
+ }
+ return ctr, nil
+}
+
+func makeHealthCheckFromCli(c *GenericCLIResults) (*manifest.Schema2HealthConfig, error) {
+ inCommand := c.String("healthcheck-command")
+ inInterval := c.String("healthcheck-interval")
+ inRetries := c.Uint("healthcheck-retries")
+ inTimeout := c.String("healthcheck-timeout")
+ inStartPeriod := c.String("healthcheck-start-period")
+
+ // Every healthcheck requires a command
+ if len(inCommand) == 0 {
+ return nil, errors.New("Must define a healthcheck command for all healthchecks")
+ }
+
+ // first try to parse option value as JSON array of strings...
+ cmd := []string{}
+ err := json.Unmarshal([]byte(inCommand), &cmd)
+ if err != nil {
+ // ...otherwise pass it to "/bin/sh -c" inside the container
+ cmd = []string{"CMD-SHELL", inCommand}
+ }
+ hc := manifest.Schema2HealthConfig{
+ Test: cmd,
+ }
+
+ if inInterval == "disable" {
+ inInterval = "0"
+ }
+ intervalDuration, err := time.ParseDuration(inInterval)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-interval %s ", inInterval)
+ }
+
+ hc.Interval = intervalDuration
+
+ if inRetries < 1 {
+ return nil, errors.New("healthcheck-retries must be greater than 0.")
+ }
+ hc.Retries = int(inRetries)
+ timeoutDuration, err := time.ParseDuration(inTimeout)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-timeout %s", inTimeout)
+ }
+ if timeoutDuration < time.Duration(1) {
+ return nil, errors.New("healthcheck-timeout must be at least 1 second")
+ }
+ hc.Timeout = timeoutDuration
+
+ startPeriodDuration, err := time.ParseDuration(inStartPeriod)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-start-period %s", inStartPeriod)
+ }
+ if startPeriodDuration < time.Duration(0) {
+ return nil, errors.New("healthcheck-start-period must be 0 seconds or greater")
+ }
+ hc.StartPeriod = startPeriodDuration
+
+ return &hc, 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
+}
+
+func addWarning(warnings []string, msg string) []string {
+ logrus.Warn(msg)
+ return append(warnings, msg)
+}
+
+func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) {
+ warnings := []string{}
+
+ cgroup2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil || cgroup2 {
+ return warnings, err
+ }
+
+ sysInfo := sysinfo.New(true)
+
+ // memory subsystem checks and adjustments
+ if config.Resources.Memory > 0 && !sysInfo.MemoryLimit {
+ warnings = addWarning(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
+ config.Resources.Memory = 0
+ config.Resources.MemorySwap = -1
+ }
+ if config.Resources.Memory > 0 && config.Resources.MemorySwap != -1 && !sysInfo.SwapLimit {
+ warnings = addWarning(warnings, "Your kernel does not support swap limit capabilities,or the cgroup is not mounted. Memory limited without swap.")
+ config.Resources.MemorySwap = -1
+ }
+ if config.Resources.Memory > 0 && config.Resources.MemorySwap > 0 && config.Resources.MemorySwap < config.Resources.Memory {
+ return warnings, fmt.Errorf("minimum memoryswap limit should be larger than memory limit, see usage")
+ }
+ if config.Resources.Memory == 0 && config.Resources.MemorySwap > 0 && !update {
+ return warnings, fmt.Errorf("you should always set the memory limit when using memoryswap limit, see usage")
+ }
+ if config.Resources.MemorySwappiness != -1 {
+ if !sysInfo.MemorySwappiness {
+ msg := "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded."
+ warnings = addWarning(warnings, msg)
+ config.Resources.MemorySwappiness = -1
+ } else {
+ swappiness := config.Resources.MemorySwappiness
+ if swappiness < -1 || swappiness > 100 {
+ return warnings, fmt.Errorf("invalid value: %v, valid memory swappiness range is 0-100", swappiness)
+ }
+ }
+ }
+ if config.Resources.MemoryReservation > 0 && !sysInfo.MemoryReservation {
+ warnings = addWarning(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.")
+ config.Resources.MemoryReservation = 0
+ }
+ if config.Resources.Memory > 0 && config.Resources.MemoryReservation > 0 && config.Resources.Memory < config.Resources.MemoryReservation {
+ return warnings, fmt.Errorf("minimum memory limit cannot be less than memory reservation limit, see usage")
+ }
+ if config.Resources.KernelMemory > 0 && !sysInfo.KernelMemory {
+ warnings = addWarning(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
+ config.Resources.KernelMemory = 0
+ }
+ if config.Resources.DisableOomKiller && !sysInfo.OomKillDisable {
+ // only produce warnings if the setting wasn't to *disable* the OOM Kill; no point
+ // warning the caller if they already wanted the feature to be off
+ warnings = addWarning(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.")
+ config.Resources.DisableOomKiller = false
+ }
+
+ if config.Resources.PidsLimit != 0 && !sysInfo.PidsLimit {
+ warnings = addWarning(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
+ config.Resources.PidsLimit = 0
+ }
+
+ if config.Resources.CPUShares > 0 && !sysInfo.CPUShares {
+ warnings = addWarning(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.")
+ config.Resources.CPUShares = 0
+ }
+ if config.Resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod {
+ warnings = addWarning(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.")
+ config.Resources.CPUPeriod = 0
+ }
+ if config.Resources.CPUPeriod != 0 && (config.Resources.CPUPeriod < 1000 || config.Resources.CPUPeriod > 1000000) {
+ return warnings, fmt.Errorf("CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
+ }
+ if config.Resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota {
+ warnings = addWarning(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
+ config.Resources.CPUQuota = 0
+ }
+ if config.Resources.CPUQuota > 0 && config.Resources.CPUQuota < 1000 {
+ return warnings, fmt.Errorf("CPU cfs quota cannot be less than 1ms (i.e. 1000)")
+ }
+ // cpuset subsystem checks and adjustments
+ if (config.Resources.CPUsetCPUs != "" || config.Resources.CPUsetMems != "") && !sysInfo.Cpuset {
+ warnings = addWarning(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. CPUset discarded.")
+ config.Resources.CPUsetCPUs = ""
+ config.Resources.CPUsetMems = ""
+ }
+ cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(config.Resources.CPUsetCPUs)
+ if err != nil {
+ return warnings, fmt.Errorf("invalid value %s for cpuset cpus", config.Resources.CPUsetCPUs)
+ }
+ if !cpusAvailable {
+ return warnings, fmt.Errorf("requested CPUs are not available - requested %s, available: %s", config.Resources.CPUsetCPUs, sysInfo.Cpus)
+ }
+ memsAvailable, err := sysInfo.IsCpusetMemsAvailable(config.Resources.CPUsetMems)
+ if err != nil {
+ return warnings, fmt.Errorf("invalid value %s for cpuset mems", config.Resources.CPUsetMems)
+ }
+ if !memsAvailable {
+ return warnings, fmt.Errorf("requested memory nodes are not available - requested %s, available: %s", config.Resources.CPUsetMems, sysInfo.Mems)
+ }
+
+ // blkio subsystem checks and adjustments
+ if config.Resources.BlkioWeight > 0 && !sysInfo.BlkioWeight {
+ warnings = addWarning(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.")
+ config.Resources.BlkioWeight = 0
+ }
+ if config.Resources.BlkioWeight > 0 && (config.Resources.BlkioWeight < 10 || config.Resources.BlkioWeight > 1000) {
+ return warnings, fmt.Errorf("range of blkio weight is from 10 to 1000")
+ }
+ if len(config.Resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
+ warnings = addWarning(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.")
+ config.Resources.BlkioWeightDevice = []string{}
+ }
+ if len(config.Resources.DeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice {
+ warnings = addWarning(warnings, "Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded")
+ config.Resources.DeviceReadBps = []string{}
+ }
+ if len(config.Resources.DeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice {
+ warnings = addWarning(warnings, "Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.")
+ config.Resources.DeviceWriteBps = []string{}
+ }
+ if len(config.Resources.DeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice {
+ warnings = addWarning(warnings, "Your kernel does not support IOPS Block read limit or the cgroup is not mounted. Block I/O IOPS read limit discarded.")
+ config.Resources.DeviceReadIOps = []string{}
+ }
+ if len(config.Resources.DeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice {
+ warnings = addWarning(warnings, "Your kernel does not support IOPS Block I/O write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.")
+ config.Resources.DeviceWriteIOps = []string{}
+ }
+
+ return warnings, nil
+}
diff --git a/pkg/varlinkapi/funcs.go b/pkg/varlinkapi/funcs.go
new file mode 100644
index 000000000..ed90ba050
--- /dev/null
+++ b/pkg/varlinkapi/funcs.go
@@ -0,0 +1,121 @@
+package varlinkapi
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/google/shlex"
+ "github.com/pkg/errors"
+)
+
+func GetSystemContext(authfile string) (*types.SystemContext, error) {
+ if authfile != "" {
+ if _, err := os.Stat(authfile); err != nil {
+ return nil, errors.Wrapf(err, "error checking authfile path %s", authfile)
+ }
+ }
+ return image.GetSystemContext("", authfile, false), nil
+}
+
+func substituteCommand(cmd string) (string, error) {
+ var (
+ newCommand string
+ )
+
+ // Replace cmd with "/proc/self/exe" if "podman" or "docker" is being
+ // used. If "/usr/bin/docker" is provided, we also sub in podman.
+ // Otherwise, leave the command unchanged.
+ if cmd == "podman" || filepath.Base(cmd) == "docker" {
+ newCommand = "/proc/self/exe"
+ } else {
+ newCommand = cmd
+ }
+
+ // If cmd is an absolute or relative path, check if the file exists.
+ // Throw an error if it doesn't exist.
+ if strings.Contains(newCommand, "/") || strings.HasPrefix(newCommand, ".") {
+ res, err := filepath.Abs(newCommand)
+ if err != nil {
+ return "", err
+ }
+ if _, err := os.Stat(res); !os.IsNotExist(err) {
+ return res, nil
+ } else if err != nil {
+ return "", err
+ }
+ }
+
+ return newCommand, nil
+}
+
+// GenerateCommand takes a label (string) and converts it to an executable command
+func GenerateCommand(command, imageName, name, globalOpts string) ([]string, error) {
+ var (
+ newCommand []string
+ )
+ if name == "" {
+ name = imageName
+ }
+
+ cmd, err := shlex.Split(command)
+ if err != nil {
+ return nil, err
+ }
+
+ prog, err := substituteCommand(cmd[0])
+ if err != nil {
+ return nil, err
+ }
+ newCommand = append(newCommand, prog)
+
+ for _, arg := range cmd[1:] {
+ var newArg string
+ switch arg {
+ case "IMAGE":
+ newArg = imageName
+ case "$IMAGE":
+ newArg = imageName
+ case "IMAGE=IMAGE":
+ newArg = fmt.Sprintf("IMAGE=%s", imageName)
+ case "IMAGE=$IMAGE":
+ newArg = fmt.Sprintf("IMAGE=%s", imageName)
+ case "NAME":
+ newArg = name
+ case "NAME=NAME":
+ newArg = fmt.Sprintf("NAME=%s", name)
+ case "NAME=$NAME":
+ newArg = fmt.Sprintf("NAME=%s", name)
+ case "$NAME":
+ newArg = name
+ case "$GLOBAL_OPTS":
+ newArg = globalOpts
+ default:
+ newArg = arg
+ }
+ newCommand = append(newCommand, newArg)
+ }
+ return newCommand, nil
+}
+
+// GenerateRunEnvironment merges the current environment variables with optional
+// environment variables provided by the user
+func GenerateRunEnvironment(name, imageName string, opts map[string]string) []string {
+ newEnv := os.Environ()
+ newEnv = append(newEnv, fmt.Sprintf("NAME=%s", name))
+ newEnv = append(newEnv, fmt.Sprintf("IMAGE=%s", imageName))
+
+ if opts["opt1"] != "" {
+ newEnv = append(newEnv, fmt.Sprintf("OPT1=%s", opts["opt1"]))
+ }
+ if opts["opt2"] != "" {
+ newEnv = append(newEnv, fmt.Sprintf("OPT2=%s", opts["opt2"]))
+ }
+ if opts["opt3"] != "" {
+ newEnv = append(newEnv, fmt.Sprintf("OPT3=%s", opts["opt3"]))
+ }
+ return newEnv
+}
diff --git a/pkg/varlinkapi/generate.go b/pkg/varlinkapi/generate.go
index 81a0df68e..4df185db6 100644
--- a/pkg/varlinkapi/generate.go
+++ b/pkg/varlinkapi/generate.go
@@ -5,13 +5,12 @@ package varlinkapi
import (
"encoding/json"
- "github.com/containers/libpod/cmd/podman/shared"
iopodman "github.com/containers/libpod/pkg/varlink"
)
// GenerateKube ...
func (i *VarlinkAPI) GenerateKube(call iopodman.VarlinkCall, name string, service bool) error {
- pod, serv, err := shared.GenerateKube(name, service, i.Runtime)
+ pod, serv, err := GenerateKube(name, service, i.Runtime)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 49bd0b0cb..8d43b8414 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -20,7 +20,6 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
@@ -779,7 +778,7 @@ func (i *VarlinkAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman
stdOut := os.Stdout
stdIn := os.Stdin
- runLabel, imageName, err := shared.GetRunlabel(input.Label, input.Image, ctx, i.Runtime, input.Pull, "", dockerRegistryOptions, input.Authfile, "", nil)
+ runLabel, imageName, err := GetRunlabel(input.Label, input.Image, ctx, i.Runtime, input.Pull, "", dockerRegistryOptions, input.Authfile, "", nil)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -787,7 +786,7 @@ func (i *VarlinkAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman
return call.ReplyErrorOccurred(fmt.Sprintf("%s does not contain the label %s", input.Image, input.Label))
}
- cmd, env, err := shared.GenerateRunlabelCommand(runLabel, imageName, input.Name, input.Opts, input.ExtraArgs, "")
+ cmd, env, err := GenerateRunlabelCommand(runLabel, imageName, input.Name, input.Opts, input.ExtraArgs, "")
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
diff --git a/pkg/varlinkapi/intermediate.go b/pkg/varlinkapi/intermediate.go
new file mode 100644
index 000000000..f04665a86
--- /dev/null
+++ b/pkg/varlinkapi/intermediate.go
@@ -0,0 +1,289 @@
+package varlinkapi
+
+import (
+ "github.com/sirupsen/logrus"
+)
+
+/*
+attention
+
+in this file you will see a lot of struct duplication. this was done because people wanted a strongly typed
+varlink mechanism. this resulted in us creating this intermediate layer that allows us to take the input
+from the cli and make an intermediate layer which can be transferred as strongly typed structures over a varlink
+interface.
+
+we intentionally avoided heavy use of reflection here because we were concerned about performance impacts to the
+non-varlink intermediate layer generation.
+*/
+
+// GenericCLIResult describes the overall interface for dealing with
+// the create command cli in both local and remote uses
+type GenericCLIResult interface {
+ IsSet() bool
+ Name() string
+ Value() interface{}
+}
+
+// CRStringSlice describes a string slice cli struct
+type CRStringSlice struct {
+ Val []string
+ createResult
+}
+
+// CRString describes a string cli struct
+type CRString struct {
+ Val string
+ createResult
+}
+
+// CRUint64 describes a uint64 cli struct
+type CRUint64 struct {
+ Val uint64
+ createResult
+}
+
+// CRFloat64 describes a float64 cli struct
+type CRFloat64 struct {
+ Val float64
+ createResult
+}
+
+//CRBool describes a bool cli struct
+type CRBool struct {
+ Val bool
+ createResult
+}
+
+// CRInt64 describes an int64 cli struct
+type CRInt64 struct {
+ Val int64
+ createResult
+}
+
+// CRUint describes a uint cli struct
+type CRUint struct {
+ Val uint
+ createResult
+}
+
+// CRInt describes an int cli struct
+type CRInt struct {
+ Val int
+ createResult
+}
+
+// CRStringArray describes a stringarray cli struct
+type CRStringArray struct {
+ Val []string
+ createResult
+}
+
+type createResult struct {
+ Flag string
+ Changed bool
+}
+
+// GenericCLIResults in the intermediate object between the cobra cli
+// and createconfig
+type GenericCLIResults struct {
+ results map[string]GenericCLIResult
+ InputArgs []string
+}
+
+// IsSet returns a bool if the flag was changed
+func (f GenericCLIResults) IsSet(flag string) bool {
+ r := f.findResult(flag)
+ if r == nil {
+ return false
+ }
+ return r.IsSet()
+}
+
+// Value returns the value of the cli flag
+func (f GenericCLIResults) Value(flag string) interface{} {
+ r := f.findResult(flag)
+ if r == nil {
+ return ""
+ }
+ return r.Value()
+}
+
+func (f GenericCLIResults) findResult(flag string) GenericCLIResult {
+ val, ok := f.results[flag]
+ if ok {
+ return val
+ }
+ logrus.Debugf("unable to find flag %s", flag)
+ return nil
+}
+
+// Bool is a wrapper to get a bool value from GenericCLIResults
+func (f GenericCLIResults) Bool(flag string) bool {
+ r := f.findResult(flag)
+ if r == nil {
+ return false
+ }
+ return r.Value().(bool)
+}
+
+// String is a wrapper to get a string value from GenericCLIResults
+func (f GenericCLIResults) String(flag string) string {
+ r := f.findResult(flag)
+ if r == nil {
+ return ""
+ }
+ return r.Value().(string)
+}
+
+// Uint is a wrapper to get an uint value from GenericCLIResults
+func (f GenericCLIResults) Uint(flag string) uint {
+ r := f.findResult(flag)
+ if r == nil {
+ return 0
+ }
+ return r.Value().(uint)
+}
+
+// StringSlice is a wrapper to get a stringslice value from GenericCLIResults
+func (f GenericCLIResults) StringSlice(flag string) []string {
+ r := f.findResult(flag)
+ if r == nil {
+ return []string{}
+ }
+ return r.Value().([]string)
+}
+
+// StringArray is a wrapper to get a stringslice value from GenericCLIResults
+func (f GenericCLIResults) StringArray(flag string) []string {
+ r := f.findResult(flag)
+ if r == nil {
+ return []string{}
+ }
+ return r.Value().([]string)
+}
+
+// Uint64 is a wrapper to get an uint64 value from GenericCLIResults
+func (f GenericCLIResults) Uint64(flag string) uint64 {
+ r := f.findResult(flag)
+ if r == nil {
+ return 0
+ }
+ return r.Value().(uint64)
+}
+
+// Int64 is a wrapper to get an int64 value from GenericCLIResults
+func (f GenericCLIResults) Int64(flag string) int64 {
+ r := f.findResult(flag)
+ if r == nil {
+ return 0
+ }
+ return r.Value().(int64)
+}
+
+// Int is a wrapper to get an int value from GenericCLIResults
+func (f GenericCLIResults) Int(flag string) int {
+ r := f.findResult(flag)
+ if r == nil {
+ return 0
+ }
+ return r.Value().(int)
+}
+
+// Float64 is a wrapper to get an float64 value from GenericCLIResults
+func (f GenericCLIResults) Float64(flag string) float64 {
+ r := f.findResult(flag)
+ if r == nil {
+ return 0
+ }
+ return r.Value().(float64)
+}
+
+// Float64 is a wrapper to get an float64 value from GenericCLIResults
+func (f GenericCLIResults) Changed(flag string) bool {
+ r := f.findResult(flag)
+ if r == nil {
+ return false
+ }
+ return r.IsSet()
+}
+
+// IsSet ...
+func (c CRStringSlice) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRStringSlice) Name() string { return c.Flag }
+
+// Value ...
+func (c CRStringSlice) Value() interface{} { return c.Val }
+
+// IsSet ...
+func (c CRString) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRString) Name() string { return c.Flag }
+
+// Value ...
+func (c CRString) Value() interface{} { return c.Val }
+
+// IsSet ...
+func (c CRUint64) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRUint64) Name() string { return c.Flag }
+
+// Value ...
+func (c CRUint64) Value() interface{} { return c.Val }
+
+// IsSet ...
+func (c CRFloat64) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRFloat64) Name() string { return c.Flag }
+
+// Value ...
+func (c CRFloat64) Value() interface{} { return c.Val }
+
+// IsSet ...
+func (c CRBool) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRBool) Name() string { return c.Flag }
+
+// Value ...
+func (c CRBool) Value() interface{} { return c.Val }
+
+// IsSet ...
+func (c CRInt64) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRInt64) Name() string { return c.Flag }
+
+// Value ...
+func (c CRInt64) Value() interface{} { return c.Val }
+
+// IsSet ...
+func (c CRUint) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRUint) Name() string { return c.Flag }
+
+// Value ...
+func (c CRUint) Value() interface{} { return c.Val }
+
+// IsSet ...
+func (c CRInt) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRInt) Name() string { return c.Flag }
+
+// Value ...
+func (c CRInt) Value() interface{} { return c.Val }
+
+// IsSet ...
+func (c CRStringArray) IsSet() bool { return c.Changed }
+
+// Name ...
+func (c CRStringArray) Name() string { return c.Flag }
+
+// Value ...
+func (c CRStringArray) Value() interface{} { return c.Val }
diff --git a/pkg/varlinkapi/intermediate_varlink.go b/pkg/varlinkapi/intermediate_varlink.go
new file mode 100644
index 000000000..21c57d4f4
--- /dev/null
+++ b/pkg/varlinkapi/intermediate_varlink.go
@@ -0,0 +1,457 @@
+// +build varlink remoteclient
+
+package varlinkapi
+
+import (
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/libpod/pkg/rootless"
+ iopodman "github.com/containers/libpod/pkg/varlink"
+ "github.com/pkg/errors"
+)
+
+//FIXME these are duplicated here to resolve a circular
+//import with cmd/podman/common.
+var (
+ // DefaultHealthCheckInterval default value
+ DefaultHealthCheckInterval = "30s"
+ // DefaultHealthCheckRetries default value
+ DefaultHealthCheckRetries uint = 3
+ // DefaultHealthCheckStartPeriod default value
+ DefaultHealthCheckStartPeriod = "0s"
+ // DefaultHealthCheckTimeout default value
+ DefaultHealthCheckTimeout = "30s"
+ // DefaultImageVolume default value
+ DefaultImageVolume = "bind"
+)
+
+// StringSliceToPtr converts a genericcliresult value into a *[]string
+func StringSliceToPtr(g GenericCLIResult) *[]string {
+ if !g.IsSet() {
+ return nil
+ }
+ newT := g.Value().([]string)
+ return &newT
+}
+
+// StringToPtr converts a genericcliresult value into a *string
+func StringToPtr(g GenericCLIResult) *string {
+ if !g.IsSet() {
+ return nil
+ }
+ newT := g.Value().(string)
+ return &newT
+}
+
+// BoolToPtr converts a genericcliresult value into a *bool
+func BoolToPtr(g GenericCLIResult) *bool {
+ if !g.IsSet() {
+ return nil
+ }
+ newT := g.Value().(bool)
+ return &newT
+}
+
+// AnyIntToInt64Ptr converts a genericcliresult value into an *int64
+func AnyIntToInt64Ptr(g GenericCLIResult) *int64 {
+ if !g.IsSet() {
+ return nil
+ }
+ var newT int64
+ switch g.Value().(type) {
+ case int:
+ newT = int64(g.Value().(int))
+ case int64:
+ newT = g.Value().(int64)
+ case uint64:
+ newT = int64(g.Value().(uint64))
+ case uint:
+ newT = int64(g.Value().(uint))
+ default:
+ panic(errors.Errorf("invalid int type"))
+ }
+ return &newT
+}
+
+// Float64ToPtr converts a genericcliresult into a *float64
+func Float64ToPtr(g GenericCLIResult) *float64 {
+ if !g.IsSet() {
+ return nil
+ }
+ newT := g.Value().(float64)
+ return &newT
+}
+
+// MakeVarlink creates a varlink transportable struct from GenericCLIResults
+func (g GenericCLIResults) MakeVarlink() iopodman.Create {
+ v := iopodman.Create{
+ Args: g.InputArgs,
+ AddHost: StringSliceToPtr(g.Find("add-host")),
+ Annotation: StringSliceToPtr(g.Find("annotation")),
+ Attach: StringSliceToPtr(g.Find("attach")),
+ BlkioWeight: StringToPtr(g.Find("blkio-weight")),
+ BlkioWeightDevice: StringSliceToPtr(g.Find("blkio-weight-device")),
+ CapAdd: StringSliceToPtr(g.Find("cap-add")),
+ CapDrop: StringSliceToPtr(g.Find("cap-drop")),
+ CgroupParent: StringToPtr(g.Find("cgroup-parent")),
+ CidFile: StringToPtr(g.Find("cidfile")),
+ ConmonPidfile: StringToPtr(g.Find("conmon-pidfile")),
+ CpuPeriod: AnyIntToInt64Ptr(g.Find("cpu-period")),
+ CpuQuota: AnyIntToInt64Ptr(g.Find("cpu-quota")),
+ CpuRtPeriod: AnyIntToInt64Ptr(g.Find("cpu-rt-period")),
+ CpuRtRuntime: AnyIntToInt64Ptr(g.Find("cpu-rt-runtime")),
+ CpuShares: AnyIntToInt64Ptr(g.Find("cpu-shares")),
+ Cpus: Float64ToPtr(g.Find("cpus")),
+ CpuSetCpus: StringToPtr(g.Find("cpuset-cpus")),
+ CpuSetMems: StringToPtr(g.Find("cpuset-mems")),
+ Detach: BoolToPtr(g.Find("detach")),
+ DetachKeys: StringToPtr(g.Find("detach-keys")),
+ Device: StringSliceToPtr(g.Find("device")),
+ DeviceReadBps: StringSliceToPtr(g.Find("device-read-bps")),
+ DeviceReadIops: StringSliceToPtr(g.Find("device-read-iops")),
+ DeviceWriteBps: StringSliceToPtr(g.Find("device-write-bps")),
+ DeviceWriteIops: StringSliceToPtr(g.Find("device-write-iops")),
+ Dns: StringSliceToPtr(g.Find("dns")),
+ DnsOpt: StringSliceToPtr(g.Find("dns-opt")),
+ DnsSearch: StringSliceToPtr(g.Find("dns-search")),
+ Entrypoint: StringToPtr(g.Find("entrypoint")),
+ Env: StringSliceToPtr(g.Find("env")),
+ EnvFile: StringSliceToPtr(g.Find("env-file")),
+ Expose: StringSliceToPtr(g.Find("expose")),
+ Gidmap: StringSliceToPtr(g.Find("gidmap")),
+ Groupadd: StringSliceToPtr(g.Find("group-add")),
+ HealthcheckCommand: StringToPtr(g.Find("healthcheck-command")),
+ HealthcheckInterval: StringToPtr(g.Find("healthcheck-interval")),
+ HealthcheckRetries: AnyIntToInt64Ptr(g.Find("healthcheck-retries")),
+ HealthcheckStartPeriod: StringToPtr(g.Find("healthcheck-start-period")),
+ HealthcheckTimeout: StringToPtr(g.Find("healthcheck-timeout")),
+ Hostname: StringToPtr(g.Find("hostname")),
+ ImageVolume: StringToPtr(g.Find("image-volume")),
+ Init: BoolToPtr(g.Find("init")),
+ InitPath: StringToPtr(g.Find("init-path")),
+ Interactive: BoolToPtr(g.Find("interactive")),
+ Ip: StringToPtr(g.Find("ip")),
+ Ipc: StringToPtr(g.Find("ipc")),
+ KernelMemory: StringToPtr(g.Find("kernel-memory")),
+ Label: StringSliceToPtr(g.Find("label")),
+ LabelFile: StringSliceToPtr(g.Find("label-file")),
+ LogDriver: StringToPtr(g.Find("log-driver")),
+ LogOpt: StringSliceToPtr(g.Find("log-opt")),
+ MacAddress: StringToPtr(g.Find("mac-address")),
+ Memory: StringToPtr(g.Find("memory")),
+ MemoryReservation: StringToPtr(g.Find("memory-reservation")),
+ MemorySwap: StringToPtr(g.Find("memory-swap")),
+ MemorySwappiness: AnyIntToInt64Ptr(g.Find("memory-swappiness")),
+ Name: StringToPtr(g.Find("name")),
+ Network: StringToPtr(g.Find("network")),
+ OomKillDisable: BoolToPtr(g.Find("oom-kill-disable")),
+ OomScoreAdj: AnyIntToInt64Ptr(g.Find("oom-score-adj")),
+ OverrideOS: StringToPtr(g.Find("override-os")),
+ OverrideArch: StringToPtr(g.Find("override-arch")),
+ Pid: StringToPtr(g.Find("pid")),
+ PidsLimit: AnyIntToInt64Ptr(g.Find("pids-limit")),
+ Pod: StringToPtr(g.Find("pod")),
+ Privileged: BoolToPtr(g.Find("privileged")),
+ Publish: StringSliceToPtr(g.Find("publish")),
+ PublishAll: BoolToPtr(g.Find("publish-all")),
+ Pull: StringToPtr(g.Find("pull")),
+ Quiet: BoolToPtr(g.Find("quiet")),
+ Readonly: BoolToPtr(g.Find("read-only")),
+ Readonlytmpfs: BoolToPtr(g.Find("read-only-tmpfs")),
+ Restart: StringToPtr(g.Find("restart")),
+ Rm: BoolToPtr(g.Find("rm")),
+ Rootfs: BoolToPtr(g.Find("rootfs")),
+ SecurityOpt: StringSliceToPtr(g.Find("security-opt")),
+ ShmSize: StringToPtr(g.Find("shm-size")),
+ StopSignal: StringToPtr(g.Find("stop-signal")),
+ StopTimeout: AnyIntToInt64Ptr(g.Find("stop-timeout")),
+ StorageOpt: StringSliceToPtr(g.Find("storage-opt")),
+ Subuidname: StringToPtr(g.Find("subuidname")),
+ Subgidname: StringToPtr(g.Find("subgidname")),
+ Sysctl: StringSliceToPtr(g.Find("sysctl")),
+ Systemd: StringToPtr(g.Find("systemd")),
+ Tmpfs: StringSliceToPtr(g.Find("tmpfs")),
+ Tty: BoolToPtr(g.Find("tty")),
+ Uidmap: StringSliceToPtr(g.Find("uidmap")),
+ Ulimit: StringSliceToPtr(g.Find("ulimit")),
+ User: StringToPtr(g.Find("user")),
+ Userns: StringToPtr(g.Find("userns")),
+ Uts: StringToPtr(g.Find("uts")),
+ Mount: StringSliceToPtr(g.Find("mount")),
+ Volume: StringSliceToPtr(g.Find("volume")),
+ VolumesFrom: StringSliceToPtr(g.Find("volumes-from")),
+ WorkDir: StringToPtr(g.Find("workdir")),
+ }
+
+ return v
+}
+
+func stringSliceFromVarlink(v *[]string, flagName string, defaultValue *[]string) CRStringSlice {
+ cr := CRStringSlice{}
+ if v == nil {
+ cr.Val = []string{}
+ if defaultValue != nil {
+ cr.Val = *defaultValue
+ }
+ cr.Changed = false
+ } else {
+ cr.Val = *v
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+func stringFromVarlink(v *string, flagName string, defaultValue *string) CRString {
+ cr := CRString{}
+ if v == nil {
+ cr.Val = ""
+ if defaultValue != nil {
+ cr.Val = *defaultValue
+ }
+ cr.Changed = false
+ } else {
+ cr.Val = *v
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+func boolFromVarlink(v *bool, flagName string, defaultValue bool) CRBool {
+ cr := CRBool{}
+ if v == nil {
+ // In case a cli bool default value is true
+ cr.Val = defaultValue
+ cr.Changed = false
+ } else {
+ cr.Val = *v
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+func uint64FromVarlink(v *int64, flagName string, defaultValue *uint64) CRUint64 {
+ cr := CRUint64{}
+ if v == nil {
+ cr.Val = 0
+ if defaultValue != nil {
+ cr.Val = *defaultValue
+ }
+ cr.Changed = false
+ } else {
+ cr.Val = uint64(*v)
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+func int64FromVarlink(v *int64, flagName string, defaultValue *int64) CRInt64 {
+ cr := CRInt64{}
+ if v == nil {
+ cr.Val = 0
+ if defaultValue != nil {
+ cr.Val = *defaultValue
+ }
+ cr.Changed = false
+ } else {
+ cr.Val = *v
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+func float64FromVarlink(v *float64, flagName string, defaultValue *float64) CRFloat64 {
+ cr := CRFloat64{}
+ if v == nil {
+ cr.Val = 0
+ if defaultValue != nil {
+ cr.Val = *defaultValue
+ }
+ cr.Changed = false
+ } else {
+ cr.Val = *v
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+func uintFromVarlink(v *int64, flagName string, defaultValue *uint) CRUint {
+ cr := CRUint{}
+ if v == nil {
+ cr.Val = 0
+ if defaultValue != nil {
+ cr.Val = *defaultValue
+ }
+ cr.Changed = false
+ } else {
+ cr.Val = uint(*v)
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+func stringArrayFromVarlink(v *[]string, flagName string, defaultValue *[]string) CRStringArray {
+ cr := CRStringArray{}
+ if v == nil {
+ cr.Val = []string{}
+ if defaultValue != nil {
+ cr.Val = *defaultValue
+ }
+ cr.Changed = false
+ } else {
+ cr.Val = *v
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+func intFromVarlink(v *int64, flagName string, defaultValue *int) CRInt {
+ cr := CRInt{}
+ if v == nil {
+ if defaultValue != nil {
+ cr.Val = *defaultValue
+ }
+ cr.Val = 0
+ cr.Changed = false
+ } else {
+ cr.Val = int(*v)
+ cr.Changed = true
+ }
+ cr.Flag = flagName
+ return cr
+}
+
+// VarlinkCreateToGeneric creates a GenericCLIResults from the varlink create
+// structure.
+func VarlinkCreateToGeneric(opts iopodman.Create) GenericCLIResults {
+ // FIXME this will need to be fixed!!!!! With containers conf
+ //defaultContainerConfig := cliconfig.GetDefaultConfig()
+ // TODO | WARN
+ // We do not get a default network over varlink. Unlike the other default values for some cli
+ // elements, it seems it gets set to the default anyway.
+
+ var memSwapDefault int64 = -1
+ netModeDefault := "bridge"
+ systemdDefault := "true"
+ if rootless.IsRootless() {
+ netModeDefault = "slirp4netns"
+ }
+
+ shmSize := config.DefaultShmSize
+
+ m := make(map[string]GenericCLIResult)
+ m["add-host"] = stringSliceFromVarlink(opts.AddHost, "add-host", nil)
+ m["annotation"] = stringSliceFromVarlink(opts.Annotation, "annotation", nil)
+ m["attach"] = stringSliceFromVarlink(opts.Attach, "attach", nil)
+ m["blkio-weight"] = stringFromVarlink(opts.BlkioWeight, "blkio-weight", nil)
+ m["blkio-weight-device"] = stringSliceFromVarlink(opts.BlkioWeightDevice, "blkio-weight-device", nil)
+ m["cap-add"] = stringSliceFromVarlink(opts.CapAdd, "cap-add", nil)
+ m["cap-drop"] = stringSliceFromVarlink(opts.CapDrop, "cap-drop", nil)
+ m["cgroup-parent"] = stringFromVarlink(opts.CgroupParent, "cgroup-parent", nil)
+ m["cidfile"] = stringFromVarlink(opts.CidFile, "cidfile", nil)
+ m["conmon-pidfile"] = stringFromVarlink(opts.ConmonPidfile, "conmon-file", nil)
+ m["cpu-period"] = uint64FromVarlink(opts.CpuPeriod, "cpu-period", nil)
+ m["cpu-quota"] = int64FromVarlink(opts.CpuQuota, "quota", nil)
+ m["cpu-rt-period"] = uint64FromVarlink(opts.CpuRtPeriod, "cpu-rt-period", nil)
+ m["cpu-rt-runtime"] = int64FromVarlink(opts.CpuRtRuntime, "cpu-rt-quota", nil)
+ m["cpu-shares"] = uint64FromVarlink(opts.CpuShares, "cpu-shares", nil)
+ m["cpus"] = float64FromVarlink(opts.Cpus, "cpus", nil)
+ m["cpuset-cpus"] = stringFromVarlink(opts.CpuSetCpus, "cpuset-cpus", nil)
+ m["cpuset-mems"] = stringFromVarlink(opts.CpuSetMems, "cpuset-mems", nil)
+ m["detach"] = boolFromVarlink(opts.Detach, "detach", false)
+ m["detach-keys"] = stringFromVarlink(opts.DetachKeys, "detach-keys", nil)
+ m["device"] = stringSliceFromVarlink(opts.Device, "device", nil)
+ m["device-read-bps"] = stringSliceFromVarlink(opts.DeviceReadBps, "device-read-bps", nil)
+ m["device-read-iops"] = stringSliceFromVarlink(opts.DeviceReadIops, "device-read-iops", nil)
+ m["device-write-bps"] = stringSliceFromVarlink(opts.DeviceWriteBps, "write-device-bps", nil)
+ m["device-write-iops"] = stringSliceFromVarlink(opts.DeviceWriteIops, "write-device-iops", nil)
+ m["dns"] = stringSliceFromVarlink(opts.Dns, "dns", nil)
+ m["dns-opt"] = stringSliceFromVarlink(opts.DnsOpt, "dns-opt", nil)
+ m["dns-search"] = stringSliceFromVarlink(opts.DnsSearch, "dns-search", nil)
+ m["entrypoint"] = stringFromVarlink(opts.Entrypoint, "entrypoint", nil)
+ m["env"] = stringArrayFromVarlink(opts.Env, "env", nil)
+ m["env-file"] = stringSliceFromVarlink(opts.EnvFile, "env-file", nil)
+ m["expose"] = stringSliceFromVarlink(opts.Expose, "expose", nil)
+ m["gidmap"] = stringSliceFromVarlink(opts.Gidmap, "gidmap", nil)
+ m["group-add"] = stringSliceFromVarlink(opts.Groupadd, "group-add", nil)
+ m["healthcheck-command"] = stringFromVarlink(opts.HealthcheckCommand, "healthcheck-command", nil)
+ m["healthcheck-interval"] = stringFromVarlink(opts.HealthcheckInterval, "healthcheck-interval", &DefaultHealthCheckInterval)
+ m["healthcheck-retries"] = uintFromVarlink(opts.HealthcheckRetries, "healthcheck-retries", &DefaultHealthCheckRetries)
+ m["healthcheck-start-period"] = stringFromVarlink(opts.HealthcheckStartPeriod, "healthcheck-start-period", &DefaultHealthCheckStartPeriod)
+ m["healthcheck-timeout"] = stringFromVarlink(opts.HealthcheckTimeout, "healthcheck-timeout", &DefaultHealthCheckTimeout)
+ m["hostname"] = stringFromVarlink(opts.Hostname, "hostname", nil)
+ m["image-volume"] = stringFromVarlink(opts.ImageVolume, "image-volume", &DefaultImageVolume)
+ m["init"] = boolFromVarlink(opts.Init, "init", false)
+ m["init-path"] = stringFromVarlink(opts.InitPath, "init-path", nil)
+ m["interactive"] = boolFromVarlink(opts.Interactive, "interactive", false)
+ m["ip"] = stringFromVarlink(opts.Ip, "ip", nil)
+ m["ipc"] = stringFromVarlink(opts.Ipc, "ipc", nil)
+ m["kernel-memory"] = stringFromVarlink(opts.KernelMemory, "kernel-memory", nil)
+ m["label"] = stringArrayFromVarlink(opts.Label, "label", nil)
+ m["label-file"] = stringSliceFromVarlink(opts.LabelFile, "label-file", nil)
+ m["log-driver"] = stringFromVarlink(opts.LogDriver, "log-driver", nil)
+ m["log-opt"] = stringSliceFromVarlink(opts.LogOpt, "log-opt", nil)
+ m["mac-address"] = stringFromVarlink(opts.MacAddress, "mac-address", nil)
+ m["memory"] = stringFromVarlink(opts.Memory, "memory", nil)
+ m["memory-reservation"] = stringFromVarlink(opts.MemoryReservation, "memory-reservation", nil)
+ m["memory-swap"] = stringFromVarlink(opts.MemorySwap, "memory-swap", nil)
+ m["memory-swappiness"] = int64FromVarlink(opts.MemorySwappiness, "memory-swappiness", &memSwapDefault)
+ m["name"] = stringFromVarlink(opts.Name, "name", nil)
+ m["network"] = stringFromVarlink(opts.Network, "network", &netModeDefault)
+ m["no-hosts"] = boolFromVarlink(opts.NoHosts, "no-hosts", false)
+ m["oom-kill-disable"] = boolFromVarlink(opts.OomKillDisable, "oon-kill-disable", false)
+ m["oom-score-adj"] = intFromVarlink(opts.OomScoreAdj, "oom-score-adj", nil)
+ m["override-os"] = stringFromVarlink(opts.OverrideOS, "override-os", nil)
+ m["override-arch"] = stringFromVarlink(opts.OverrideArch, "override-arch", nil)
+ m["pid"] = stringFromVarlink(opts.Pid, "pid", nil)
+ m["pids-limit"] = int64FromVarlink(opts.PidsLimit, "pids-limit", nil)
+ m["pod"] = stringFromVarlink(opts.Pod, "pod", nil)
+ m["privileged"] = boolFromVarlink(opts.Privileged, "privileged", false)
+ m["publish"] = stringSliceFromVarlink(opts.Publish, "publish", nil)
+ m["publish-all"] = boolFromVarlink(opts.PublishAll, "publish-all", false)
+ m["pull"] = stringFromVarlink(opts.Pull, "missing", nil)
+ m["quiet"] = boolFromVarlink(opts.Quiet, "quiet", false)
+ m["read-only"] = boolFromVarlink(opts.Readonly, "read-only", false)
+ m["read-only-tmpfs"] = boolFromVarlink(opts.Readonlytmpfs, "read-only-tmpfs", true)
+ m["restart"] = stringFromVarlink(opts.Restart, "restart", nil)
+ m["rm"] = boolFromVarlink(opts.Rm, "rm", false)
+ m["rootfs"] = boolFromVarlink(opts.Rootfs, "rootfs", false)
+ m["security-opt"] = stringArrayFromVarlink(opts.SecurityOpt, "security-opt", nil)
+ m["shm-size"] = stringFromVarlink(opts.ShmSize, "shm-size", &shmSize)
+ m["stop-signal"] = stringFromVarlink(opts.StopSignal, "stop-signal", nil)
+ m["stop-timeout"] = uintFromVarlink(opts.StopTimeout, "stop-timeout", nil)
+ m["storage-opt"] = stringSliceFromVarlink(opts.StorageOpt, "storage-opt", nil)
+ m["subgidname"] = stringFromVarlink(opts.Subgidname, "subgidname", nil)
+ m["subuidname"] = stringFromVarlink(opts.Subuidname, "subuidname", nil)
+ m["sysctl"] = stringSliceFromVarlink(opts.Sysctl, "sysctl", nil)
+ m["systemd"] = stringFromVarlink(opts.Systemd, "systemd", &systemdDefault)
+ m["tmpfs"] = stringSliceFromVarlink(opts.Tmpfs, "tmpfs", nil)
+ m["tty"] = boolFromVarlink(opts.Tty, "tty", false)
+ m["uidmap"] = stringSliceFromVarlink(opts.Uidmap, "uidmap", nil)
+ m["ulimit"] = stringSliceFromVarlink(opts.Ulimit, "ulimit", nil)
+ m["user"] = stringFromVarlink(opts.User, "user", nil)
+ m["userns"] = stringFromVarlink(opts.Userns, "userns", nil)
+ m["uts"] = stringFromVarlink(opts.Uts, "uts", nil)
+ m["mount"] = stringArrayFromVarlink(opts.Mount, "mount", nil)
+ m["volume"] = stringArrayFromVarlink(opts.Volume, "volume", nil)
+ m["volumes-from"] = stringSliceFromVarlink(opts.VolumesFrom, "volumes-from", nil)
+ m["workdir"] = stringFromVarlink(opts.WorkDir, "workdir", nil)
+
+ gcli := GenericCLIResults{m, opts.Args}
+ return gcli
+}
+
+// Find returns a flag from a GenericCLIResults by name
+func (g GenericCLIResults) Find(name string) GenericCLIResult {
+ result, ok := g.results[name]
+ if ok {
+ return result
+ }
+ panic(errors.Errorf("unable to find generic flag for varlink %s", name))
+}
diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go
index 94add1b6c..5a9360447 100644
--- a/pkg/varlinkapi/pods.go
+++ b/pkg/varlinkapi/pods.go
@@ -5,11 +5,14 @@ package varlinkapi
import (
"encoding/json"
"fmt"
+ "strconv"
"syscall"
- "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-connections/nat"
+ "github.com/pkg/errors"
+
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/adapter/shortcuts"
iopodman "github.com/containers/libpod/pkg/varlink"
)
@@ -18,7 +21,7 @@ func (i *VarlinkAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCre
var options []libpod.PodCreateOption
if create.Infra {
options = append(options, libpod.WithInfraContainer())
- nsOptions, err := shared.GetNamespaceOptions(create.Share)
+ nsOptions, err := GetNamespaceOptions(create.Share)
if err != nil {
return err
}
@@ -44,7 +47,7 @@ func (i *VarlinkAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCre
if !create.Infra {
return call.ReplyErrorOccurred("you must have an infra container to publish port bindings to the host")
}
- portBindings, err := shared.CreatePortBindings(create.Publish)
+ portBindings, err := CreatePortBindings(create.Publish)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -70,7 +73,7 @@ func (i *VarlinkAPI) ListPods(call iopodman.VarlinkCall) error {
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
- opts := shared.PsOptions{}
+ opts := PsOptions{}
for _, pod := range pods {
listPod, err := makeListPod(pod, opts)
if err != nil {
@@ -87,7 +90,7 @@ func (i *VarlinkAPI) GetPod(call iopodman.VarlinkCall, name string) error {
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
- opts := shared.PsOptions{}
+ opts := PsOptions{}
listPod, err := makeListPod(pod, opts)
if err != nil {
@@ -100,7 +103,7 @@ func (i *VarlinkAPI) GetPod(call iopodman.VarlinkCall, name string) error {
// 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, _ := shared.GetPodStatus(p)
+ state, _ := p.GetPodStatus()
for _, status := range statuses {
if state == status {
return true
@@ -290,11 +293,11 @@ func (i *VarlinkAPI) GetPodStats(call iopodman.VarlinkCall, name string) error {
return call.ReplyGetPodStats(pod.ID(), containersStats)
}
-// GetPodsByContext returns a slice of pod ids based on all, latest, or a list
+// 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 := shortcuts.GetPodsByContext(all, latest, input, i.Runtime)
+ pods, err := getPodsByContext(all, latest, input, i.Runtime)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -337,7 +340,7 @@ func (i *VarlinkAPI) TopPod(call iopodman.VarlinkCall, name string, latest bool,
return call.ReplyPodNotFound(name, err.Error())
}
- podStatus, err := shared.GetPodStatus(pod)
+ podStatus, err := pod.GetPodStatus()
if err != nil {
return call.ReplyErrorOccurred(fmt.Sprintf("unable to get status for pod %s", pod.ID()))
}
@@ -350,3 +353,36 @@ func (i *VarlinkAPI) TopPod(call iopodman.VarlinkCall, name string, latest bool,
}
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
+}
diff --git a/pkg/varlinkapi/shortcuts.go b/pkg/varlinkapi/shortcuts.go
new file mode 100644
index 000000000..771129404
--- /dev/null
+++ b/pkg/varlinkapi/shortcuts.go
@@ -0,0 +1,66 @@
+package varlinkapi
+
+import (
+ "github.com/containers/libpod/libpod"
+ "github.com/sirupsen/logrus"
+)
+
+// getPodsByContext returns a slice of pods. Note that all, latest and pods are
+// mutually exclusive arguments.
+func getPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) ([]*libpod.Pod, error) {
+ var outpods []*libpod.Pod
+ if all {
+ return runtime.GetAllPods()
+ }
+ if latest {
+ p, err := runtime.GetLatestPod()
+ if err != nil {
+ return nil, err
+ }
+ outpods = append(outpods, p)
+ return outpods, nil
+ }
+ var err error
+ for _, p := range pods {
+ pod, e := runtime.LookupPod(p)
+ if e != nil {
+ // Log all errors here, so callers don't need to.
+ logrus.Debugf("Error looking up pod %q: %v", p, e)
+ if err == nil {
+ err = e
+ }
+ } else {
+ outpods = append(outpods, pod)
+ }
+ }
+ return outpods, err
+}
+
+// getContainersByContext gets pods whether all, latest, or a slice of names/ids
+// is specified.
+func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) {
+ var ctr *libpod.Container
+ ctrs = []*libpod.Container{}
+
+ switch {
+ case all:
+ ctrs, err = runtime.GetAllContainers()
+ case latest:
+ ctr, err = runtime.GetLatestContainer()
+ ctrs = append(ctrs, ctr)
+ default:
+ for _, n := range names {
+ ctr, e := runtime.LookupContainer(n)
+ if e != nil {
+ // Log all errors here, so callers don't need to.
+ logrus.Debugf("Error looking up container %q: %v", n, e)
+ if err == nil {
+ err = e
+ }
+ } else {
+ ctrs = append(ctrs, ctr)
+ }
+ }
+ }
+ return
+}
diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go
index 6b196f384..f73e77249 100644
--- a/pkg/varlinkapi/util.go
+++ b/pkg/varlinkapi/util.go
@@ -9,7 +9,6 @@ import (
"time"
"github.com/containers/buildah"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/channelwriter"
@@ -22,12 +21,12 @@ func getContext() context.Context {
return context.TODO()
}
-func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct) iopodman.Container {
+func makeListContainer(containerID string, batchInfo BatchContainerStruct) iopodman.Container {
var (
mounts []iopodman.ContainerMount
ports []iopodman.ContainerPortMappings
)
- ns := shared.GetNamespaces(batchInfo.Pid)
+ ns := GetNamespaces(batchInfo.Pid)
for _, mount := range batchInfo.ConConfig.Spec.Mounts {
m := iopodman.ContainerMount{
@@ -85,7 +84,7 @@ func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct
return lc
}
-func makeListPodContainers(containerID string, batchInfo shared.BatchContainerStruct) iopodman.ListPodContainerInfo {
+func makeListPodContainers(containerID string, batchInfo BatchContainerStruct) iopodman.ListPodContainerInfo {
lc := iopodman.ListPodContainerInfo{
Id: containerID,
Status: batchInfo.ConState.String(),
@@ -94,10 +93,10 @@ func makeListPodContainers(containerID string, batchInfo shared.BatchContainerSt
return lc
}
-func makeListPod(pod *libpod.Pod, batchInfo shared.PsOptions) (iopodman.ListPodData, error) {
+func makeListPod(pod *libpod.Pod, batchInfo PsOptions) (iopodman.ListPodData, error) {
var listPodsContainers []iopodman.ListPodContainerInfo
var errPodData = iopodman.ListPodData{}
- status, err := shared.GetPodStatus(pod)
+ status, err := pod.GetPodStatus()
if err != nil {
return errPodData, err
}
@@ -106,7 +105,7 @@ func makeListPod(pod *libpod.Pod, batchInfo shared.PsOptions) (iopodman.ListPodD
return errPodData, err
}
for _, ctr := range containers {
- batchInfo, err := shared.BatchContainerOp(ctr, batchInfo)
+ batchInfo, err := BatchContainerOp(ctr, batchInfo)
if err != nil {
return errPodData, err
}
@@ -179,13 +178,13 @@ func derefString(in *string) string {
return *in
}
-func makePsOpts(inOpts iopodman.PsOpts) shared.PsOptions {
+func makePsOpts(inOpts iopodman.PsOpts) PsOptions {
last := 0
if inOpts.Last != nil {
lastT := *inOpts.Last
last = int(lastT)
}
- return shared.PsOptions{
+ return PsOptions{
All: inOpts.All,
Last: last,
Latest: derefBool(inOpts.Latest),
diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go
index ff72c3869..aa0eb1fb5 100644
--- a/pkg/varlinkapi/volumes.go
+++ b/pkg/varlinkapi/volumes.go
@@ -3,10 +3,11 @@
package varlinkapi
import (
+ "context"
"encoding/json"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/infra/abi/parse"
iopodman "github.com/containers/libpod/pkg/varlink"
)
@@ -24,7 +25,7 @@ func (i *VarlinkAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.Vo
volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(options.Labels))
}
if len(options.Options) > 0 {
- parsedOptions, err := shared.ParseVolumeOptions(options.Options)
+ parsedOptions, err := parse.ParseVolumeOptions(options.Options)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -39,7 +40,7 @@ func (i *VarlinkAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.Vo
// VolumeRemove removes volumes by options.All or options.Volumes
func (i *VarlinkAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.VolumeRemoveOpts) error {
- success, failed, err := shared.SharedRemoveVolumes(getContext(), i.Runtime, options.Volumes, options.All, options.Force)
+ success, failed, err := SharedRemoveVolumes(getContext(), i.Runtime, options.Volumes, options.All, options.Force)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -122,3 +123,43 @@ func (i *VarlinkAPI) VolumesPrune(call iopodman.VarlinkCall) error {
}
return call.ReplyVolumesPrune(prunedNames, prunedErrors)
}
+
+// Remove given set of volumes
+func SharedRemoveVolumes(ctx context.Context, runtime *libpod.Runtime, vols []string, all, force bool) ([]string, map[string]error, error) {
+ var (
+ toRemove []*libpod.Volume
+ success []string
+ failed map[string]error
+ )
+
+ failed = make(map[string]error)
+
+ if all {
+ vols, err := runtime.Volumes()
+ if err != nil {
+ return nil, nil, err
+ }
+ toRemove = vols
+ } else {
+ for _, v := range vols {
+ vol, err := runtime.LookupVolume(v)
+ if err != nil {
+ failed[v] = err
+ continue
+ }
+ toRemove = append(toRemove, vol)
+ }
+ }
+
+ // We could parallelize this, but I haven't heard anyone complain about
+ // performance here yet, so hold off.
+ for _, vol := range toRemove {
+ if err := runtime.RemoveVolume(ctx, vol, force); err != nil {
+ failed[vol.Name()] = err
+ continue
+ }
+ success = append(success, vol.Name())
+ }
+
+ return success, failed, nil
+}