github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/varlinkapi/pods.go (about)

     1  // +build varlink
     2  
     3  package varlinkapi
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"strconv"
    10  	"syscall"
    11  
    12  	"github.com/containers/podman/v2/libpod"
    13  	"github.com/containers/podman/v2/libpod/define"
    14  	iopodman "github.com/containers/podman/v2/pkg/varlink"
    15  	"github.com/cri-o/ocicni/pkg/ocicni"
    16  	"github.com/docker/go-connections/nat"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  // CreatePod ...
    21  func (i *VarlinkAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error {
    22  	var options []libpod.PodCreateOption
    23  	if create.Infra {
    24  		options = append(options, libpod.WithInfraContainer())
    25  		nsOptions, err := GetNamespaceOptions(create.Share)
    26  		if err != nil {
    27  			return err
    28  		}
    29  		options = append(options, nsOptions...)
    30  	}
    31  	if create.CgroupParent != "" {
    32  		options = append(options, libpod.WithPodCgroupParent(create.CgroupParent))
    33  	}
    34  	if len(create.Labels) > 0 {
    35  		options = append(options, libpod.WithPodLabels(create.Labels))
    36  	}
    37  	if create.Name != "" {
    38  		options = append(options, libpod.WithPodName(create.Name))
    39  	}
    40  	if len(create.Share) > 0 && !create.Infra {
    41  		return call.ReplyErrorOccurred("You cannot share kernel namespaces on the pod level without an infra container")
    42  	}
    43  	if len(create.Share) == 0 && create.Infra {
    44  		return call.ReplyErrorOccurred("You must share kernel namespaces to run an infra container")
    45  	}
    46  
    47  	if len(create.Publish) > 0 {
    48  		if !create.Infra {
    49  			return call.ReplyErrorOccurred("you must have an infra container to publish port bindings to the host")
    50  		}
    51  		portBindings, err := CreatePortBindings(create.Publish)
    52  		if err != nil {
    53  			return call.ReplyErrorOccurred(err.Error())
    54  		}
    55  		options = append(options, libpod.WithInfraContainerPorts(portBindings))
    56  
    57  	}
    58  	options = append(options, libpod.WithPodCgroups())
    59  
    60  	pod, err := i.Runtime.NewPod(getContext(), options...)
    61  	if err != nil {
    62  		return call.ReplyErrorOccurred(err.Error())
    63  	}
    64  	return call.ReplyCreatePod(pod.ID())
    65  }
    66  
    67  // ListPods ...
    68  func (i *VarlinkAPI) ListPods(call iopodman.VarlinkCall) error {
    69  	var (
    70  		listPods []iopodman.ListPodData
    71  	)
    72  
    73  	pods, err := i.Runtime.GetAllPods()
    74  	if err != nil {
    75  		return call.ReplyErrorOccurred(err.Error())
    76  	}
    77  	opts := PsOptions{}
    78  	for _, pod := range pods {
    79  		listPod, err := makeListPod(pod, opts)
    80  		if err != nil {
    81  			return call.ReplyErrorOccurred(err.Error())
    82  		}
    83  		listPods = append(listPods, listPod)
    84  	}
    85  	return call.ReplyListPods(listPods)
    86  }
    87  
    88  // GetPod ...
    89  func (i *VarlinkAPI) GetPod(call iopodman.VarlinkCall, name string) error {
    90  	pod, err := i.Runtime.LookupPod(name)
    91  	if err != nil {
    92  		return call.ReplyPodNotFound(name, err.Error())
    93  	}
    94  	opts := PsOptions{}
    95  
    96  	listPod, err := makeListPod(pod, opts)
    97  	if err != nil {
    98  		return call.ReplyErrorOccurred(err.Error())
    99  	}
   100  
   101  	return call.ReplyGetPod(listPod)
   102  }
   103  
   104  // GetPodsByStatus returns a slice of pods filtered by a libpod status
   105  func (i *VarlinkAPI) GetPodsByStatus(call iopodman.VarlinkCall, statuses []string) error {
   106  	filterFuncs := func(p *libpod.Pod) bool {
   107  		state, _ := p.GetPodStatus()
   108  		for _, status := range statuses {
   109  			if state == status {
   110  				return true
   111  			}
   112  		}
   113  		return false
   114  	}
   115  	filteredPods, err := i.Runtime.Pods(filterFuncs)
   116  	if err != nil {
   117  		return call.ReplyErrorOccurred(err.Error())
   118  	}
   119  	podIDs := make([]string, 0, len(filteredPods))
   120  	for _, p := range filteredPods {
   121  		podIDs = append(podIDs, p.ID())
   122  	}
   123  	return call.ReplyGetPodsByStatus(podIDs)
   124  }
   125  
   126  // InspectPod ...
   127  func (i *VarlinkAPI) InspectPod(call iopodman.VarlinkCall, name string) error {
   128  	pod, err := i.Runtime.LookupPod(name)
   129  	if err != nil {
   130  		return call.ReplyPodNotFound(name, err.Error())
   131  	}
   132  	inspectData, err := pod.Inspect()
   133  	if err != nil {
   134  		return call.ReplyErrorOccurred(err.Error())
   135  	}
   136  	b, err := json.Marshal(&inspectData)
   137  	if err != nil {
   138  		return call.ReplyErrorOccurred("unable to serialize")
   139  	}
   140  	return call.ReplyInspectPod(string(b))
   141  }
   142  
   143  // StartPod ...
   144  func (i *VarlinkAPI) StartPod(call iopodman.VarlinkCall, name string) error {
   145  	pod, err := i.Runtime.LookupPod(name)
   146  	if err != nil {
   147  		return call.ReplyPodNotFound(name, err.Error())
   148  	}
   149  	ctnrs, err := pod.AllContainers()
   150  	if err != nil {
   151  		return call.ReplyErrorOccurred(err.Error())
   152  	}
   153  	if 0 == len(ctnrs) {
   154  		return call.ReplyNoContainersInPod(name)
   155  	}
   156  	ctrErrs, err := pod.Start(getContext())
   157  	callErr := handlePodCall(call, pod, ctrErrs, err)
   158  	if callErr != nil {
   159  		return err
   160  	}
   161  	return call.ReplyStartPod(pod.ID())
   162  }
   163  
   164  // StopPod ...
   165  func (i *VarlinkAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int64) error {
   166  	pod, err := i.Runtime.LookupPod(name)
   167  	if err != nil {
   168  		return call.ReplyPodNotFound(name, err.Error())
   169  	}
   170  	ctrErrs, err := pod.StopWithTimeout(getContext(), true, int(timeout))
   171  	callErr := handlePodCall(call, pod, ctrErrs, err)
   172  	if callErr != nil {
   173  		return err
   174  	}
   175  	return call.ReplyStopPod(pod.ID())
   176  }
   177  
   178  // RestartPod ...
   179  func (i *VarlinkAPI) RestartPod(call iopodman.VarlinkCall, name string) error {
   180  	pod, err := i.Runtime.LookupPod(name)
   181  	if err != nil {
   182  		return call.ReplyPodNotFound(name, err.Error())
   183  	}
   184  	ctnrs, err := pod.AllContainers()
   185  	if err != nil {
   186  		return call.ReplyErrorOccurred(err.Error())
   187  	}
   188  	if 0 == len(ctnrs) {
   189  		return call.ReplyNoContainersInPod(name)
   190  	}
   191  	ctrErrs, err := pod.Restart(getContext())
   192  	callErr := handlePodCall(call, pod, ctrErrs, err)
   193  	if callErr != nil {
   194  		return err
   195  	}
   196  	return call.ReplyRestartPod(pod.ID())
   197  }
   198  
   199  // KillPod kills the running containers in a pod.  If you want to use the default SIGTERM signal,
   200  // just send a -1 for the signal arg.
   201  func (i *VarlinkAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64) error {
   202  	killSignal := uint(syscall.SIGTERM)
   203  	if signal != -1 {
   204  		killSignal = uint(signal)
   205  	}
   206  
   207  	pod, err := i.Runtime.LookupPod(name)
   208  	if err != nil {
   209  		return call.ReplyPodNotFound(name, err.Error())
   210  	}
   211  	ctrErrs, err := pod.Kill(context.TODO(), killSignal)
   212  	callErr := handlePodCall(call, pod, ctrErrs, err)
   213  	if callErr != nil {
   214  		return err
   215  	}
   216  	return call.ReplyKillPod(pod.ID())
   217  }
   218  
   219  // PausePod ...
   220  func (i *VarlinkAPI) PausePod(call iopodman.VarlinkCall, name string) error {
   221  	pod, err := i.Runtime.LookupPod(name)
   222  	if err != nil {
   223  		return call.ReplyPodNotFound(name, err.Error())
   224  	}
   225  	ctrErrs, err := pod.Pause(context.TODO())
   226  	callErr := handlePodCall(call, pod, ctrErrs, err)
   227  	if callErr != nil {
   228  		return err
   229  	}
   230  	return call.ReplyPausePod(pod.ID())
   231  }
   232  
   233  // UnpausePod ...
   234  func (i *VarlinkAPI) UnpausePod(call iopodman.VarlinkCall, name string) error {
   235  	pod, err := i.Runtime.LookupPod(name)
   236  	if err != nil {
   237  		return call.ReplyPodNotFound(name, err.Error())
   238  	}
   239  	ctrErrs, err := pod.Unpause(context.TODO())
   240  	callErr := handlePodCall(call, pod, ctrErrs, err)
   241  	if callErr != nil {
   242  		return err
   243  	}
   244  	return call.ReplyUnpausePod(pod.ID())
   245  }
   246  
   247  // RemovePod ...
   248  func (i *VarlinkAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool) error {
   249  	ctx := getContext()
   250  	pod, err := i.Runtime.LookupPod(name)
   251  	if err != nil {
   252  		return call.ReplyPodNotFound(name, err.Error())
   253  	}
   254  	if err = i.Runtime.RemovePod(ctx, pod, true, force); err != nil {
   255  		return call.ReplyErrorOccurred(err.Error())
   256  	}
   257  
   258  	return call.ReplyRemovePod(pod.ID())
   259  }
   260  
   261  // GetPodStats ...
   262  func (i *VarlinkAPI) GetPodStats(call iopodman.VarlinkCall, name string) error {
   263  	pod, err := i.Runtime.LookupPod(name)
   264  	if err != nil {
   265  		return call.ReplyPodNotFound(name, err.Error())
   266  	}
   267  	prevStats := make(map[string]*define.ContainerStats)
   268  	podStats, err := pod.GetPodStats(prevStats)
   269  	if err != nil {
   270  		return call.ReplyErrorOccurred(err.Error())
   271  	}
   272  	if len(podStats) == 0 {
   273  		return call.ReplyNoContainerRunning()
   274  	}
   275  	containersStats := make([]iopodman.ContainerStats, 0)
   276  	for ctrID, containerStats := range podStats {
   277  		cs := iopodman.ContainerStats{
   278  			Id:           ctrID,
   279  			Name:         containerStats.Name,
   280  			Cpu:          containerStats.CPU,
   281  			Cpu_nano:     int64(containerStats.CPUNano),
   282  			System_nano:  int64(containerStats.SystemNano),
   283  			Mem_usage:    int64(containerStats.MemUsage),
   284  			Mem_limit:    int64(containerStats.MemLimit),
   285  			Mem_perc:     containerStats.MemPerc,
   286  			Net_input:    int64(containerStats.NetInput),
   287  			Net_output:   int64(containerStats.NetOutput),
   288  			Block_input:  int64(containerStats.BlockInput),
   289  			Block_output: int64(containerStats.BlockOutput),
   290  			Pids:         int64(containerStats.PIDs),
   291  		}
   292  		containersStats = append(containersStats, cs)
   293  	}
   294  	return call.ReplyGetPodStats(pod.ID(), containersStats)
   295  }
   296  
   297  // getPodsByContext returns a slice of pod ids based on all, latest, or a list
   298  func (i *VarlinkAPI) GetPodsByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error {
   299  	var podids []string
   300  
   301  	pods, err := getPodsByContext(all, latest, input, i.Runtime)
   302  	if err != nil {
   303  		return call.ReplyErrorOccurred(err.Error())
   304  	}
   305  	for _, p := range pods {
   306  		podids = append(podids, p.ID())
   307  	}
   308  	return call.ReplyGetPodsByContext(podids)
   309  }
   310  
   311  // PodStateData returns a container's state data in string format
   312  func (i *VarlinkAPI) PodStateData(call iopodman.VarlinkCall, name string) error {
   313  	pod, err := i.Runtime.LookupPod(name)
   314  	if err != nil {
   315  		return call.ReplyErrorOccurred(err.Error())
   316  	}
   317  	data, err := pod.Inspect()
   318  	if err != nil {
   319  		return call.ReplyErrorOccurred("unable to obtain pod state")
   320  	}
   321  	b, err := json.Marshal(data)
   322  	if err != nil {
   323  		return call.ReplyErrorOccurred("unable to serialize pod inspect data")
   324  	}
   325  	return call.ReplyPodStateData(string(b))
   326  }
   327  
   328  // TopPod provides the top stats for a given or latest pod
   329  func (i *VarlinkAPI) TopPod(call iopodman.VarlinkCall, name string, latest bool, descriptors []string) error {
   330  	var (
   331  		pod *libpod.Pod
   332  		err error
   333  	)
   334  	if latest {
   335  		name = "latest"
   336  		pod, err = i.Runtime.GetLatestPod()
   337  	} else {
   338  		pod, err = i.Runtime.LookupPod(name)
   339  	}
   340  	if err != nil {
   341  		return call.ReplyPodNotFound(name, err.Error())
   342  	}
   343  
   344  	podStatus, err := pod.GetPodStatus()
   345  	if err != nil {
   346  		return call.ReplyErrorOccurred(fmt.Sprintf("unable to get status for pod %s", pod.ID()))
   347  	}
   348  	if podStatus != "Running" {
   349  		return call.ReplyErrorOccurred("pod top can only be used on pods with at least one running container")
   350  	}
   351  	reply, err := pod.GetPodPidInformation(descriptors)
   352  	if err != nil {
   353  		return call.ReplyErrorOccurred(err.Error())
   354  	}
   355  	return call.ReplyTopPod(reply)
   356  }
   357  
   358  // CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
   359  func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) {
   360  	var portBindings []ocicni.PortMapping
   361  	// The conversion from []string to natBindings is temporary while mheon reworks the port
   362  	// deduplication code.  Eventually that step will not be required.
   363  	_, natBindings, err := nat.ParsePortSpecs(ports)
   364  	if err != nil {
   365  		return nil, err
   366  	}
   367  	for containerPb, hostPb := range natBindings {
   368  		var pm ocicni.PortMapping
   369  		pm.ContainerPort = int32(containerPb.Int())
   370  		for _, i := range hostPb {
   371  			var hostPort int
   372  			var err error
   373  			pm.HostIP = i.HostIP
   374  			if i.HostPort == "" {
   375  				hostPort = containerPb.Int()
   376  			} else {
   377  				hostPort, err = strconv.Atoi(i.HostPort)
   378  				if err != nil {
   379  					return nil, errors.Wrapf(err, "unable to convert host port to integer")
   380  				}
   381  			}
   382  
   383  			pm.HostPort = int32(hostPort)
   384  			pm.Protocol = containerPb.Proto()
   385  			portBindings = append(portBindings, pm)
   386  		}
   387  	}
   388  	return portBindings, nil
   389  }