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