diff options
Diffstat (limited to 'pkg/domain')
-rw-r--r-- | pkg/domain/entities/engine_container.go | 1 | ||||
-rw-r--r-- | pkg/domain/entities/pods.go | 48 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 17 | ||||
-rw-r--r-- | pkg/domain/infra/abi/pods_stats.go | 85 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/pods.go | 4 |
6 files changed, 156 insertions, 3 deletions
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 8c5bc3058..502279bcf 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -53,6 +53,7 @@ type ContainerEngine interface { PodRestart(ctx context.Context, namesOrIds []string, options PodRestartOptions) ([]*PodRestartReport, error) PodRm(ctx context.Context, namesOrIds []string, options PodRmOptions) ([]*PodRmReport, error) PodStart(ctx context.Context, namesOrIds []string, options PodStartOptions) ([]*PodStartReport, error) + PodStats(ctx context.Context, namesOrIds []string, options PodStatsOptions) ([]*PodStatsReport, error) PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error) PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error) PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error) diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index aa1445a6a..a4896ce4d 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -1,6 +1,7 @@ package entities import ( + "errors" "strings" "time" @@ -188,3 +189,50 @@ type PodInspectOptions struct { type PodInspectReport struct { *define.InspectPodData } + +// PodStatsOptions are options for the pod stats command. +type PodStatsOptions struct { + // All - provide stats for all running pods. + All bool + // Latest - provide stats for the latest pod. + Latest bool +} + +// PodStatsReport includes pod-resource statistics data. +type PodStatsReport struct { + CPU string + MemUsage string + Mem string + NetIO string + BlockIO string + PIDS string + Pod string + CID string + Name string +} + +// ValidatePodStatsOptions validates the specified slice and options. Allows +// for sharing code in the front- and the back-end. +func ValidatePodStatsOptions(args []string, options *PodStatsOptions) error { + num := 0 + if len(args) > 0 { + num++ + } + if options.All { + num++ + } + if options.Latest { + num++ + } + switch num { + case 0: + // Podman v1 compat: if nothing's specified get all running + // pods. + options.All = true + return nil + case 1: + return nil + default: + return errors.New("--all, --latest and arguments cannot be used together") + } +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 9844d1d96..e09891760 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -217,12 +217,23 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { var ( + ctrs []*libpod.Container + err error reports []*entities.RestartReport ) - ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) - if err != nil { - return nil, err + + if options.Running { + ctrs, err = ic.Libpod.GetRunningContainers() + if err != nil { + return nil, err + } + } else { + ctrs, err = getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } } + for _, con := range ctrs { timeout := con.StopTimeout() if options.Timeout != nil { diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go new file mode 100644 index 000000000..a41c01da0 --- /dev/null +++ b/pkg/domain/infra/abi/pods_stats.go @@ -0,0 +1,85 @@ +package abi + +import ( + "context" + "fmt" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/cgroups" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/rootless" + "github.com/docker/go-units" + "github.com/pkg/errors" +) + +// PodStats implements printing stats about pods. +func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { + // Cgroups v2 check for rootless. + if rootless.IsRootless() { + unified, err := cgroups.IsCgroup2UnifiedMode() + if err != nil { + return nil, err + } + if !unified { + return nil, errors.New("pod stats is not supported in rootless mode without cgroups v2") + } + } + // Get the (running) pods and convert them to the entities format. + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, errors.Wrap(err, "unable to get list of pods") + } + return ic.podsToStatsReport(pods) +} + +// podsToStatsReport converts a slice of pods into a corresponding slice of stats reports. +func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.PodStatsReport, error) { + reports := []*entities.PodStatsReport{} + for i := range pods { // Access by index to prevent potential loop-variable leaks. + podStats, err := pods[i].GetPodStats(nil) + if err != nil { + return nil, err + } + podID := pods[i].ID()[:12] + for j := range podStats { + r := entities.PodStatsReport{ + CPU: floatToPercentString(podStats[j].CPU), + MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit), + Mem: floatToPercentString(podStats[j].MemPerc), + NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput), + BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput), + PIDS: pidsToString(podStats[j].PIDs), + CID: podStats[j].ContainerID[:12], + Name: podStats[j].Name, + Pod: podID, + } + reports = append(reports, &r) + } + } + + return reports, nil +} + +func combineHumanValues(a, b uint64) string { + if a == 0 && b == 0 { + return "-- / --" + } + return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) +} + +func floatToPercentString(f float64) string { + strippedFloat, err := libpod.RemoveScientificNotationFromFloat(f) + if err != nil || strippedFloat == 0 { + // If things go bazinga, return a safe value + return "--" + } + return fmt.Sprintf("%.2f", strippedFloat) + "%" +} + +func pidsToString(pid uint64) string { + if pid == 0 { + // If things go bazinga, return a safe value + return "--" + } + return fmt.Sprintf("%d", pid) +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 18d6613f4..32f9c4e36 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -115,11 +115,15 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st t := int(*options.Timeout) timeout = &t } + ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } for _, c := range ctrs { + if options.Running && c.State != define.ContainerStateRunning.String() { + continue + } reports = append(reports, &entities.RestartReport{ Id: c.ID, Err: containers.Restart(ic.ClientCxt, c.ID, timeout), diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index e7641c077..c193c6752 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -211,3 +211,7 @@ func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodI } return pods.Inspect(ic.ClientCxt, options.NameOrID) } + +func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { + return pods.Stats(ic.ClientCxt, namesOrIds, options) +} |