651 lines
21 KiB
Go
651 lines
21 KiB
Go
//+build !remoteclient
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containers/buildah/pkg/formats"
|
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
|
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/containers/libpod/libpod/define"
|
|
"github.com/containers/libpod/libpod/image"
|
|
"github.com/docker/go-units"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
dfSystemCommand cliconfig.SystemDfValues
|
|
dfSystemDescription = `
|
|
podman system df
|
|
|
|
Show podman disk usage
|
|
`
|
|
_dfSystemCommand = &cobra.Command{
|
|
Use: "df",
|
|
Args: noSubArgs,
|
|
Short: "Show podman disk usage",
|
|
Long: dfSystemDescription,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
dfSystemCommand.GlobalFlags = MainGlobalOpts
|
|
dfSystemCommand.Remote = remoteclient
|
|
return dfSystemCmd(&dfSystemCommand)
|
|
},
|
|
}
|
|
)
|
|
|
|
type dfMetaData struct {
|
|
images []*image.Image
|
|
containers []*libpod.Container
|
|
activeContainers map[string]*libpod.Container
|
|
imagesUsedbyCtrMap map[string][]*libpod.Container
|
|
imagesUsedbyActiveCtr map[string][]*libpod.Container
|
|
volumes []*libpod.Volume
|
|
volumeUsedByContainerMap map[string][]*libpod.Container
|
|
}
|
|
|
|
type systemDfDiskUsage struct {
|
|
Type string
|
|
Total int
|
|
Active int
|
|
Size string
|
|
Reclaimable string
|
|
}
|
|
|
|
type imageVerboseDiskUsage struct {
|
|
Repository string
|
|
Tag string
|
|
ImageID string
|
|
Created string
|
|
Size string
|
|
SharedSize string
|
|
UniqueSize string
|
|
Containers int
|
|
}
|
|
|
|
type containerVerboseDiskUsage struct {
|
|
ContainerID string
|
|
Image string
|
|
Command string
|
|
LocalVolumes int
|
|
Size string
|
|
Created string
|
|
Status string
|
|
Names string
|
|
}
|
|
|
|
type volumeVerboseDiskUsage struct {
|
|
VolumeName string
|
|
Links int
|
|
Size string
|
|
}
|
|
|
|
const systemDfDefaultFormat string = "table {{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}"
|
|
const imageVerboseFormat string = "table {{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}"
|
|
const containerVerboseFormat string = "table {{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.Size}}\t{{.Created}}\t{{.Status}}\t{{.Names}}"
|
|
const volumeVerboseFormat string = "table {{.VolumeName}}\t{{.Links}}\t{{.Size}}"
|
|
|
|
func init() {
|
|
dfSystemCommand.Command = _dfSystemCommand
|
|
dfSystemCommand.SetUsageTemplate(UsageTemplate())
|
|
flags := dfSystemCommand.Flags()
|
|
flags.BoolVarP(&dfSystemCommand.Verbose, "verbose", "v", false, "Show detailed information on space usage")
|
|
flags.StringVar(&dfSystemCommand.Format, "format", "", "Pretty-print images using a Go template")
|
|
}
|
|
|
|
func dfSystemCmd(c *cliconfig.SystemDfValues) error {
|
|
runtime, err := libpodruntime.GetRuntime(getContext(), &c.PodmanCommand)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "Could not get runtime")
|
|
}
|
|
defer runtime.DeferredShutdown(false)
|
|
|
|
ctx := getContext()
|
|
|
|
metaData, err := getDfMetaData(ctx, runtime)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error getting disk usage data")
|
|
}
|
|
|
|
if c.Verbose {
|
|
err := verboseOutput(ctx, metaData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
systemDfDiskUsages, err := getDiskUsage(ctx, runtime, metaData)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error getting output of system df")
|
|
}
|
|
format := systemDfDefaultFormat
|
|
if c.Format != "" {
|
|
format = strings.Replace(c.Format, `\t`, "\t", -1)
|
|
}
|
|
return generateSysDfOutput(systemDfDiskUsages, format)
|
|
}
|
|
|
|
func generateSysDfOutput(systemDfDiskUsages []systemDfDiskUsage, format string) error {
|
|
var systemDfHeader = map[string]string{
|
|
"Type": "TYPE",
|
|
"Total": "TOTAL",
|
|
"Active": "ACTIVE",
|
|
"Size": "SIZE",
|
|
"Reclaimable": "RECLAIMABLE",
|
|
}
|
|
out := formats.StdoutTemplateArray{Output: systemDfDiskUsageToGeneric(systemDfDiskUsages), Template: format, Fields: systemDfHeader}
|
|
return out.Out()
|
|
}
|
|
|
|
func getDiskUsage(ctx context.Context, runtime *libpod.Runtime, metaData dfMetaData) ([]systemDfDiskUsage, error) {
|
|
imageDiskUsage, err := getImageDiskUsage(ctx, metaData.images, metaData.imagesUsedbyCtrMap, metaData.imagesUsedbyActiveCtr)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error getting disk usage of images")
|
|
}
|
|
containerDiskUsage, err := getContainerDiskUsage(metaData.containers, metaData.activeContainers)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error getting disk usage of containers")
|
|
}
|
|
volumeDiskUsage, err := getVolumeDiskUsage(metaData.volumes, metaData.volumeUsedByContainerMap)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error getting disk usage of volumess")
|
|
}
|
|
|
|
systemDfDiskUsages := []systemDfDiskUsage{imageDiskUsage, containerDiskUsage, volumeDiskUsage}
|
|
return systemDfDiskUsages, nil
|
|
}
|
|
|
|
func getDfMetaData(ctx context.Context, runtime *libpod.Runtime) (dfMetaData, error) {
|
|
var metaData dfMetaData
|
|
images, err := runtime.ImageRuntime().GetImages()
|
|
if err != nil {
|
|
return metaData, errors.Wrapf(err, "unable to get images")
|
|
}
|
|
containers, err := runtime.GetAllContainers()
|
|
if err != nil {
|
|
return metaData, errors.Wrapf(err, "error getting all containers")
|
|
}
|
|
volumes, err := runtime.GetAllVolumes()
|
|
if err != nil {
|
|
return metaData, errors.Wrap(err, "error getting all volumes")
|
|
}
|
|
activeContainers, err := activeContainers(containers)
|
|
if err != nil {
|
|
return metaData, errors.Wrapf(err, "error getting active containers")
|
|
}
|
|
imagesUsedbyCtrMap, imagesUsedbyActiveCtr, err := imagesUsedbyCtr(containers, activeContainers)
|
|
if err != nil {
|
|
return metaData, errors.Wrapf(err, "error getting getting images used by containers")
|
|
}
|
|
metaData = dfMetaData{
|
|
images: images,
|
|
containers: containers,
|
|
activeContainers: activeContainers,
|
|
imagesUsedbyCtrMap: imagesUsedbyCtrMap,
|
|
imagesUsedbyActiveCtr: imagesUsedbyActiveCtr,
|
|
volumes: volumes,
|
|
volumeUsedByContainerMap: volumeUsedByContainer(containers),
|
|
}
|
|
return metaData, nil
|
|
}
|
|
|
|
func imageUniqueSize(ctx context.Context, images []*image.Image) (map[string]uint64, error) {
|
|
imgUniqueSizeMap := make(map[string]uint64)
|
|
for _, img := range images {
|
|
parentImg := img
|
|
for {
|
|
next, err := parentImg.GetParent(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error getting parent of image %s", parentImg.ID())
|
|
}
|
|
if next == nil {
|
|
break
|
|
}
|
|
parentImg = next
|
|
}
|
|
imgSize, err := img.Size(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if img.ID() == parentImg.ID() {
|
|
imgUniqueSizeMap[img.ID()] = *imgSize
|
|
} else {
|
|
parentImgSize, err := parentImg.Size(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error getting size of parent image %s", parentImg.ID())
|
|
}
|
|
imgUniqueSizeMap[img.ID()] = *imgSize - *parentImgSize
|
|
}
|
|
}
|
|
return imgUniqueSizeMap, nil
|
|
}
|
|
|
|
func getImageDiskUsage(ctx context.Context, images []*image.Image, imageUsedbyCintainerMap map[string][]*libpod.Container, imageUsedbyActiveContainerMap map[string][]*libpod.Container) (systemDfDiskUsage, error) {
|
|
var (
|
|
numberOfImages int
|
|
sumSize uint64
|
|
numberOfActiveImages int
|
|
unreclaimableSize uint64
|
|
imageDiskUsage systemDfDiskUsage
|
|
reclaimableStr string
|
|
)
|
|
|
|
imgUniqueSizeMap, err := imageUniqueSize(ctx, images)
|
|
if err != nil {
|
|
return imageDiskUsage, errors.Wrapf(err, "error getting unique size of images")
|
|
}
|
|
|
|
for _, img := range images {
|
|
|
|
unreclaimableSize += imageUsedSize(img, imgUniqueSizeMap, imageUsedbyCintainerMap, imageUsedbyActiveContainerMap)
|
|
|
|
isParent, err := img.IsParent(ctx)
|
|
if err != nil {
|
|
return imageDiskUsage, err
|
|
}
|
|
parent, err := img.GetParent(ctx)
|
|
if err != nil {
|
|
return imageDiskUsage, errors.Wrapf(err, "error getting parent of image %s", img.ID())
|
|
}
|
|
if isParent && parent != nil {
|
|
continue
|
|
}
|
|
numberOfImages++
|
|
if _, isActive := imageUsedbyCintainerMap[img.ID()]; isActive {
|
|
numberOfActiveImages++
|
|
}
|
|
|
|
if !isParent {
|
|
size, err := img.Size(ctx)
|
|
if err != nil {
|
|
return imageDiskUsage, errors.Wrapf(err, "error getting disk usage of image %s", img.ID())
|
|
}
|
|
sumSize += *size
|
|
}
|
|
|
|
}
|
|
sumSizeStr := units.HumanSizeWithPrecision(float64(sumSize), 3)
|
|
reclaimable := sumSize - unreclaimableSize
|
|
if sumSize != 0 {
|
|
reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 100*reclaimable/sumSize)
|
|
} else {
|
|
reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 0)
|
|
}
|
|
imageDiskUsage = systemDfDiskUsage{
|
|
Type: "Images",
|
|
Total: numberOfImages,
|
|
Active: numberOfActiveImages,
|
|
Size: sumSizeStr,
|
|
Reclaimable: reclaimableStr,
|
|
}
|
|
return imageDiskUsage, nil
|
|
}
|
|
|
|
func imageUsedSize(img *image.Image, imgUniqueSizeMap map[string]uint64, imageUsedbyCintainerMap map[string][]*libpod.Container, imageUsedbyActiveContainerMap map[string][]*libpod.Container) uint64 {
|
|
var usedSize uint64
|
|
imgUnique := imgUniqueSizeMap[img.ID()]
|
|
if _, isCtrActive := imageUsedbyActiveContainerMap[img.ID()]; isCtrActive {
|
|
return imgUnique
|
|
}
|
|
containers := imageUsedbyCintainerMap[img.ID()]
|
|
for _, ctr := range containers {
|
|
if len(ctr.UserVolumes()) > 0 {
|
|
usedSize += imgUnique
|
|
return usedSize
|
|
}
|
|
}
|
|
return usedSize
|
|
}
|
|
|
|
func imagesUsedbyCtr(containers []*libpod.Container, activeContainers map[string]*libpod.Container) (map[string][]*libpod.Container, map[string][]*libpod.Container, error) {
|
|
imgCtrMap := make(map[string][]*libpod.Container)
|
|
imgActiveCtrMap := make(map[string][]*libpod.Container)
|
|
for _, ctr := range containers {
|
|
imgID, _ := ctr.Image()
|
|
imgCtrMap[imgID] = append(imgCtrMap[imgID], ctr)
|
|
if _, isActive := activeContainers[ctr.ID()]; isActive {
|
|
imgActiveCtrMap[imgID] = append(imgActiveCtrMap[imgID], ctr)
|
|
}
|
|
}
|
|
return imgCtrMap, imgActiveCtrMap, nil
|
|
}
|
|
|
|
func getContainerDiskUsage(containers []*libpod.Container, activeContainers map[string]*libpod.Container) (systemDfDiskUsage, error) {
|
|
var (
|
|
sumSize int64
|
|
unreclaimableSize int64
|
|
reclaimableStr string
|
|
)
|
|
for _, ctr := range containers {
|
|
size, err := ctr.RWSize()
|
|
if err != nil {
|
|
return systemDfDiskUsage{}, errors.Wrapf(err, "error getting size of container %s", ctr.ID())
|
|
}
|
|
sumSize += size
|
|
}
|
|
for _, activeCtr := range activeContainers {
|
|
size, err := activeCtr.RWSize()
|
|
if err != nil {
|
|
return systemDfDiskUsage{}, errors.Wrapf(err, "error getting size of active container %s", activeCtr.ID())
|
|
}
|
|
unreclaimableSize += size
|
|
}
|
|
if sumSize == 0 {
|
|
reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(0, 3), 0)
|
|
} else {
|
|
reclaimable := sumSize - unreclaimableSize
|
|
reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 100*reclaimable/sumSize)
|
|
}
|
|
containerDiskUsage := systemDfDiskUsage{
|
|
Type: "Containers",
|
|
Total: len(containers),
|
|
Active: len(activeContainers),
|
|
Size: units.HumanSizeWithPrecision(float64(sumSize), 3),
|
|
Reclaimable: reclaimableStr,
|
|
}
|
|
return containerDiskUsage, nil
|
|
}
|
|
|
|
func ctrIsActive(ctr *libpod.Container) (bool, error) {
|
|
state, err := ctr.State()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return state == define.ContainerStatePaused || state == define.ContainerStateRunning, nil
|
|
}
|
|
|
|
func activeContainers(containers []*libpod.Container) (map[string]*libpod.Container, error) {
|
|
activeContainers := make(map[string]*libpod.Container)
|
|
for _, aCtr := range containers {
|
|
isActive, err := ctrIsActive(aCtr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if isActive {
|
|
activeContainers[aCtr.ID()] = aCtr
|
|
}
|
|
}
|
|
return activeContainers, nil
|
|
}
|
|
|
|
func getVolumeDiskUsage(volumes []*libpod.Volume, volumeUsedByContainerMap map[string][]*libpod.Container) (systemDfDiskUsage, error) {
|
|
var (
|
|
sumSize int64
|
|
unreclaimableSize int64
|
|
reclaimableStr string
|
|
)
|
|
for _, volume := range volumes {
|
|
size, err := volumeSize(volume)
|
|
if err != nil {
|
|
return systemDfDiskUsage{}, errors.Wrapf(err, "error getting size of volime %s", volume.Name())
|
|
}
|
|
sumSize += size
|
|
if _, exist := volumeUsedByContainerMap[volume.Name()]; exist {
|
|
unreclaimableSize += size
|
|
}
|
|
}
|
|
reclaimable := sumSize - unreclaimableSize
|
|
if sumSize != 0 {
|
|
reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 100*reclaimable/sumSize)
|
|
} else {
|
|
reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 0)
|
|
}
|
|
volumesDiskUsage := systemDfDiskUsage{
|
|
Type: "Local Volumes",
|
|
Total: len(volumes),
|
|
Active: len(volumeUsedByContainerMap),
|
|
Size: units.HumanSizeWithPrecision(float64(sumSize), 3),
|
|
Reclaimable: reclaimableStr,
|
|
}
|
|
return volumesDiskUsage, nil
|
|
}
|
|
|
|
func volumeUsedByContainer(containers []*libpod.Container) map[string][]*libpod.Container {
|
|
volumeUsedByContainerMap := make(map[string][]*libpod.Container)
|
|
for _, ctr := range containers {
|
|
|
|
ctrVolumes := ctr.UserVolumes()
|
|
for _, ctrVolume := range ctrVolumes {
|
|
volumeUsedByContainerMap[ctrVolume] = append(volumeUsedByContainerMap[ctrVolume], ctr)
|
|
}
|
|
}
|
|
return volumeUsedByContainerMap
|
|
}
|
|
|
|
func volumeSize(volume *libpod.Volume) (int64, error) {
|
|
var size int64
|
|
err := filepath.Walk(volume.MountPoint(), func(path string, info os.FileInfo, err error) error {
|
|
if err == nil && !info.IsDir() {
|
|
size += info.Size()
|
|
}
|
|
return err
|
|
})
|
|
return size, err
|
|
}
|
|
|
|
func getImageVerboseDiskUsage(ctx context.Context, images []*image.Image, imagesUsedbyCtr map[string][]*libpod.Container) ([]imageVerboseDiskUsage, error) {
|
|
var imagesVerboseDiskUsage []imageVerboseDiskUsage
|
|
imgUniqueSizeMap, err := imageUniqueSize(ctx, images)
|
|
if err != nil {
|
|
return imagesVerboseDiskUsage, errors.Wrapf(err, "error getting unique size of images")
|
|
}
|
|
for _, img := range images {
|
|
isParent, err := img.IsParent(ctx)
|
|
if err != nil {
|
|
return imagesVerboseDiskUsage, errors.Wrapf(err, "error checking if %s is a parent images", img.ID())
|
|
}
|
|
parent, err := img.GetParent(ctx)
|
|
if err != nil {
|
|
return imagesVerboseDiskUsage, errors.Wrapf(err, "error getting parent of image %s", img.ID())
|
|
}
|
|
if isParent && parent != nil {
|
|
continue
|
|
}
|
|
size, err := img.Size(ctx)
|
|
if err != nil {
|
|
return imagesVerboseDiskUsage, errors.Wrapf(err, "error getting size of image %s", img.ID())
|
|
}
|
|
numberOfContainers := 0
|
|
if ctrs, exist := imagesUsedbyCtr[img.ID()]; exist {
|
|
numberOfContainers = len(ctrs)
|
|
}
|
|
var repo string
|
|
var tag string
|
|
var repotags []string
|
|
if len(img.Names()) != 0 {
|
|
repotags = []string{img.Names()[0]}
|
|
}
|
|
repopairs, err := image.ReposToMap(repotags)
|
|
if err != nil {
|
|
logrus.Errorf("error finding tag/digest for %s", img.ID())
|
|
}
|
|
for reponame, tags := range repopairs {
|
|
for _, tagname := range tags {
|
|
repo = reponame
|
|
tag = tagname
|
|
}
|
|
}
|
|
|
|
imageVerbosedf := imageVerboseDiskUsage{
|
|
Repository: repo,
|
|
Tag: tag,
|
|
ImageID: shortID(img.ID()),
|
|
Created: fmt.Sprintf("%s ago", units.HumanDuration(time.Since((img.Created().Local())))),
|
|
Size: units.HumanSizeWithPrecision(float64(*size), 3),
|
|
SharedSize: units.HumanSizeWithPrecision(float64(*size-imgUniqueSizeMap[img.ID()]), 3),
|
|
UniqueSize: units.HumanSizeWithPrecision(float64(imgUniqueSizeMap[img.ID()]), 3),
|
|
Containers: numberOfContainers,
|
|
}
|
|
imagesVerboseDiskUsage = append(imagesVerboseDiskUsage, imageVerbosedf)
|
|
}
|
|
return imagesVerboseDiskUsage, nil
|
|
}
|
|
|
|
func getContainerVerboseDiskUsage(containers []*libpod.Container) (containersVerboseDiskUsage []containerVerboseDiskUsage, err error) {
|
|
for _, ctr := range containers {
|
|
imgID, _ := ctr.Image()
|
|
size, err := ctr.RWSize()
|
|
if err != nil {
|
|
return containersVerboseDiskUsage, errors.Wrapf(err, "error getting size of container %s", ctr.ID())
|
|
}
|
|
state, err := ctr.State()
|
|
if err != nil {
|
|
return containersVerboseDiskUsage, errors.Wrapf(err, "error getting the state of container %s", ctr.ID())
|
|
}
|
|
|
|
ctrVerboseData := containerVerboseDiskUsage{
|
|
ContainerID: shortID(ctr.ID()),
|
|
Image: shortImageID(imgID),
|
|
Command: strings.Join(ctr.Command(), " "),
|
|
LocalVolumes: len(ctr.UserVolumes()),
|
|
Size: units.HumanSizeWithPrecision(float64(size), 3),
|
|
Created: fmt.Sprintf("%s ago", units.HumanDuration(time.Since(ctr.CreatedTime().Local()))),
|
|
Status: state.String(),
|
|
Names: ctr.Name(),
|
|
}
|
|
containersVerboseDiskUsage = append(containersVerboseDiskUsage, ctrVerboseData)
|
|
|
|
}
|
|
return containersVerboseDiskUsage, nil
|
|
}
|
|
|
|
func getVolumeVerboseDiskUsage(volumes []*libpod.Volume, volumeUsedByContainerMap map[string][]*libpod.Container) (volumesVerboseDiskUsage []volumeVerboseDiskUsage, err error) {
|
|
for _, vol := range volumes {
|
|
volSize, err := volumeSize(vol)
|
|
if err != nil {
|
|
return volumesVerboseDiskUsage, errors.Wrapf(err, "error getting size of volume %s", vol.Name())
|
|
}
|
|
links := 0
|
|
if linkCtr, exist := volumeUsedByContainerMap[vol.Name()]; exist {
|
|
links = len(linkCtr)
|
|
}
|
|
volumeVerboseData := volumeVerboseDiskUsage{
|
|
VolumeName: vol.Name(),
|
|
Links: links,
|
|
Size: units.HumanSizeWithPrecision(float64(volSize), 3),
|
|
}
|
|
volumesVerboseDiskUsage = append(volumesVerboseDiskUsage, volumeVerboseData)
|
|
}
|
|
return volumesVerboseDiskUsage, nil
|
|
}
|
|
|
|
func imagesVerboseOutput(ctx context.Context, metaData dfMetaData) error {
|
|
var imageVerboseHeader = map[string]string{
|
|
"Repository": "REPOSITORY",
|
|
"Tag": "TAG",
|
|
"ImageID": "IMAGE ID",
|
|
"Created": "CREATED",
|
|
"Size": "SIZE",
|
|
"SharedSize": "SHARED SIZE",
|
|
"UniqueSize": "UNIQUE SIZE",
|
|
"Containers": "CONTAINERS",
|
|
}
|
|
imagesVerboseDiskUsage, err := getImageVerboseDiskUsage(ctx, metaData.images, metaData.imagesUsedbyCtrMap)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error getting verbose output of images")
|
|
}
|
|
if _, err := os.Stderr.WriteString("Images space usage:\n\n"); err != nil {
|
|
return err
|
|
}
|
|
out := formats.StdoutTemplateArray{Output: systemDfImageVerboseDiskUsageToGeneric(imagesVerboseDiskUsage), Template: imageVerboseFormat, Fields: imageVerboseHeader}
|
|
return out.Out()
|
|
}
|
|
|
|
func containersVerboseOutput(ctx context.Context, metaData dfMetaData) error {
|
|
var containerVerboseHeader = map[string]string{
|
|
"ContainerID": "CONTAINER ID ",
|
|
"Image": "IMAGE",
|
|
"Command": "COMMAND",
|
|
"LocalVolumes": "LOCAL VOLUMES",
|
|
"Size": "SIZE",
|
|
"Created": "CREATED",
|
|
"Status": "STATUS",
|
|
"Names": "NAMES",
|
|
}
|
|
containersVerboseDiskUsage, err := getContainerVerboseDiskUsage(metaData.containers)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error getting verbose output of containers")
|
|
}
|
|
if _, err := os.Stderr.WriteString("\nContainers space usage:\n\n"); err != nil {
|
|
return err
|
|
}
|
|
out := formats.StdoutTemplateArray{Output: systemDfContainerVerboseDiskUsageToGeneric(containersVerboseDiskUsage), Template: containerVerboseFormat, Fields: containerVerboseHeader}
|
|
return out.Out()
|
|
|
|
}
|
|
|
|
func volumesVerboseOutput(ctx context.Context, metaData dfMetaData) error {
|
|
var volumeVerboseHeader = map[string]string{
|
|
"VolumeName": "VOLUME NAME",
|
|
"Links": "LINKS",
|
|
"Size": "SIZE",
|
|
}
|
|
volumesVerboseDiskUsage, err := getVolumeVerboseDiskUsage(metaData.volumes, metaData.volumeUsedByContainerMap)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error getting verbose output of volumes")
|
|
}
|
|
if _, err := os.Stderr.WriteString("\nLocal Volumes space usage:\n\n"); err != nil {
|
|
return err
|
|
}
|
|
out := formats.StdoutTemplateArray{Output: systemDfVolumeVerboseDiskUsageToGeneric(volumesVerboseDiskUsage), Template: volumeVerboseFormat, Fields: volumeVerboseHeader}
|
|
return out.Out()
|
|
}
|
|
|
|
func verboseOutput(ctx context.Context, metaData dfMetaData) error {
|
|
if err := imagesVerboseOutput(ctx, metaData); err != nil {
|
|
return err
|
|
}
|
|
if err := containersVerboseOutput(ctx, metaData); err != nil {
|
|
return err
|
|
}
|
|
if err := volumesVerboseOutput(ctx, metaData); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func systemDfDiskUsageToGeneric(diskUsages []systemDfDiskUsage) (out []interface{}) {
|
|
for _, usage := range diskUsages {
|
|
out = append(out, interface{}(usage))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func systemDfImageVerboseDiskUsageToGeneric(diskUsages []imageVerboseDiskUsage) (out []interface{}) {
|
|
for _, usage := range diskUsages {
|
|
out = append(out, interface{}(usage))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func systemDfContainerVerboseDiskUsageToGeneric(diskUsages []containerVerboseDiskUsage) (out []interface{}) {
|
|
for _, usage := range diskUsages {
|
|
out = append(out, interface{}(usage))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func systemDfVolumeVerboseDiskUsageToGeneric(diskUsages []volumeVerboseDiskUsage) (out []interface{}) {
|
|
for _, usage := range diskUsages {
|
|
out = append(out, interface{}(usage))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func shortImageID(id string) string {
|
|
const imageIDTruncLength int = 4
|
|
if len(id) > imageIDTruncLength {
|
|
return id[:imageIDTruncLength]
|
|
}
|
|
return id
|
|
}
|