Files
podman/cmd/podman/pods/stats.go
Paul Holzinger 0d9f190280 shell completion --format: use structs by reference
For the AutocompleteFormat function we expect the correct template
struct which is used in the golang template. The function can handle
both struct and pointer to a struct. Using the reference is more
efficient since it doe snot have to copy the whole struct.

Also change some structs to use he actual type from the template instead
of some nested one to make sure it has to correct fields.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2022-04-28 18:12:17 +02:00

161 lines
4.5 KiB
Go

package pods
import (
"context"
"fmt"
"os"
"time"
"github.com/buger/goterm"
"github.com/containers/common/pkg/report"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/spf13/cobra"
)
type podStatsOptionsWrapper struct {
entities.PodStatsOptions
// Format - pretty-print to JSON or a go template.
Format string
// NoReset - do not reset the screen when streaming.
NoReset bool
// NoStream - do not stream stats but write them once.
NoStream bool
}
var (
statsOptions = podStatsOptionsWrapper{}
statsDescription = `Display the containers' resource-usage statistics of one or more running pod`
// Command: podman pod _pod_
statsCmd = &cobra.Command{
Use: "stats [options] [POD...]",
Short: "Display a live stream of resource usage statistics for the containers in one or more pods",
Long: statsDescription,
RunE: stats,
ValidArgsFunction: common.AutocompletePodsRunning,
Example: `podman pod stats
podman pod stats a69b23034235 named-pod
podman pod stats --latest
podman pod stats --all`,
}
)
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: statsCmd,
Parent: podCmd,
})
flags := statsCmd.Flags()
flags.BoolVarP(&statsOptions.All, "all", "a", false, "Provide stats for all pods")
formatFlagName := "format"
flags.StringVar(&statsOptions.Format, formatFlagName, "", "Pretty-print container statistics to JSON or using a Go template")
_ = statsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.PodStatsReport{}))
flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen when streaming")
flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result")
validate.AddLatestFlag(statsCmd, &statsOptions.Latest)
}
func stats(cmd *cobra.Command, args []string) error {
// Validate input.
if err := entities.ValidatePodStatsOptions(args, &statsOptions.PodStatsOptions); err != nil {
return err
}
rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush()
var err error
doJSON := report.IsJSON(statsOptions.Format)
if !doJSON {
if cmd.Flags().Changed("format") {
rpt, err = rpt.Parse(report.OriginUser, statsOptions.Format)
if err != nil {
return err
}
} else {
rpt = rpt.Init(os.Stdout, 12, 2, 2, ' ', 0)
rpt.Origin = report.OriginPodman
}
}
for ; ; time.Sleep(time.Second) {
reports, err := registry.ContainerEngine().PodStats(context.Background(), args, statsOptions.PodStatsOptions)
if err != nil {
return err
}
// Print the stats in the requested format and configuration.
if doJSON {
err = printJSONPodStats(reports)
} else {
if !statsOptions.NoReset {
goterm.Clear()
goterm.MoveCursor(1, 1)
goterm.Flush()
}
if report.OriginUser == rpt.Origin {
err = userTemplate(rpt, reports)
} else {
err = defaultTemplate(rpt, reports)
}
}
if err != nil {
return err
}
if statsOptions.NoStream {
break
}
}
return nil
}
func printJSONPodStats(stats []*entities.PodStatsReport) error {
b, err := json.MarshalIndent(&stats, "", " ")
if err != nil {
return err
}
fmt.Fprintf(os.Stdout, "%s\n", string(b))
return nil
}
func defaultTemplate(rpt *report.Formatter, stats []*entities.PodStatsReport) error {
outFormat := "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
fmt.Fprintf(rpt.Writer(), outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS")
if len(stats) == 0 {
fmt.Fprintf(rpt.Writer(), outFormat, "--", "--", "--", "--", "--", "--", "--", "--", "--")
} else {
for _, i := range stats {
fmt.Fprintf(rpt.Writer(), outFormat, i.Pod, i.CID, i.Name, i.CPU, i.MemUsage, i.Mem, i.NetIO, i.BlockIO, i.PIDS)
}
}
return rpt.Flush()
}
func userTemplate(rpt *report.Formatter, stats []*entities.PodStatsReport) error {
if len(stats) == 0 {
return nil
}
headers := report.Headers(entities.PodStatsReport{}, map[string]string{
"CPU": "CPU %",
"MemUsage": "MEM USAGE/ LIMIT",
"MemUsageBytes": "MEM USAGE/ LIMIT",
"MEM": "MEM %",
"NET IO": "NET IO",
"BlockIO": "BLOCK IO",
})
if err := rpt.Execute(headers); err != nil {
return err
}
if err := rpt.Execute(stats); err != nil {
return err
}
return rpt.Flush()
}