github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/varlinkapi/containers.go (about) 1 // +build varlink 2 3 package varlinkapi 4 5 import ( 6 "bufio" 7 "encoding/json" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "strings" 13 "sync" 14 "syscall" 15 "time" 16 17 "github.com/containers/podman/v2/libpod" 18 "github.com/containers/podman/v2/libpod/define" 19 "github.com/containers/podman/v2/libpod/logs" 20 "github.com/containers/podman/v2/pkg/cgroups" 21 "github.com/containers/podman/v2/pkg/rootless" 22 iopodman "github.com/containers/podman/v2/pkg/varlink" 23 "github.com/containers/podman/v2/pkg/varlinkapi/virtwriter" 24 "github.com/containers/storage/pkg/archive" 25 "github.com/pkg/errors" 26 "github.com/sirupsen/logrus" 27 "k8s.io/client-go/tools/remotecommand" 28 ) 29 30 // ListContainers ... 31 func (i *VarlinkAPI) ListContainers(call iopodman.VarlinkCall) error { 32 var ( 33 listContainers []iopodman.Container 34 ) 35 36 containers, err := i.Runtime.GetAllContainers() 37 if err != nil { 38 return call.ReplyErrorOccurred(err.Error()) 39 } 40 opts := PsOptions{ 41 Namespace: true, 42 Size: true, 43 } 44 for _, ctr := range containers { 45 batchInfo, err := BatchContainerOp(ctr, opts) 46 if err != nil { 47 return call.ReplyErrorOccurred(err.Error()) 48 } 49 50 listContainers = append(listContainers, makeListContainer(ctr.ID(), batchInfo)) 51 } 52 return call.ReplyListContainers(listContainers) 53 } 54 55 func (i *VarlinkAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error { 56 var ( 57 containers []iopodman.PsContainer 58 ) 59 maxWorkers := Parallelize("ps") 60 psOpts := makePsOpts(opts) 61 filters := []string{} 62 if opts.Filters != nil { 63 filters = *opts.Filters 64 } 65 psContainerOutputs, err := GetPsContainerOutput(i.Runtime, psOpts, filters, maxWorkers) 66 if err != nil { 67 return call.ReplyErrorOccurred(err.Error()) 68 } 69 70 for _, ctr := range psContainerOutputs { 71 container := iopodman.PsContainer{ 72 Id: ctr.ID, 73 Image: ctr.Image, 74 Command: ctr.Command, 75 Created: ctr.Created, 76 Ports: ctr.Ports, 77 Names: ctr.Names, 78 IsInfra: ctr.IsInfra, 79 Status: ctr.Status, 80 State: ctr.State.String(), 81 PidNum: int64(ctr.Pid), 82 Pod: ctr.Pod, 83 CreatedAt: ctr.CreatedAt.Format(time.RFC3339Nano), 84 ExitedAt: ctr.ExitedAt.Format(time.RFC3339Nano), 85 StartedAt: ctr.StartedAt.Format(time.RFC3339Nano), 86 Labels: ctr.Labels, 87 NsPid: ctr.PID, 88 Cgroup: ctr.Cgroup, 89 Ipc: ctr.Cgroup, 90 Mnt: ctr.MNT, 91 Net: ctr.NET, 92 PidNs: ctr.PIDNS, 93 User: ctr.User, 94 Uts: ctr.UTS, 95 Mounts: ctr.Mounts, 96 } 97 if ctr.Size != nil { 98 container.RootFsSize = ctr.Size.RootFsSize 99 container.RwSize = ctr.Size.RwSize 100 } 101 containers = append(containers, container) 102 } 103 return call.ReplyPs(containers) 104 } 105 106 // GetContainer ... 107 func (i *VarlinkAPI) GetContainer(call iopodman.VarlinkCall, id string) error { 108 ctr, err := i.Runtime.LookupContainer(id) 109 if err != nil { 110 return call.ReplyContainerNotFound(id, err.Error()) 111 } 112 opts := PsOptions{ 113 Namespace: true, 114 Size: true, 115 } 116 batchInfo, err := BatchContainerOp(ctr, opts) 117 if err != nil { 118 return call.ReplyErrorOccurred(err.Error()) 119 } 120 return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo)) 121 } 122 123 // getContainersByContext returns a slice of container ids based on all, latest, or a list 124 func (i *VarlinkAPI) GetContainersByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error { 125 var ids []string 126 127 ctrs, err := getContainersByContext(all, latest, input, i.Runtime) 128 if err != nil { 129 if errors.Cause(err) == define.ErrNoSuchCtr { 130 return call.ReplyContainerNotFound("", err.Error()) 131 } 132 return call.ReplyErrorOccurred(err.Error()) 133 } 134 135 for _, c := range ctrs { 136 ids = append(ids, c.ID()) 137 } 138 return call.ReplyGetContainersByContext(ids) 139 } 140 141 // GetContainersByStatus returns a slice of containers filtered by a libpod status 142 func (i *VarlinkAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses []string) error { 143 var ( 144 filterFuncs []libpod.ContainerFilter 145 containers []iopodman.Container 146 ) 147 for _, status := range statuses { 148 lpstatus, err := define.StringToContainerStatus(status) 149 if err != nil { 150 return call.ReplyErrorOccurred(err.Error()) 151 } 152 filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { 153 state, _ := c.State() 154 return state == lpstatus 155 }) 156 } 157 filteredContainers, err := i.Runtime.GetContainers(filterFuncs...) 158 if err != nil { 159 return call.ReplyErrorOccurred(err.Error()) 160 } 161 opts := PsOptions{Size: true, Namespace: true} 162 for _, ctr := range filteredContainers { 163 batchInfo, err := BatchContainerOp(ctr, opts) 164 if err != nil { 165 return call.ReplyErrorOccurred(err.Error()) 166 } 167 containers = append(containers, makeListContainer(ctr.ID(), batchInfo)) 168 } 169 return call.ReplyGetContainersByStatus(containers) 170 } 171 172 // InspectContainer ... 173 func (i *VarlinkAPI) InspectContainer(call iopodman.VarlinkCall, name string) error { 174 ctr, err := i.Runtime.LookupContainer(name) 175 if err != nil { 176 return call.ReplyContainerNotFound(name, err.Error()) 177 } 178 data, err := ctr.Inspect(true) 179 if err != nil { 180 return call.ReplyErrorOccurred(err.Error()) 181 } 182 b, err := json.Marshal(data) 183 if err != nil { 184 return call.ReplyErrorOccurred(fmt.Sprintf("unable to serialize")) 185 } 186 return call.ReplyInspectContainer(string(b)) 187 } 188 189 // ListContainerProcesses ... 190 func (i *VarlinkAPI) ListContainerProcesses(call iopodman.VarlinkCall, name string, opts []string) error { 191 ctr, err := i.Runtime.LookupContainer(name) 192 if err != nil { 193 return call.ReplyContainerNotFound(name, err.Error()) 194 } 195 containerState, err := ctr.State() 196 if err != nil { 197 return call.ReplyErrorOccurred(err.Error()) 198 } 199 if containerState != define.ContainerStateRunning { 200 return call.ReplyErrorOccurred(fmt.Sprintf("container %s is not running", name)) 201 } 202 var psArgs []string 203 psOpts := []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "comm"} 204 if len(opts) > 1 { 205 psOpts = opts 206 } 207 psArgs = append(psArgs, psOpts...) 208 psOutput, err := ctr.GetContainerPidInformation(psArgs) 209 if err != nil { 210 return call.ReplyErrorOccurred(err.Error()) 211 } 212 213 return call.ReplyListContainerProcesses(psOutput) 214 } 215 216 // GetContainerLogs ... 217 func (i *VarlinkAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) error { 218 var logs []string 219 ctr, err := i.Runtime.LookupContainer(name) 220 if err != nil { 221 return call.ReplyContainerNotFound(name, err.Error()) 222 } 223 logPath := ctr.LogPath() 224 225 containerState, err := ctr.State() 226 if err != nil { 227 return call.ReplyErrorOccurred(err.Error()) 228 } 229 if _, err := os.Stat(logPath); err != nil { 230 if containerState == define.ContainerStateConfigured { 231 return call.ReplyGetContainerLogs(logs) 232 } 233 } 234 file, err := os.Open(logPath) 235 if err != nil { 236 return errors.Wrapf(err, "unable to read container log file") 237 } 238 defer file.Close() 239 reader := bufio.NewReader(file) 240 if call.WantsMore() { 241 call.Continues = true 242 } 243 for { 244 line, err := reader.ReadString('\n') 245 // We've read the entire file 246 if err == io.EOF { 247 if !call.WantsMore() { 248 // If this is a non-following log request, we return what we have 249 break 250 } else { 251 // If we want to follow, return what we have, wipe the slice, and make 252 // sure the container is still running before iterating. 253 call.ReplyGetContainerLogs(logs) 254 logs = []string{} 255 time.Sleep(1 * time.Second) 256 state, err := ctr.State() 257 if err != nil { 258 return call.ReplyErrorOccurred(err.Error()) 259 } 260 if state != define.ContainerStateRunning && state != define.ContainerStatePaused { 261 return call.ReplyErrorOccurred(fmt.Sprintf("%s is no longer running", ctr.ID())) 262 } 263 264 } 265 } else if err != nil { 266 return call.ReplyErrorOccurred(err.Error()) 267 } else { 268 logs = append(logs, line) 269 } 270 } 271 272 call.Continues = false 273 274 return call.ReplyGetContainerLogs(logs) 275 } 276 277 // ListContainerChanges ... 278 func (i *VarlinkAPI) ListContainerChanges(call iopodman.VarlinkCall, name string) error { 279 changes, err := i.Runtime.GetDiff("", name) 280 if err != nil { 281 return call.ReplyErrorOccurred(err.Error()) 282 } 283 result := iopodman.ContainerChanges{} 284 for _, change := range changes { 285 switch change.Kind { 286 case archive.ChangeModify: 287 result.Changed = append(result.Changed, change.Path) 288 case archive.ChangeDelete: 289 result.Deleted = append(result.Deleted, change.Path) 290 case archive.ChangeAdd: 291 result.Added = append(result.Added, change.Path) 292 } 293 } 294 return call.ReplyListContainerChanges(result) 295 } 296 297 // ExportContainer ... 298 func (i *VarlinkAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath string) error { 299 ctr, err := i.Runtime.LookupContainer(name) 300 if err != nil { 301 return call.ReplyContainerNotFound(name, err.Error()) 302 } 303 outputFile, err := ioutil.TempFile("", "varlink_recv") 304 if err != nil { 305 return call.ReplyErrorOccurred(err.Error()) 306 } 307 308 defer outputFile.Close() 309 if outPath == "" { 310 outPath = outputFile.Name() 311 } 312 if err := ctr.Export(outPath); err != nil { 313 return call.ReplyErrorOccurred(err.Error()) 314 } 315 return call.ReplyExportContainer(outPath) 316 317 } 318 319 // GetContainerStats ... 320 func (i *VarlinkAPI) GetContainerStats(call iopodman.VarlinkCall, name string) error { 321 if rootless.IsRootless() { 322 cgroupv2, err := cgroups.IsCgroup2UnifiedMode() 323 if err != nil { 324 return call.ReplyErrorOccurred(err.Error()) 325 } 326 if !cgroupv2 { 327 return call.ReplyErrRequiresCgroupsV2ForRootless("rootless containers cannot report container stats") 328 } 329 } 330 ctr, err := i.Runtime.LookupContainer(name) 331 if err != nil { 332 return call.ReplyContainerNotFound(name, err.Error()) 333 } 334 containerStats, err := ctr.GetContainerStats(&define.ContainerStats{}) 335 if err != nil { 336 if errors.Cause(err) == define.ErrCtrStateInvalid { 337 return call.ReplyNoContainerRunning() 338 } 339 return call.ReplyErrorOccurred(err.Error()) 340 } 341 cs := iopodman.ContainerStats{ 342 Id: ctr.ID(), 343 Name: ctr.Name(), 344 Cpu: containerStats.CPU, 345 Cpu_nano: int64(containerStats.CPUNano), 346 System_nano: int64(containerStats.SystemNano), 347 Mem_usage: int64(containerStats.MemUsage), 348 Mem_limit: int64(containerStats.MemLimit), 349 Mem_perc: containerStats.MemPerc, 350 Net_input: int64(containerStats.NetInput), 351 Net_output: int64(containerStats.NetOutput), 352 Block_input: int64(containerStats.BlockInput), 353 Block_output: int64(containerStats.BlockOutput), 354 Pids: int64(containerStats.PIDs), 355 } 356 return call.ReplyGetContainerStats(cs) 357 } 358 359 // StartContainer ... 360 func (i *VarlinkAPI) StartContainer(call iopodman.VarlinkCall, name string) error { 361 ctr, err := i.Runtime.LookupContainer(name) 362 if err != nil { 363 return call.ReplyContainerNotFound(name, err.Error()) 364 } 365 state, err := ctr.State() 366 if err != nil { 367 return call.ReplyErrorOccurred(err.Error()) 368 } 369 if state == define.ContainerStateRunning || state == define.ContainerStatePaused { 370 return call.ReplyErrorOccurred("container is already running or paused") 371 } 372 recursive := false 373 if ctr.PodID() != "" { 374 recursive = true 375 } 376 if err := ctr.Start(getContext(), recursive); err != nil { 377 return call.ReplyErrorOccurred(err.Error()) 378 } 379 return call.ReplyStartContainer(ctr.ID()) 380 } 381 382 // InitContainer initializes the container given by Varlink. 383 func (i *VarlinkAPI) InitContainer(call iopodman.VarlinkCall, name string) error { 384 ctr, err := i.Runtime.LookupContainer(name) 385 if err != nil { 386 return call.ReplyContainerNotFound(name, err.Error()) 387 } 388 if err := ctr.Init(getContext(), false); err != nil { 389 if errors.Cause(err) == define.ErrCtrStateInvalid { 390 return call.ReplyInvalidState(ctr.ID(), err.Error()) 391 } 392 return call.ReplyErrorOccurred(err.Error()) 393 } 394 return call.ReplyInitContainer(ctr.ID()) 395 } 396 397 // StopContainer ... 398 func (i *VarlinkAPI) StopContainer(call iopodman.VarlinkCall, name string, timeout int64) error { 399 ctr, err := i.Runtime.LookupContainer(name) 400 if err != nil { 401 return call.ReplyContainerNotFound(name, err.Error()) 402 } 403 if err := ctr.StopWithTimeout(uint(timeout)); err != nil { 404 if errors.Cause(err) == define.ErrCtrStopped { 405 return call.ReplyErrCtrStopped(ctr.ID()) 406 } 407 if errors.Cause(err) == define.ErrCtrStateInvalid { 408 return call.ReplyInvalidState(ctr.ID(), err.Error()) 409 } 410 return call.ReplyErrorOccurred(err.Error()) 411 } 412 return call.ReplyStopContainer(ctr.ID()) 413 } 414 415 // RestartContainer ... 416 func (i *VarlinkAPI) RestartContainer(call iopodman.VarlinkCall, name string, timeout int64) error { 417 ctr, err := i.Runtime.LookupContainer(name) 418 if err != nil { 419 return call.ReplyContainerNotFound(name, err.Error()) 420 } 421 if err := ctr.RestartWithTimeout(getContext(), uint(timeout)); err != nil { 422 return call.ReplyErrorOccurred(err.Error()) 423 } 424 return call.ReplyRestartContainer(ctr.ID()) 425 } 426 427 // ContainerExists looks in local storage for the existence of a container 428 func (i *VarlinkAPI) ContainerExists(call iopodman.VarlinkCall, name string) error { 429 _, err := i.Runtime.LookupContainer(name) 430 if errors.Cause(err) == define.ErrNoSuchCtr { 431 return call.ReplyContainerExists(1) 432 } 433 if err != nil { 434 return call.ReplyErrorOccurred(err.Error()) 435 } 436 return call.ReplyContainerExists(0) 437 } 438 439 // KillContainer kills a running container. If you want to use the default SIGTERM signal, just send a -1 440 // for the signal arg. 441 func (i *VarlinkAPI) KillContainer(call iopodman.VarlinkCall, name string, signal int64) error { 442 killSignal := uint(syscall.SIGTERM) 443 if signal != -1 { 444 killSignal = uint(signal) 445 } 446 ctr, err := i.Runtime.LookupContainer(name) 447 if err != nil { 448 return call.ReplyContainerNotFound(name, err.Error()) 449 } 450 if err := ctr.Kill(killSignal); err != nil { 451 return call.ReplyErrorOccurred(err.Error()) 452 } 453 return call.ReplyKillContainer(ctr.ID()) 454 } 455 456 // PauseContainer ... 457 func (i *VarlinkAPI) PauseContainer(call iopodman.VarlinkCall, name string) error { 458 ctr, err := i.Runtime.LookupContainer(name) 459 if err != nil { 460 return call.ReplyContainerNotFound(name, err.Error()) 461 } 462 if err := ctr.Pause(); err != nil { 463 return call.ReplyErrorOccurred(err.Error()) 464 } 465 return call.ReplyPauseContainer(ctr.ID()) 466 } 467 468 // UnpauseContainer ... 469 func (i *VarlinkAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) error { 470 ctr, err := i.Runtime.LookupContainer(name) 471 if err != nil { 472 return call.ReplyContainerNotFound(name, err.Error()) 473 } 474 if err := ctr.Unpause(); err != nil { 475 return call.ReplyErrorOccurred(err.Error()) 476 } 477 return call.ReplyUnpauseContainer(ctr.ID()) 478 } 479 480 // WaitContainer ... 481 func (i *VarlinkAPI) WaitContainer(call iopodman.VarlinkCall, name string, interval int64) error { 482 ctr, err := i.Runtime.LookupContainer(name) 483 if err != nil { 484 return call.ReplyContainerNotFound(name, err.Error()) 485 } 486 exitCode, err := ctr.WaitWithInterval(time.Duration(interval)) 487 if err != nil { 488 return call.ReplyErrorOccurred(err.Error()) 489 } 490 return call.ReplyWaitContainer(int64(exitCode)) 491 } 492 493 // RemoveContainer ... 494 func (i *VarlinkAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool, removeVolumes bool) error { 495 ctx := getContext() 496 ctr, err := i.Runtime.LookupContainer(name) 497 if err != nil { 498 return call.ReplyContainerNotFound(name, err.Error()) 499 } 500 if err := i.Runtime.RemoveContainer(ctx, ctr, force, removeVolumes); err != nil { 501 if errors.Cause(err) == define.ErrNoSuchCtr { 502 return call.ReplyContainerExists(1) 503 } 504 if errors.Cause(err) == define.ErrCtrStateInvalid { 505 return call.ReplyInvalidState(ctr.ID(), err.Error()) 506 } 507 return call.ReplyErrorOccurred(err.Error()) 508 } 509 return call.ReplyRemoveContainer(ctr.ID()) 510 } 511 512 // EvictContainer ... 513 func (i *VarlinkAPI) EvictContainer(call iopodman.VarlinkCall, name string, removeVolumes bool) error { 514 ctx := getContext() 515 id, err := i.Runtime.EvictContainer(ctx, name, removeVolumes) 516 if err != nil { 517 return call.ReplyErrorOccurred(err.Error()) 518 } 519 return call.ReplyEvictContainer(id) 520 } 521 522 // DeleteStoppedContainers ... 523 func (i *VarlinkAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error { 524 ctx := getContext() 525 var deletedContainers []string 526 containers, err := i.Runtime.GetAllContainers() 527 if err != nil { 528 return call.ReplyErrorOccurred(err.Error()) 529 } 530 for _, ctr := range containers { 531 state, err := ctr.State() 532 if err != nil { 533 return call.ReplyErrorOccurred(err.Error()) 534 } 535 if state != define.ContainerStateRunning { 536 if err := i.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil { 537 return call.ReplyErrorOccurred(err.Error()) 538 } 539 deletedContainers = append(deletedContainers, ctr.ID()) 540 } 541 } 542 return call.ReplyDeleteStoppedContainers(deletedContainers) 543 } 544 545 // GetAttachSockets ... 546 func (i *VarlinkAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) error { 547 ctr, err := i.Runtime.LookupContainer(name) 548 if err != nil { 549 return call.ReplyContainerNotFound(name, err.Error()) 550 } 551 552 status, err := ctr.State() 553 if err != nil { 554 return call.ReplyErrorOccurred(err.Error()) 555 } 556 557 // If the container hasn't been run, we need to run init 558 // so the conmon sockets get created. 559 if status == define.ContainerStateConfigured || status == define.ContainerStateStopped { 560 if err := ctr.Init(getContext(), false); err != nil { 561 return call.ReplyErrorOccurred(err.Error()) 562 } 563 } 564 565 sockPath, err := ctr.AttachSocketPath() 566 if err != nil { 567 return call.ReplyErrorOccurred(err.Error()) 568 } 569 570 s := iopodman.Sockets{ 571 Container_id: ctr.ID(), 572 Io_socket: sockPath, 573 Control_socket: ctr.ControlSocketPath(), 574 } 575 return call.ReplyGetAttachSockets(s) 576 } 577 578 // ContainerCheckpoint ... 579 func (i *VarlinkAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string, keep, leaveRunning, tcpEstablished bool) error { 580 ctx := getContext() 581 ctr, err := i.Runtime.LookupContainer(name) 582 if err != nil { 583 return call.ReplyContainerNotFound(name, err.Error()) 584 } 585 586 options := libpod.ContainerCheckpointOptions{ 587 Keep: keep, 588 TCPEstablished: tcpEstablished, 589 KeepRunning: leaveRunning, 590 } 591 if err := ctr.Checkpoint(ctx, options); err != nil { 592 return call.ReplyErrorOccurred(err.Error()) 593 } 594 return call.ReplyContainerCheckpoint(ctr.ID()) 595 } 596 597 // ContainerRestore ... 598 func (i *VarlinkAPI) ContainerRestore(call iopodman.VarlinkCall, name string, keep, tcpEstablished bool) error { 599 ctx := getContext() 600 ctr, err := i.Runtime.LookupContainer(name) 601 if err != nil { 602 return call.ReplyContainerNotFound(name, err.Error()) 603 } 604 605 options := libpod.ContainerCheckpointOptions{ 606 Keep: keep, 607 TCPEstablished: tcpEstablished, 608 } 609 if err := ctr.Restore(ctx, options); err != nil { 610 return call.ReplyErrorOccurred(err.Error()) 611 } 612 return call.ReplyContainerRestore(ctr.ID()) 613 } 614 615 // ContainerConfig returns just the container.config struct 616 func (i *VarlinkAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error { 617 ctr, err := i.Runtime.LookupContainer(name) 618 if err != nil { 619 return call.ReplyContainerNotFound(name, err.Error()) 620 } 621 config := ctr.Config() 622 b, err := json.Marshal(config) 623 if err != nil { 624 return call.ReplyErrorOccurred("unable to serialize container config") 625 } 626 return call.ReplyContainerConfig(string(b)) 627 } 628 629 // ContainerArtifacts returns an untouched container's artifact in string format 630 func (i *VarlinkAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifactName string) error { 631 ctr, err := i.Runtime.LookupContainer(name) 632 if err != nil { 633 return call.ReplyContainerNotFound(name, err.Error()) 634 } 635 artifacts, err := ctr.GetArtifact(artifactName) 636 if err != nil { 637 return call.ReplyErrorOccurred("unable to get container artifacts") 638 } 639 b, err := json.Marshal(artifacts) 640 if err != nil { 641 return call.ReplyErrorOccurred("unable to serialize container artifacts") 642 } 643 return call.ReplyContainerArtifacts(string(b)) 644 } 645 646 // ContainerInspectData returns the inspect data of a container in string format 647 func (i *VarlinkAPI) ContainerInspectData(call iopodman.VarlinkCall, name string, size bool) error { 648 ctr, err := i.Runtime.LookupContainer(name) 649 if err != nil { 650 return call.ReplyContainerNotFound(name, err.Error()) 651 } 652 data, err := ctr.Inspect(size) 653 if err != nil { 654 return call.ReplyErrorOccurred("unable to inspect container") 655 } 656 b, err := json.Marshal(data) 657 if err != nil { 658 return call.ReplyErrorOccurred("unable to serialize container inspect data") 659 } 660 return call.ReplyContainerInspectData(string(b)) 661 662 } 663 664 // ContainerStateData returns a container's state data in string format 665 func (i *VarlinkAPI) ContainerStateData(call iopodman.VarlinkCall, name string) error { 666 ctr, err := i.Runtime.LookupContainer(name) 667 if err != nil { 668 return call.ReplyContainerNotFound(name, err.Error()) 669 } 670 data, err := ctr.ContainerState() 671 if err != nil { 672 return call.ReplyErrorOccurred("unable to obtain container state") 673 } 674 b, err := json.Marshal(data) 675 if err != nil { 676 return call.ReplyErrorOccurred("unable to serialize container inspect data") 677 } 678 return call.ReplyContainerStateData(string(b)) 679 } 680 681 // GetContainerStatsWithHistory is a varlink endpoint that returns container stats based on current and 682 // previous statistics 683 func (i *VarlinkAPI) GetContainerStatsWithHistory(call iopodman.VarlinkCall, prevStats iopodman.ContainerStats) error { 684 con, err := i.Runtime.LookupContainer(prevStats.Id) 685 if err != nil { 686 return call.ReplyContainerNotFound(prevStats.Id, err.Error()) 687 } 688 previousStats := ContainerStatsToLibpodContainerStats(prevStats) 689 stats, err := con.GetContainerStats(&previousStats) 690 if err != nil { 691 return call.ReplyErrorOccurred(err.Error()) 692 } 693 cStats := iopodman.ContainerStats{ 694 Id: stats.ContainerID, 695 Name: stats.Name, 696 Cpu: stats.CPU, 697 Cpu_nano: int64(stats.CPUNano), 698 System_nano: int64(stats.SystemNano), 699 Mem_usage: int64(stats.MemUsage), 700 Mem_limit: int64(stats.MemLimit), 701 Mem_perc: stats.MemPerc, 702 Net_input: int64(stats.NetInput), 703 Net_output: int64(stats.NetOutput), 704 Block_input: int64(stats.BlockInput), 705 Block_output: int64(stats.BlockOutput), 706 Pids: int64(stats.PIDs), 707 } 708 return call.ReplyGetContainerStatsWithHistory(cStats) 709 } 710 711 // Spec ... 712 func (i *VarlinkAPI) Spec(call iopodman.VarlinkCall, name string) error { 713 ctr, err := i.Runtime.LookupContainer(name) 714 if err != nil { 715 return call.ReplyErrorOccurred(err.Error()) 716 } 717 718 spec := ctr.Spec() 719 b, err := json.Marshal(spec) 720 if err != nil { 721 return call.ReplyErrorOccurred(err.Error()) 722 } 723 724 return call.ReplySpec(string(b)) 725 } 726 727 // GetContainersLogs is the varlink endpoint to obtain one or more container logs 728 func (i *VarlinkAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, follow, latest bool, since string, tail int64, timestamps bool) error { 729 var wg sync.WaitGroup 730 if call.WantsMore() { 731 call.Continues = true 732 } 733 sinceTime, err := time.Parse(time.RFC3339Nano, since) 734 if err != nil { 735 return call.ReplyErrorOccurred(err.Error()) 736 } 737 options := logs.LogOptions{ 738 Follow: follow, 739 Since: sinceTime, 740 Tail: tail, 741 Timestamps: timestamps, 742 } 743 744 options.WaitGroup = &wg 745 if len(names) > 1 { 746 options.Multi = true 747 } 748 tailLen := int(tail) 749 if tailLen < 0 { 750 tailLen = 0 751 } 752 logChannel := make(chan *logs.LogLine, tailLen*len(names)+1) 753 containers, err := getContainersByContext(false, latest, names, i.Runtime) 754 if err != nil { 755 return call.ReplyErrorOccurred(err.Error()) 756 } 757 if err := i.Runtime.Log(getContext(), containers, &options, logChannel); err != nil { 758 return err 759 } 760 go func() { 761 wg.Wait() 762 close(logChannel) 763 }() 764 for line := range logChannel { 765 call.ReplyGetContainersLogs(newPodmanLogLine(line)) 766 if !call.Continues { 767 break 768 } 769 770 } 771 return call.ReplyGetContainersLogs(iopodman.LogLine{}) 772 } 773 774 func newPodmanLogLine(line *logs.LogLine) iopodman.LogLine { 775 return iopodman.LogLine{ 776 Device: line.Device, 777 ParseLogType: line.ParseLogType, 778 Time: line.Time.Format(time.RFC3339Nano), 779 Msg: line.Msg, 780 Cid: line.CID, 781 } 782 } 783 784 // Top displays information about a container's running processes 785 func (i *VarlinkAPI) Top(call iopodman.VarlinkCall, nameOrID string, descriptors []string) error { 786 ctr, err := i.Runtime.LookupContainer(nameOrID) 787 if err != nil { 788 return call.ReplyContainerNotFound(ctr.ID(), err.Error()) 789 } 790 topInfo, err := ctr.Top(descriptors) 791 if err != nil { 792 return call.ReplyErrorOccurred(err.Error()) 793 } 794 return call.ReplyTop(topInfo) 795 } 796 797 // ExecContainer is the varlink endpoint to execute a command in a container 798 func (i *VarlinkAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecOpts) error { 799 if !call.WantsUpgrade() { 800 return call.ReplyErrorOccurred("client must use upgraded connection to exec") 801 } 802 803 ctr, err := i.Runtime.LookupContainer(opts.Name) 804 if err != nil { 805 return call.ReplyContainerNotFound(opts.Name, err.Error()) 806 } 807 808 state, err := ctr.State() 809 if err != nil { 810 return call.ReplyErrorOccurred( 811 fmt.Sprintf("exec failed to obtain container %s state: %s", ctr.ID(), err.Error())) 812 } 813 814 if state != define.ContainerStateRunning { 815 return call.ReplyErrorOccurred( 816 fmt.Sprintf("exec requires a running container, %s is %s", ctr.ID(), state.String())) 817 } 818 819 // ACK the client upgrade request 820 call.ReplyExecContainer() 821 822 envs := make(map[string]string) 823 if opts.Env != nil { 824 // HACK: The Varlink API uses the old []string format for env, 825 // storage as "k=v". Split on the = and turn into the new map 826 // format. 827 for _, env := range *opts.Env { 828 splitEnv := strings.SplitN(env, "=", 2) 829 if len(splitEnv) == 1 { 830 logrus.Errorf("Got badly-formatted environment variable %q in exec", env) 831 continue 832 } 833 envs[splitEnv[0]] = splitEnv[1] 834 } 835 } 836 837 var user string 838 if opts.User != nil { 839 user = *opts.User 840 } 841 842 var workDir string 843 if opts.Workdir != nil { 844 workDir = *opts.Workdir 845 } 846 847 resizeChan := make(chan remotecommand.TerminalSize) 848 849 reader, writer, _, pipeWriter, streams := setupStreams(call) 850 851 type ExitCodeError struct { 852 ExitCode uint32 853 Error error 854 } 855 ecErrChan := make(chan ExitCodeError, 1) 856 857 go func() { 858 if err := virtwriter.Reader(reader, nil, nil, pipeWriter, resizeChan, nil); err != nil { 859 ecErrChan <- ExitCodeError{ 860 define.ExecErrorCodeGeneric, 861 err, 862 } 863 } 864 }() 865 866 execConfig := new(libpod.ExecConfig) 867 execConfig.Command = opts.Cmd 868 execConfig.Terminal = opts.Tty 869 execConfig.Privileged = opts.Privileged 870 execConfig.Environment = envs 871 execConfig.User = user 872 execConfig.WorkDir = workDir 873 execConfig.DetachKeys = opts.DetachKeys 874 875 go func() { 876 ec, err := ctr.Exec(execConfig, streams, resizeChan) 877 if err != nil { 878 logrus.Errorf(err.Error()) 879 } 880 ecErrChan <- ExitCodeError{ 881 uint32(ec), 882 err, 883 } 884 }() 885 886 ecErr := <-ecErrChan 887 888 exitCode := define.TranslateExecErrorToExitCode(int(ecErr.ExitCode), ecErr.Error) 889 890 if err = virtwriter.HangUp(writer, uint32(exitCode)); err != nil { 891 logrus.Errorf("ExecContainer failed to HANG-UP on %s: %s", ctr.ID(), err.Error()) 892 } 893 894 if err := call.Writer.Flush(); err != nil { 895 logrus.Errorf("Exec Container err: %s", err.Error()) 896 } 897 898 return ecErr.Error 899 } 900 901 // HealthCheckRun executes defined container's healthcheck command and returns the container's health status. 902 func (i *VarlinkAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error { 903 hcStatus, err := i.Runtime.HealthCheck(nameOrID) 904 if err != nil && hcStatus != define.HealthCheckFailure { 905 return call.ReplyErrorOccurred(err.Error()) 906 } 907 status := define.HealthCheckUnhealthy 908 if hcStatus == define.HealthCheckSuccess { 909 status = define.HealthCheckHealthy 910 } 911 return call.ReplyHealthCheckRun(status) 912 }