github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/system_df.go (about) 1 //+build !remoteclient 2 3 package main 4 5 import ( 6 "context" 7 "fmt" 8 "os" 9 "path/filepath" 10 "strings" 11 "time" 12 13 "github.com/containers/buildah/pkg/formats" 14 "github.com/containers/libpod/cmd/podman/cliconfig" 15 "github.com/containers/libpod/cmd/podman/libpodruntime" 16 "github.com/containers/libpod/libpod" 17 "github.com/containers/libpod/libpod/define" 18 "github.com/containers/libpod/libpod/image" 19 "github.com/docker/go-units" 20 "github.com/pkg/errors" 21 "github.com/sirupsen/logrus" 22 "github.com/spf13/cobra" 23 ) 24 25 var ( 26 dfSystemCommand cliconfig.SystemDfValues 27 dfSystemDescription = ` 28 podman system df 29 30 Show podman disk usage 31 ` 32 _dfSystemCommand = &cobra.Command{ 33 Use: "df", 34 Args: noSubArgs, 35 Short: "Show podman disk usage", 36 Long: dfSystemDescription, 37 RunE: func(cmd *cobra.Command, args []string) error { 38 dfSystemCommand.GlobalFlags = MainGlobalOpts 39 dfSystemCommand.Remote = remoteclient 40 return dfSystemCmd(&dfSystemCommand) 41 }, 42 } 43 ) 44 45 type dfMetaData struct { 46 images []*image.Image 47 containers []*libpod.Container 48 activeContainers map[string]*libpod.Container 49 imagesUsedbyCtrMap map[string][]*libpod.Container 50 imagesUsedbyActiveCtr map[string][]*libpod.Container 51 volumes []*libpod.Volume 52 volumeUsedByContainerMap map[string][]*libpod.Container 53 } 54 55 type systemDfDiskUsage struct { 56 Type string 57 Total int 58 Active int 59 Size string 60 Reclaimable string 61 } 62 63 type imageVerboseDiskUsage struct { 64 Repository string 65 Tag string 66 ImageID string 67 Created string 68 Size string 69 SharedSize string 70 UniqueSize string 71 Containers int 72 } 73 74 type containerVerboseDiskUsage struct { 75 ContainerID string 76 Image string 77 Command string 78 LocalVolumes int 79 Size string 80 Created string 81 Status string 82 Names string 83 } 84 85 type volumeVerboseDiskUsage struct { 86 VolumeName string 87 Links int 88 Size string 89 } 90 91 const systemDfDefaultFormat string = "table {{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}" 92 const imageVerboseFormat string = "table {{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}" 93 const containerVerboseFormat string = "table {{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.Size}}\t{{.Created}}\t{{.Status}}\t{{.Names}}" 94 const volumeVerboseFormat string = "table {{.VolumeName}}\t{{.Links}}\t{{.Size}}" 95 96 func init() { 97 dfSystemCommand.Command = _dfSystemCommand 98 dfSystemCommand.SetUsageTemplate(UsageTemplate()) 99 flags := dfSystemCommand.Flags() 100 flags.BoolVarP(&dfSystemCommand.Verbose, "verbose", "v", false, "Show detailed information on space usage") 101 flags.StringVar(&dfSystemCommand.Format, "format", "", "Pretty-print images using a Go template") 102 } 103 104 func dfSystemCmd(c *cliconfig.SystemDfValues) error { 105 runtime, err := libpodruntime.GetRuntime(getContext(), &c.PodmanCommand) 106 if err != nil { 107 return errors.Wrapf(err, "Could not get runtime") 108 } 109 defer runtime.DeferredShutdown(false) 110 111 ctx := getContext() 112 113 metaData, err := getDfMetaData(ctx, runtime) 114 if err != nil { 115 return errors.Wrapf(err, "error getting disk usage data") 116 } 117 118 if c.Verbose { 119 err := verboseOutput(ctx, metaData) 120 if err != nil { 121 return err 122 } 123 return nil 124 } 125 126 systemDfDiskUsages, err := getDiskUsage(ctx, runtime, metaData) 127 if err != nil { 128 return errors.Wrapf(err, "error getting output of system df") 129 } 130 format := systemDfDefaultFormat 131 if c.Format != "" { 132 format = strings.Replace(c.Format, `\t`, "\t", -1) 133 } 134 return generateSysDfOutput(systemDfDiskUsages, format) 135 } 136 137 func generateSysDfOutput(systemDfDiskUsages []systemDfDiskUsage, format string) error { 138 var systemDfHeader = map[string]string{ 139 "Type": "TYPE", 140 "Total": "TOTAL", 141 "Active": "ACTIVE", 142 "Size": "SIZE", 143 "Reclaimable": "RECLAIMABLE", 144 } 145 out := formats.StdoutTemplateArray{Output: systemDfDiskUsageToGeneric(systemDfDiskUsages), Template: format, Fields: systemDfHeader} 146 return out.Out() 147 } 148 149 func getDiskUsage(ctx context.Context, runtime *libpod.Runtime, metaData dfMetaData) ([]systemDfDiskUsage, error) { 150 imageDiskUsage, err := getImageDiskUsage(ctx, metaData.images, metaData.imagesUsedbyCtrMap, metaData.imagesUsedbyActiveCtr) 151 if err != nil { 152 return nil, errors.Wrapf(err, "error getting disk usage of images") 153 } 154 containerDiskUsage, err := getContainerDiskUsage(metaData.containers, metaData.activeContainers) 155 if err != nil { 156 return nil, errors.Wrapf(err, "error getting disk usage of containers") 157 } 158 volumeDiskUsage, err := getVolumeDiskUsage(metaData.volumes, metaData.volumeUsedByContainerMap) 159 if err != nil { 160 return nil, errors.Wrapf(err, "error getting disk usage of volumess") 161 } 162 163 systemDfDiskUsages := []systemDfDiskUsage{imageDiskUsage, containerDiskUsage, volumeDiskUsage} 164 return systemDfDiskUsages, nil 165 } 166 167 func getDfMetaData(ctx context.Context, runtime *libpod.Runtime) (dfMetaData, error) { 168 var metaData dfMetaData 169 images, err := runtime.ImageRuntime().GetImages() 170 if err != nil { 171 return metaData, errors.Wrapf(err, "unable to get images") 172 } 173 containers, err := runtime.GetAllContainers() 174 if err != nil { 175 return metaData, errors.Wrapf(err, "error getting all containers") 176 } 177 volumes, err := runtime.GetAllVolumes() 178 if err != nil { 179 return metaData, errors.Wrap(err, "error getting all volumes") 180 } 181 activeContainers, err := activeContainers(containers) 182 if err != nil { 183 return metaData, errors.Wrapf(err, "error getting active containers") 184 } 185 imagesUsedbyCtrMap, imagesUsedbyActiveCtr, err := imagesUsedbyCtr(containers, activeContainers) 186 if err != nil { 187 return metaData, errors.Wrapf(err, "error getting getting images used by containers") 188 } 189 metaData = dfMetaData{ 190 images: images, 191 containers: containers, 192 activeContainers: activeContainers, 193 imagesUsedbyCtrMap: imagesUsedbyCtrMap, 194 imagesUsedbyActiveCtr: imagesUsedbyActiveCtr, 195 volumes: volumes, 196 volumeUsedByContainerMap: volumeUsedByContainer(containers), 197 } 198 return metaData, nil 199 } 200 201 func imageUniqueSize(ctx context.Context, images []*image.Image) (map[string]uint64, error) { 202 imgUniqueSizeMap := make(map[string]uint64) 203 for _, img := range images { 204 parentImg := img 205 for { 206 next, err := parentImg.GetParent(ctx) 207 if err != nil { 208 return nil, errors.Wrapf(err, "error getting parent of image %s", parentImg.ID()) 209 } 210 if next == nil { 211 break 212 } 213 parentImg = next 214 } 215 imgSize, err := img.Size(ctx) 216 if err != nil { 217 return nil, err 218 } 219 if img.ID() == parentImg.ID() { 220 imgUniqueSizeMap[img.ID()] = *imgSize 221 } else { 222 parentImgSize, err := parentImg.Size(ctx) 223 if err != nil { 224 return nil, errors.Wrapf(err, "error getting size of parent image %s", parentImg.ID()) 225 } 226 imgUniqueSizeMap[img.ID()] = *imgSize - *parentImgSize 227 } 228 } 229 return imgUniqueSizeMap, nil 230 } 231 232 func getImageDiskUsage(ctx context.Context, images []*image.Image, imageUsedbyCintainerMap map[string][]*libpod.Container, imageUsedbyActiveContainerMap map[string][]*libpod.Container) (systemDfDiskUsage, error) { 233 var ( 234 numberOfImages int 235 sumSize uint64 236 numberOfActiveImages int 237 unreclaimableSize uint64 238 imageDiskUsage systemDfDiskUsage 239 reclaimableStr string 240 ) 241 242 imgUniqueSizeMap, err := imageUniqueSize(ctx, images) 243 if err != nil { 244 return imageDiskUsage, errors.Wrapf(err, "error getting unique size of images") 245 } 246 247 for _, img := range images { 248 249 unreclaimableSize += imageUsedSize(img, imgUniqueSizeMap, imageUsedbyCintainerMap, imageUsedbyActiveContainerMap) 250 251 isParent, err := img.IsParent(ctx) 252 if err != nil { 253 return imageDiskUsage, err 254 } 255 parent, err := img.GetParent(ctx) 256 if err != nil { 257 return imageDiskUsage, errors.Wrapf(err, "error getting parent of image %s", img.ID()) 258 } 259 if isParent && parent != nil { 260 continue 261 } 262 numberOfImages++ 263 if _, isActive := imageUsedbyCintainerMap[img.ID()]; isActive { 264 numberOfActiveImages++ 265 } 266 267 if !isParent { 268 size, err := img.Size(ctx) 269 if err != nil { 270 return imageDiskUsage, errors.Wrapf(err, "error getting disk usage of image %s", img.ID()) 271 } 272 sumSize += *size 273 } 274 275 } 276 sumSizeStr := units.HumanSizeWithPrecision(float64(sumSize), 3) 277 reclaimable := sumSize - unreclaimableSize 278 if sumSize != 0 { 279 reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 100*reclaimable/sumSize) 280 } else { 281 reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 0) 282 } 283 imageDiskUsage = systemDfDiskUsage{ 284 Type: "Images", 285 Total: numberOfImages, 286 Active: numberOfActiveImages, 287 Size: sumSizeStr, 288 Reclaimable: reclaimableStr, 289 } 290 return imageDiskUsage, nil 291 } 292 293 func imageUsedSize(img *image.Image, imgUniqueSizeMap map[string]uint64, imageUsedbyCintainerMap map[string][]*libpod.Container, imageUsedbyActiveContainerMap map[string][]*libpod.Container) uint64 { 294 var usedSize uint64 295 imgUnique := imgUniqueSizeMap[img.ID()] 296 if _, isCtrActive := imageUsedbyActiveContainerMap[img.ID()]; isCtrActive { 297 return imgUnique 298 } 299 containers := imageUsedbyCintainerMap[img.ID()] 300 for _, ctr := range containers { 301 if len(ctr.UserVolumes()) > 0 { 302 usedSize += imgUnique 303 return usedSize 304 } 305 } 306 return usedSize 307 } 308 309 func imagesUsedbyCtr(containers []*libpod.Container, activeContainers map[string]*libpod.Container) (map[string][]*libpod.Container, map[string][]*libpod.Container, error) { 310 imgCtrMap := make(map[string][]*libpod.Container) 311 imgActiveCtrMap := make(map[string][]*libpod.Container) 312 for _, ctr := range containers { 313 imgID, _ := ctr.Image() 314 imgCtrMap[imgID] = append(imgCtrMap[imgID], ctr) 315 if _, isActive := activeContainers[ctr.ID()]; isActive { 316 imgActiveCtrMap[imgID] = append(imgActiveCtrMap[imgID], ctr) 317 } 318 } 319 return imgCtrMap, imgActiveCtrMap, nil 320 } 321 322 func getContainerDiskUsage(containers []*libpod.Container, activeContainers map[string]*libpod.Container) (systemDfDiskUsage, error) { 323 var ( 324 sumSize int64 325 unreclaimableSize int64 326 reclaimableStr string 327 ) 328 for _, ctr := range containers { 329 size, err := ctr.RWSize() 330 if err != nil { 331 return systemDfDiskUsage{}, errors.Wrapf(err, "error getting size of container %s", ctr.ID()) 332 } 333 sumSize += size 334 } 335 for _, activeCtr := range activeContainers { 336 size, err := activeCtr.RWSize() 337 if err != nil { 338 return systemDfDiskUsage{}, errors.Wrapf(err, "error getting size of active container %s", activeCtr.ID()) 339 } 340 unreclaimableSize += size 341 } 342 if sumSize == 0 { 343 reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(0, 3), 0) 344 } else { 345 reclaimable := sumSize - unreclaimableSize 346 reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 100*reclaimable/sumSize) 347 } 348 containerDiskUsage := systemDfDiskUsage{ 349 Type: "Containers", 350 Total: len(containers), 351 Active: len(activeContainers), 352 Size: units.HumanSizeWithPrecision(float64(sumSize), 3), 353 Reclaimable: reclaimableStr, 354 } 355 return containerDiskUsage, nil 356 } 357 358 func ctrIsActive(ctr *libpod.Container) (bool, error) { 359 state, err := ctr.State() 360 if err != nil { 361 return false, err 362 } 363 return state == define.ContainerStatePaused || state == define.ContainerStateRunning, nil 364 } 365 366 func activeContainers(containers []*libpod.Container) (map[string]*libpod.Container, error) { 367 activeContainers := make(map[string]*libpod.Container) 368 for _, aCtr := range containers { 369 isActive, err := ctrIsActive(aCtr) 370 if err != nil { 371 return nil, err 372 } 373 if isActive { 374 activeContainers[aCtr.ID()] = aCtr 375 } 376 } 377 return activeContainers, nil 378 } 379 380 func getVolumeDiskUsage(volumes []*libpod.Volume, volumeUsedByContainerMap map[string][]*libpod.Container) (systemDfDiskUsage, error) { 381 var ( 382 sumSize int64 383 unreclaimableSize int64 384 reclaimableStr string 385 ) 386 for _, volume := range volumes { 387 size, err := volumeSize(volume) 388 if err != nil { 389 return systemDfDiskUsage{}, errors.Wrapf(err, "error getting size of volime %s", volume.Name()) 390 } 391 sumSize += size 392 if _, exist := volumeUsedByContainerMap[volume.Name()]; exist { 393 unreclaimableSize += size 394 } 395 } 396 reclaimable := sumSize - unreclaimableSize 397 if sumSize != 0 { 398 reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 100*reclaimable/sumSize) 399 } else { 400 reclaimableStr = fmt.Sprintf("%s (%v%%)", units.HumanSizeWithPrecision(float64(reclaimable), 3), 0) 401 } 402 volumesDiskUsage := systemDfDiskUsage{ 403 Type: "Local Volumes", 404 Total: len(volumes), 405 Active: len(volumeUsedByContainerMap), 406 Size: units.HumanSizeWithPrecision(float64(sumSize), 3), 407 Reclaimable: reclaimableStr, 408 } 409 return volumesDiskUsage, nil 410 } 411 412 func volumeUsedByContainer(containers []*libpod.Container) map[string][]*libpod.Container { 413 volumeUsedByContainerMap := make(map[string][]*libpod.Container) 414 for _, ctr := range containers { 415 416 ctrVolumes := ctr.UserVolumes() 417 for _, ctrVolume := range ctrVolumes { 418 volumeUsedByContainerMap[ctrVolume] = append(volumeUsedByContainerMap[ctrVolume], ctr) 419 } 420 } 421 return volumeUsedByContainerMap 422 } 423 424 func volumeSize(volume *libpod.Volume) (int64, error) { 425 var size int64 426 err := filepath.Walk(volume.MountPoint(), func(path string, info os.FileInfo, err error) error { 427 if err == nil && !info.IsDir() { 428 size += info.Size() 429 } 430 return err 431 }) 432 return size, err 433 } 434 435 func getImageVerboseDiskUsage(ctx context.Context, images []*image.Image, imagesUsedbyCtr map[string][]*libpod.Container) ([]imageVerboseDiskUsage, error) { 436 var imagesVerboseDiskUsage []imageVerboseDiskUsage 437 imgUniqueSizeMap, err := imageUniqueSize(ctx, images) 438 if err != nil { 439 return imagesVerboseDiskUsage, errors.Wrapf(err, "error getting unique size of images") 440 } 441 for _, img := range images { 442 isParent, err := img.IsParent(ctx) 443 if err != nil { 444 return imagesVerboseDiskUsage, errors.Wrapf(err, "error checking if %s is a parent images", img.ID()) 445 } 446 parent, err := img.GetParent(ctx) 447 if err != nil { 448 return imagesVerboseDiskUsage, errors.Wrapf(err, "error getting parent of image %s", img.ID()) 449 } 450 if isParent && parent != nil { 451 continue 452 } 453 size, err := img.Size(ctx) 454 if err != nil { 455 return imagesVerboseDiskUsage, errors.Wrapf(err, "error getting size of image %s", img.ID()) 456 } 457 numberOfContainers := 0 458 if ctrs, exist := imagesUsedbyCtr[img.ID()]; exist { 459 numberOfContainers = len(ctrs) 460 } 461 var repo string 462 var tag string 463 var repotags []string 464 if len(img.Names()) != 0 { 465 repotags = []string{img.Names()[0]} 466 } 467 repopairs, err := image.ReposToMap(repotags) 468 if err != nil { 469 logrus.Errorf("error finding tag/digest for %s", img.ID()) 470 } 471 for reponame, tags := range repopairs { 472 for _, tagname := range tags { 473 repo = reponame 474 tag = tagname 475 } 476 } 477 478 imageVerbosedf := imageVerboseDiskUsage{ 479 Repository: repo, 480 Tag: tag, 481 ImageID: shortID(img.ID()), 482 Created: fmt.Sprintf("%s ago", units.HumanDuration(time.Since((img.Created().Local())))), 483 Size: units.HumanSizeWithPrecision(float64(*size), 3), 484 SharedSize: units.HumanSizeWithPrecision(float64(*size-imgUniqueSizeMap[img.ID()]), 3), 485 UniqueSize: units.HumanSizeWithPrecision(float64(imgUniqueSizeMap[img.ID()]), 3), 486 Containers: numberOfContainers, 487 } 488 imagesVerboseDiskUsage = append(imagesVerboseDiskUsage, imageVerbosedf) 489 } 490 return imagesVerboseDiskUsage, nil 491 } 492 493 func getContainerVerboseDiskUsage(containers []*libpod.Container) (containersVerboseDiskUsage []containerVerboseDiskUsage, err error) { 494 for _, ctr := range containers { 495 imgID, _ := ctr.Image() 496 size, err := ctr.RWSize() 497 if err != nil { 498 return containersVerboseDiskUsage, errors.Wrapf(err, "error getting size of container %s", ctr.ID()) 499 } 500 state, err := ctr.State() 501 if err != nil { 502 return containersVerboseDiskUsage, errors.Wrapf(err, "error getting the state of container %s", ctr.ID()) 503 } 504 505 ctrVerboseData := containerVerboseDiskUsage{ 506 ContainerID: shortID(ctr.ID()), 507 Image: shortImageID(imgID), 508 Command: strings.Join(ctr.Command(), " "), 509 LocalVolumes: len(ctr.UserVolumes()), 510 Size: units.HumanSizeWithPrecision(float64(size), 3), 511 Created: fmt.Sprintf("%s ago", units.HumanDuration(time.Since(ctr.CreatedTime().Local()))), 512 Status: state.String(), 513 Names: ctr.Name(), 514 } 515 containersVerboseDiskUsage = append(containersVerboseDiskUsage, ctrVerboseData) 516 517 } 518 return containersVerboseDiskUsage, nil 519 } 520 521 func getVolumeVerboseDiskUsage(volumes []*libpod.Volume, volumeUsedByContainerMap map[string][]*libpod.Container) (volumesVerboseDiskUsage []volumeVerboseDiskUsage, err error) { 522 for _, vol := range volumes { 523 volSize, err := volumeSize(vol) 524 if err != nil { 525 return volumesVerboseDiskUsage, errors.Wrapf(err, "error getting size of volume %s", vol.Name()) 526 } 527 links := 0 528 if linkCtr, exist := volumeUsedByContainerMap[vol.Name()]; exist { 529 links = len(linkCtr) 530 } 531 volumeVerboseData := volumeVerboseDiskUsage{ 532 VolumeName: vol.Name(), 533 Links: links, 534 Size: units.HumanSizeWithPrecision(float64(volSize), 3), 535 } 536 volumesVerboseDiskUsage = append(volumesVerboseDiskUsage, volumeVerboseData) 537 } 538 return volumesVerboseDiskUsage, nil 539 } 540 541 func imagesVerboseOutput(ctx context.Context, metaData dfMetaData) error { 542 var imageVerboseHeader = map[string]string{ 543 "Repository": "REPOSITORY", 544 "Tag": "TAG", 545 "ImageID": "IMAGE ID", 546 "Created": "CREATED", 547 "Size": "SIZE", 548 "SharedSize": "SHARED SIZE", 549 "UniqueSize": "UNIQUE SIZE", 550 "Containers": "CONTAINERS", 551 } 552 imagesVerboseDiskUsage, err := getImageVerboseDiskUsage(ctx, metaData.images, metaData.imagesUsedbyCtrMap) 553 if err != nil { 554 return errors.Wrapf(err, "error getting verbose output of images") 555 } 556 if _, err := os.Stderr.WriteString("Images space usage:\n\n"); err != nil { 557 return err 558 } 559 out := formats.StdoutTemplateArray{Output: systemDfImageVerboseDiskUsageToGeneric(imagesVerboseDiskUsage), Template: imageVerboseFormat, Fields: imageVerboseHeader} 560 return out.Out() 561 } 562 563 func containersVerboseOutput(ctx context.Context, metaData dfMetaData) error { 564 var containerVerboseHeader = map[string]string{ 565 "ContainerID": "CONTAINER ID ", 566 "Image": "IMAGE", 567 "Command": "COMMAND", 568 "LocalVolumes": "LOCAL VOLUMES", 569 "Size": "SIZE", 570 "Created": "CREATED", 571 "Status": "STATUS", 572 "Names": "NAMES", 573 } 574 containersVerboseDiskUsage, err := getContainerVerboseDiskUsage(metaData.containers) 575 if err != nil { 576 return errors.Wrapf(err, "error getting verbose output of containers") 577 } 578 if _, err := os.Stderr.WriteString("\nContainers space usage:\n\n"); err != nil { 579 return err 580 } 581 out := formats.StdoutTemplateArray{Output: systemDfContainerVerboseDiskUsageToGeneric(containersVerboseDiskUsage), Template: containerVerboseFormat, Fields: containerVerboseHeader} 582 return out.Out() 583 584 } 585 586 func volumesVerboseOutput(ctx context.Context, metaData dfMetaData) error { 587 var volumeVerboseHeader = map[string]string{ 588 "VolumeName": "VOLUME NAME", 589 "Links": "LINKS", 590 "Size": "SIZE", 591 } 592 volumesVerboseDiskUsage, err := getVolumeVerboseDiskUsage(metaData.volumes, metaData.volumeUsedByContainerMap) 593 if err != nil { 594 return errors.Wrapf(err, "error getting verbose output of volumes") 595 } 596 if _, err := os.Stderr.WriteString("\nLocal Volumes space usage:\n\n"); err != nil { 597 return err 598 } 599 out := formats.StdoutTemplateArray{Output: systemDfVolumeVerboseDiskUsageToGeneric(volumesVerboseDiskUsage), Template: volumeVerboseFormat, Fields: volumeVerboseHeader} 600 return out.Out() 601 } 602 603 func verboseOutput(ctx context.Context, metaData dfMetaData) error { 604 if err := imagesVerboseOutput(ctx, metaData); err != nil { 605 return err 606 } 607 if err := containersVerboseOutput(ctx, metaData); err != nil { 608 return err 609 } 610 if err := volumesVerboseOutput(ctx, metaData); err != nil { 611 return err 612 } 613 return nil 614 } 615 616 func systemDfDiskUsageToGeneric(diskUsages []systemDfDiskUsage) (out []interface{}) { 617 for _, usage := range diskUsages { 618 out = append(out, interface{}(usage)) 619 } 620 return out 621 } 622 623 func systemDfImageVerboseDiskUsageToGeneric(diskUsages []imageVerboseDiskUsage) (out []interface{}) { 624 for _, usage := range diskUsages { 625 out = append(out, interface{}(usage)) 626 } 627 return out 628 } 629 630 func systemDfContainerVerboseDiskUsageToGeneric(diskUsages []containerVerboseDiskUsage) (out []interface{}) { 631 for _, usage := range diskUsages { 632 out = append(out, interface{}(usage)) 633 } 634 return out 635 } 636 637 func systemDfVolumeVerboseDiskUsageToGeneric(diskUsages []volumeVerboseDiskUsage) (out []interface{}) { 638 for _, usage := range diskUsages { 639 out = append(out, interface{}(usage)) 640 } 641 return out 642 } 643 644 func shortImageID(id string) string { 645 const imageIDTruncLength int = 4 646 if len(id) > imageIDTruncLength { 647 return id[:imageIDTruncLength] 648 } 649 return id 650 }