From bb57c1631cffbb8f9e92a71ecdb27100ed07338d Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Fri, 28 Jul 2023 10:52:20 +0100 Subject: [PATCH] libpod: add 'pod top' support on FreeBSD This shares code with 'container top' which runs ps on the host, filtering for the containers that are part of the pod. (*Container).jailName is modified to take into account the possiblity that the container is in a pod - this also fixes stats reporting for pods on FreeBSD. [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/container_internal_freebsd.go | 32 +++++++++-- libpod/container_top_freebsd.go | 11 ++-- libpod/pod_top_freebsd.go | 79 ++++++++++++++++++++++++++++ libpod/pod_top_unsupported.go | 4 +- libpod/stats_freebsd.go | 9 +++- 5 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 libpod/pod_top_freebsd.go diff --git a/libpod/container_internal_freebsd.go b/libpod/container_internal_freebsd.go index 6bd872aa45..249e5af72d 100644 --- a/libpod/container_internal_freebsd.go +++ b/libpod/container_internal_freebsd.go @@ -310,11 +310,35 @@ func (c *Container) getConmonPidFd() int { return -1 } -func (c *Container) jailName() string { - if c.state.NetNS != "" { - return c.state.NetNS + "." + c.ID() +func (c *Container) jailName() (string, error) { + // If this container is in a pod, get the vnet name from the + // corresponding infra container + var ic *Container + if c.config.Pod != "" && c.config.Pod != c.ID() { + // Get the pod from state + pod, err := c.runtime.state.Pod(c.config.Pod) + if err != nil { + return "", fmt.Errorf("cannot find infra container for pod %s: %w", c.config.Pod, err) + } + ic, err = pod.InfraContainer() + if err != nil { + return "", fmt.Errorf("getting infra container for pod %s: %w", pod.ID(), err) + } + if ic.ID() != c.ID() { + ic.lock.Lock() + defer ic.lock.Unlock() + if err := ic.syncContainer(); err != nil { + return "", err + } + } } else { - return c.ID() + ic = c + } + + if ic.state.NetNS != "" { + return ic.state.NetNS + "." + c.ID(), nil + } else { + return c.ID(), nil } } diff --git a/libpod/container_top_freebsd.go b/libpod/container_top_freebsd.go index 8500e19b1c..0ca26fab40 100644 --- a/libpod/container_top_freebsd.go +++ b/libpod/container_top_freebsd.go @@ -75,13 +75,18 @@ func (c *Container) Top(descriptors []string) ([]string, error) { } } + jailName, err := c.jailName() + if err != nil { + return nil, fmt.Errorf("getting jail name: %w", err) + } + args := []string{ "-J", - c.jailName(), + jailName, } args = append(args, psDescriptors...) - output, err := c.execPS(args) + output, err := execPS(args) if err != nil { return nil, fmt.Errorf("executing ps(1): %w", err) } @@ -89,7 +94,7 @@ func (c *Container) Top(descriptors []string) ([]string, error) { return output, nil } -func (c *Container) execPS(args []string) ([]string, error) { +func execPS(args []string) ([]string, error) { cmd := exec.Command("ps", args...) stdoutPipe, err := cmd.StdoutPipe() if err != nil { diff --git a/libpod/pod_top_freebsd.go b/libpod/pod_top_freebsd.go new file mode 100644 index 0000000000..da7935bd56 --- /dev/null +++ b/libpod/pod_top_freebsd.go @@ -0,0 +1,79 @@ +//go:build freebsd +// +build freebsd + +package libpod + +import ( + "fmt" + "strings" + + "github.com/containers/podman/v4/libpod/define" +) + +// GetPodPidInformation returns process-related data of all processes in +// the pod. The output data can be controlled via the `descriptors` +// argument which expects format descriptors and supports all AIXformat +// descriptors of ps (1) plus some additional ones to for instance inspect the +// set of effective capabilities. Each element in the returned string slice +// is a tab-separated string. +// +// For more details, please refer to github.com/containers/psgo. +func (p *Pod) GetPodPidInformation(descriptors []string) ([]string, error) { + // Default to 'ps -ef' compatible descriptors + if len(strings.Join(descriptors, "")) == 0 { + descriptors = []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "args"} + } + + jailNames := make([]string, 0) + ctrsInPod, err := p.AllContainers() + if err != nil { + return nil, err + } + for _, c := range ctrsInPod { + c.lock.Lock() + err := c.syncContainer() + c.lock.Unlock() + if err != nil { + return nil, err + } + + if c.state.State == define.ContainerStateRunning { + jailName, err := c.jailName() + if err != nil { + return nil, fmt.Errorf("getting jail name: %w", err) + } + jailNames = append(jailNames, jailName) + } + } + + // Also support comma-separated input. + psDescriptors := []string{} + for _, d := range descriptors { + for _, s := range strings.Split(d, ",") { + if s != "" { + psDescriptors = append(psDescriptors, s) + } + } + } + + // For consistency with pod_top_linux.go, only allow descriptor names + for _, d := range psDescriptors { + if _, ok := isDescriptor[d]; !ok { + return nil, fmt.Errorf("unknown descriptor: %s", d) + } + } + + args := []string{ + "-J", + strings.Join(jailNames, ","), + "-ao", + strings.Join(psDescriptors, ","), + } + + output, err := execPS(args) + if err != nil { + return nil, fmt.Errorf("executing ps(1): %w", err) + } + + return output, nil +} diff --git a/libpod/pod_top_unsupported.go b/libpod/pod_top_unsupported.go index 92323043a0..193bb61386 100644 --- a/libpod/pod_top_unsupported.go +++ b/libpod/pod_top_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package libpod diff --git a/libpod/stats_freebsd.go b/libpod/stats_freebsd.go index 4ddd8098fd..14f367d8a0 100644 --- a/libpod/stats_freebsd.go +++ b/libpod/stats_freebsd.go @@ -20,9 +20,14 @@ import ( func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, previousStats *define.ContainerStats) error { now := uint64(time.Now().UnixNano()) - entries, err := rctl.GetRacct("jail:" + c.jailName()) + jailName, err := c.jailName() if err != nil { - return fmt.Errorf("unable to read accounting for %s: %w", c.jailName(), err) + return fmt.Errorf("getting jail name: %w", err) + } + + entries, err := rctl.GetRacct("jail:" + jailName) + if err != nil { + return fmt.Errorf("unable to read accounting for %s: %w", jailName, err) } // If the current total usage is less than what was previously