spiegel_podman/cmd/podman/system/df.go
Paul Holzinger b5d1d89a37 Add shell completion with cobra
Allow automatic generation for shell completion scripts
with the internal cobra functions (requires v1.0.0+).

This should replace the handwritten completion scripts
and even adds support for fish. With this approach it is
less likley that completions and code are out of sync.

We can now create the scripts with
- podman completion bash
- podman completion zsh
- podman completion fish

To test the completion run:
source <(podman completion bash)

The same works for podman-remote and podman --remote and
it will complete your remote containers/images with
the correct endpoints values from --url/--connection.

The completion logic is written in go and provided by the
cobra library. The completion functions lives in
`cmd/podman/completion/completion.go`.

The unit test at cmd/podman/shell_completion_test.go checks
if each command and flag has an autocompletion function set.
This prevents that commands and flags have no shell completion set.

This commit does not replace the current autocompletion scripts.

Closes #6440

Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
2020-11-12 11:38:31 +01:00

284 lines
7.5 KiB
Go

package system
import (
"fmt"
"os"
"strings"
"text/tabwriter"
"text/template"
"time"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/report"
"github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/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{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
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
}
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
if dfOptions.Verbose {
return printVerbose(w, cmd, reports)
}
return printSummary(w, cmd, reports)
}
func printSummary(w *tabwriter.Writer, 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",
})
row := "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n"
if cmd.Flags().Changed("format") {
row = report.NormalizeFormat(dfOptions.Format)
}
return writeTemplate(w, cmd, hdrs, row, dfSummaries)
}
func printVerbose(w *tabwriter.Writer, cmd *cobra.Command, reports *entities.SystemDfReport) error {
defer w.Flush()
fmt.Fprint(w, "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 := "{{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}\n"
if err := writeTemplate(w, cmd, hdrs, imageRow, dfImages); err != nil {
return nil
}
fmt.Fprint(w, "\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 := "{{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.RWSize}}\t{{.Created}}\t{{.Status}}\t{{.Names}}\n"
if err := writeTemplate(w, cmd, hdrs, containerRow, dfContainers); err != nil {
return nil
}
fmt.Fprint(w, "\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 := "{{.VolumeName}}\t{{.Links}}\t{{.Size}}\n"
return writeTemplate(w, cmd, hdrs, volumeRow, dfVolumes)
}
func writeTemplate(w *tabwriter.Writer, cmd *cobra.Command, hdrs []map[string]string, format string, output interface{}) error {
defer w.Flush()
format = parse.EnforceRange(format)
tmpl, err := template.New("df").Parse(format)
if err != nil {
return err
}
if !cmd.Flags().Changed("format") {
if err := tmpl.Execute(w, hdrs); err != nil {
return err
}
}
return tmpl.Execute(w, 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 := int(float64(d.reclaimable)/float64(d.size)) * 100
return fmt.Sprintf("%s (%d%%)", units.HumanSize(float64(d.reclaimable)), percent)
}