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 }