mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +08:00

The nolintlint linter does not deny the use of `//nolint` Instead it allows us to enforce a common nolint style: - force that a linter name must be specified - do not add a space between `//` and `nolint` - make sure nolint is only used when there is actually a problem Signed-off-by: Paul Holzinger <pholzing@redhat.com>
299 lines
7.7 KiB
Go
299 lines
7.7 KiB
Go
package system
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containers/common/pkg/completion"
|
|
"github.com/containers/common/pkg/report"
|
|
"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/docker/go-units"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
dfSystemDescription = `
|
|
podman system df
|
|
|
|
Show podman disk usage
|
|
`
|
|
dfSystemCommand = &cobra.Command{
|
|
Use: "df [options]",
|
|
Args: validate.NoArgs,
|
|
Short: "Show podman disk usage",
|
|
Long: dfSystemDescription,
|
|
RunE: df,
|
|
ValidArgsFunction: completion.AutocompleteNone,
|
|
}
|
|
)
|
|
|
|
var (
|
|
dfOptions entities.SystemDfOptions
|
|
)
|
|
|
|
func init() {
|
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
|
Command: dfSystemCommand,
|
|
Parent: systemCmd,
|
|
})
|
|
flags := dfSystemCommand.Flags()
|
|
flags.BoolVarP(&dfOptions.Verbose, "verbose", "v", false, "Show detailed information on disk usage")
|
|
|
|
formatFlagName := "format"
|
|
flags.StringVar(&dfOptions.Format, formatFlagName, "", "Pretty-print images using a Go template")
|
|
_ = dfSystemCommand.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone)
|
|
}
|
|
|
|
func df(cmd *cobra.Command, args []string) error {
|
|
reports, err := registry.ContainerEngine().SystemDf(registry.Context(), dfOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if dfOptions.Verbose {
|
|
return printVerbose(cmd, reports)
|
|
}
|
|
return printSummary(cmd, reports)
|
|
}
|
|
|
|
func printSummary(cmd *cobra.Command, reports *entities.SystemDfReport) error {
|
|
var (
|
|
dfSummaries []*dfSummary
|
|
active int
|
|
size, reclaimable int64
|
|
)
|
|
|
|
for _, i := range reports.Images {
|
|
if i.Containers > 0 {
|
|
active++
|
|
}
|
|
size += i.Size
|
|
if i.Containers < 1 {
|
|
reclaimable += i.Size
|
|
}
|
|
}
|
|
imageSummary := dfSummary{
|
|
Type: "Images",
|
|
Total: len(reports.Images),
|
|
Active: active,
|
|
size: size,
|
|
reclaimable: reclaimable,
|
|
}
|
|
dfSummaries = append(dfSummaries, &imageSummary)
|
|
|
|
// Containers
|
|
var (
|
|
conActive int
|
|
conSize, conReclaimable int64
|
|
)
|
|
for _, c := range reports.Containers {
|
|
if c.Status == "running" {
|
|
conActive++
|
|
} else {
|
|
conReclaimable += c.RWSize
|
|
}
|
|
conSize += c.RWSize
|
|
}
|
|
containerSummary := dfSummary{
|
|
Type: "Containers",
|
|
Total: len(reports.Containers),
|
|
Active: conActive,
|
|
size: conSize,
|
|
reclaimable: conReclaimable,
|
|
}
|
|
dfSummaries = append(dfSummaries, &containerSummary)
|
|
|
|
// Volumes
|
|
var (
|
|
activeVolumes int
|
|
volumesSize, volumesReclaimable int64
|
|
)
|
|
|
|
for _, v := range reports.Volumes {
|
|
activeVolumes += v.Links
|
|
volumesSize += v.Size
|
|
volumesReclaimable += v.ReclaimableSize
|
|
}
|
|
volumeSummary := dfSummary{
|
|
Type: "Local Volumes",
|
|
Total: len(reports.Volumes),
|
|
Active: activeVolumes,
|
|
size: volumesSize,
|
|
reclaimable: volumesReclaimable,
|
|
}
|
|
dfSummaries = append(dfSummaries, &volumeSummary)
|
|
|
|
// need to give un-exported fields
|
|
hdrs := report.Headers(dfSummary{}, map[string]string{
|
|
"Size": "SIZE",
|
|
"Reclaimable": "RECLAIMABLE",
|
|
})
|
|
|
|
rpt := report.New(os.Stdout, cmd.Name())
|
|
defer rpt.Flush()
|
|
|
|
var err error
|
|
if cmd.Flags().Changed("format") {
|
|
rpt, err = rpt.Parse(report.OriginUser, dfOptions.Format)
|
|
} else {
|
|
row := "{{range . }}{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n{{end -}}"
|
|
rpt, err = rpt.Parse(report.OriginPodman, row)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return writeTemplate(rpt, hdrs, dfSummaries)
|
|
}
|
|
|
|
func printVerbose(cmd *cobra.Command, reports *entities.SystemDfReport) error { //nolint:interfacer
|
|
rpt := report.New(os.Stdout, cmd.Name())
|
|
defer rpt.Flush()
|
|
|
|
fmt.Fprint(rpt.Writer(), "Images space usage:\n\n")
|
|
// convert to dfImage for output
|
|
dfImages := make([]*dfImage, 0, len(reports.Images))
|
|
for _, d := range reports.Images {
|
|
dfImages = append(dfImages, &dfImage{SystemDfImageReport: d})
|
|
}
|
|
hdrs := report.Headers(entities.SystemDfImageReport{}, map[string]string{
|
|
"ImageID": "IMAGE ID",
|
|
"SharedSize": "SHARED SIZE",
|
|
"UniqueSize": "UNIQUE SIZE",
|
|
})
|
|
imageRow := "{{range .}}{{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}\n{{end -}}"
|
|
rpt, err := rpt.Parse(report.OriginPodman, imageRow)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := writeTemplate(rpt, hdrs, dfImages); err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprint(rpt.Writer(), "\nContainers space usage:\n\n")
|
|
// convert to dfContainers for output
|
|
dfContainers := make([]*dfContainer, 0, len(reports.Containers))
|
|
for _, d := range reports.Containers {
|
|
dfContainers = append(dfContainers, &dfContainer{SystemDfContainerReport: d})
|
|
}
|
|
hdrs = report.Headers(entities.SystemDfContainerReport{}, map[string]string{
|
|
"ContainerID": "CONTAINER ID",
|
|
"LocalVolumes": "LOCAL VOLUMES",
|
|
"RWSize": "SIZE",
|
|
})
|
|
containerRow := "{{range .}}{{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.RWSize}}\t{{.Created}}\t{{.Status}}\t{{.Names}}\n{{end -}}"
|
|
rpt, err = rpt.Parse(report.OriginPodman, containerRow)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := writeTemplate(rpt, hdrs, dfContainers); err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprint(rpt.Writer(), "\nLocal Volumes space usage:\n\n")
|
|
dfVolumes := make([]*dfVolume, 0, len(reports.Volumes))
|
|
// convert to dfVolume for output
|
|
for _, d := range reports.Volumes {
|
|
dfVolumes = append(dfVolumes, &dfVolume{SystemDfVolumeReport: d})
|
|
}
|
|
hdrs = report.Headers(entities.SystemDfVolumeReport{}, map[string]string{
|
|
"VolumeName": "VOLUME NAME",
|
|
})
|
|
volumeRow := "{{range .}}{{.VolumeName}}\t{{.Links}}\t{{.Size}}\n{{end -}}"
|
|
rpt, err = rpt.Parse(report.OriginPodman, volumeRow)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return writeTemplate(rpt, hdrs, dfVolumes)
|
|
}
|
|
|
|
func writeTemplate(rpt *report.Formatter, hdrs []map[string]string, output interface{}) error {
|
|
if rpt.RenderHeaders {
|
|
if err := rpt.Execute(hdrs); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return rpt.Execute(output)
|
|
}
|
|
|
|
type dfImage struct {
|
|
*entities.SystemDfImageReport
|
|
}
|
|
|
|
func (d *dfImage) ImageID() string {
|
|
return d.SystemDfImageReport.ImageID[0:12]
|
|
}
|
|
|
|
func (d *dfImage) Created() string {
|
|
return units.HumanDuration(time.Since(d.SystemDfImageReport.Created))
|
|
}
|
|
|
|
func (d *dfImage) Size() string {
|
|
return units.HumanSize(float64(d.SystemDfImageReport.Size))
|
|
}
|
|
|
|
func (d *dfImage) SharedSize() string {
|
|
return units.HumanSize(float64(d.SystemDfImageReport.SharedSize))
|
|
}
|
|
|
|
func (d *dfImage) UniqueSize() string {
|
|
return units.HumanSize(float64(d.SystemDfImageReport.UniqueSize))
|
|
}
|
|
|
|
type dfContainer struct {
|
|
*entities.SystemDfContainerReport
|
|
}
|
|
|
|
func (d *dfContainer) ContainerID() string {
|
|
return d.SystemDfContainerReport.ContainerID[0:12]
|
|
}
|
|
|
|
func (d *dfContainer) Image() string {
|
|
return d.SystemDfContainerReport.Image[0:12]
|
|
}
|
|
|
|
func (d *dfContainer) Command() string {
|
|
return strings.Join(d.SystemDfContainerReport.Command, " ")
|
|
}
|
|
|
|
func (d *dfContainer) RWSize() string {
|
|
return units.HumanSize(float64(d.SystemDfContainerReport.RWSize))
|
|
}
|
|
|
|
func (d *dfContainer) Created() string {
|
|
return units.HumanDuration(time.Since(d.SystemDfContainerReport.Created))
|
|
}
|
|
|
|
type dfVolume struct {
|
|
*entities.SystemDfVolumeReport
|
|
}
|
|
|
|
func (d *dfVolume) Size() string {
|
|
return units.HumanSize(float64(d.SystemDfVolumeReport.Size))
|
|
}
|
|
|
|
type dfSummary struct {
|
|
Type string
|
|
Total int
|
|
Active int
|
|
size int64
|
|
reclaimable int64
|
|
}
|
|
|
|
func (d *dfSummary) Size() string {
|
|
return units.HumanSize(float64(d.size))
|
|
}
|
|
|
|
func (d *dfSummary) Reclaimable() string {
|
|
percent := 0
|
|
// make sure to check this to prevent div by zero problems
|
|
if d.size > 0 {
|
|
percent = int(math.Round(float64(d.reclaimable) / float64(d.size) * float64(100)))
|
|
}
|
|
return fmt.Sprintf("%s (%d%%)", units.HumanSize(float64(d.reclaimable)), percent)
|
|
}
|