Files
moby/client/system_disk_usage.go
Sebastiaan van Stijn ed428234bd api: remove / internalize LegacyDiskUsage
These fields have been removed from the API specification, and the struct
was only needed to produce legacy responses (server), or to unmarshal
legacy responses in the client.

As the API module only provides API definitions for the current API version,
we should remove these legacy structs, and keep them internal to the daemon
and client.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 20:16:03 +01:00

333 lines
8.0 KiB
Go

package client
import (
"context"
"encoding/json"
"fmt"
"net/url"
"slices"
"github.com/moby/moby/api/types/build"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/image"
"github.com/moby/moby/api/types/system"
"github.com/moby/moby/api/types/volume"
"github.com/moby/moby/client/pkg/versions"
)
// DiskUsageOptions holds parameters for [Client.DiskUsage] operations.
type DiskUsageOptions struct {
// Containers controls whether container disk usage should be computed.
Containers bool
// Images controls whether image disk usage should be computed.
Images bool
// BuildCache controls whether build cache disk usage should be computed.
BuildCache bool
// Volumes controls whether volume disk usage should be computed.
Volumes bool
// Verbose enables more detailed disk usage information.
Verbose bool
}
// DiskUsageResult is the result of [Client.DiskUsage] operations.
type DiskUsageResult struct {
// Containers holds container disk usage information.
Containers ContainersDiskUsage
// Images holds image disk usage information.
Images ImagesDiskUsage
// BuildCache holds build cache disk usage information.
BuildCache BuildCacheDiskUsage
// Volumes holds volume disk usage information.
Volumes VolumesDiskUsage
}
// ContainersDiskUsage contains disk usage information for containers.
type ContainersDiskUsage struct {
// ActiveCount is the number of active containers.
ActiveCount int64
// TotalCount is the total number of containers.
TotalCount int64
// Reclaimable is the amount of disk space that can be reclaimed.
Reclaimable int64
// TotalSize is the total disk space used by all containers.
TotalSize int64
// Items holds detailed information about each container.
Items []container.Summary
}
// ImagesDiskUsage contains disk usage information for images.
type ImagesDiskUsage struct {
// ActiveCount is the number of active images.
ActiveCount int64
// TotalCount is the total number of images.
TotalCount int64
// Reclaimable is the amount of disk space that can be reclaimed.
Reclaimable int64
// TotalSize is the total disk space used by all images.
TotalSize int64
// Items holds detailed information about each image.
Items []image.Summary
}
// VolumesDiskUsage contains disk usage information for volumes.
type VolumesDiskUsage struct {
// ActiveCount is the number of active volumes.
ActiveCount int64
// TotalCount is the total number of volumes.
TotalCount int64
// Reclaimable is the amount of disk space that can be reclaimed.
Reclaimable int64
// TotalSize is the total disk space used by all volumes.
TotalSize int64
// Items holds detailed information about each volume.
Items []volume.Volume
}
// BuildCacheDiskUsage contains disk usage information for build cache.
type BuildCacheDiskUsage struct {
// ActiveCount is the number of active build cache records.
ActiveCount int64
// TotalCount is the total number of build cache records.
TotalCount int64
// Reclaimable is the amount of disk space that can be reclaimed.
Reclaimable int64
// TotalSize is the total disk space used by all build cache records.
TotalSize int64
// Items holds detailed information about each build cache record.
Items []build.CacheRecord
}
// DiskUsage requests the current data usage from the daemon.
func (cli *Client) DiskUsage(ctx context.Context, options DiskUsageOptions) (DiskUsageResult, error) {
query := url.Values{}
for _, t := range []struct {
flag bool
sysObj system.DiskUsageObject
}{
{options.Containers, system.ContainerObject},
{options.Images, system.ImageObject},
{options.Volumes, system.VolumeObject},
{options.BuildCache, system.BuildCacheObject},
} {
if t.flag {
query.Add("type", string(t.sysObj))
}
}
if options.Verbose {
query.Set("verbose", "1")
}
resp, err := cli.get(ctx, "/system/df", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return DiskUsageResult{}, err
}
if versions.LessThan(cli.version, "1.52") {
// Generate result from a legacy response.
var du legacyDiskUsage
if err := json.NewDecoder(resp.Body).Decode(&du); err != nil {
return DiskUsageResult{}, fmt.Errorf("retrieving disk usage: %v", err)
}
return diskUsageResultFromLegacyAPI(&du), nil
}
var du system.DiskUsage
if err := json.NewDecoder(resp.Body).Decode(&du); err != nil {
return DiskUsageResult{}, fmt.Errorf("retrieving disk usage: %v", err)
}
var r DiskUsageResult
if idu := du.ImageUsage; idu != nil {
r.Images = ImagesDiskUsage{
ActiveCount: idu.ActiveCount,
Reclaimable: idu.Reclaimable,
TotalCount: idu.TotalCount,
TotalSize: idu.TotalSize,
}
if options.Verbose {
r.Images.Items = slices.Clone(idu.Items)
}
}
if cdu := du.ContainerUsage; cdu != nil {
r.Containers = ContainersDiskUsage{
ActiveCount: cdu.ActiveCount,
Reclaimable: cdu.Reclaimable,
TotalCount: cdu.TotalCount,
TotalSize: cdu.TotalSize,
}
if options.Verbose {
r.Containers.Items = slices.Clone(cdu.Items)
}
}
if bdu := du.BuildCacheUsage; bdu != nil {
r.BuildCache = BuildCacheDiskUsage{
ActiveCount: bdu.ActiveCount,
Reclaimable: bdu.Reclaimable,
TotalCount: bdu.TotalCount,
TotalSize: bdu.TotalSize,
}
if options.Verbose {
r.BuildCache.Items = slices.Clone(bdu.Items)
}
}
if vdu := du.VolumeUsage; vdu != nil {
r.Volumes = VolumesDiskUsage{
ActiveCount: vdu.ActiveCount,
Reclaimable: vdu.Reclaimable,
TotalCount: vdu.TotalCount,
TotalSize: vdu.TotalSize,
}
if options.Verbose {
r.Volumes.Items = slices.Clone(vdu.Items)
}
}
return r, nil
}
// legacyDiskUsage is the response as was used by API < v1.52.
type legacyDiskUsage struct {
LayersSize int64 `json:"LayersSize,omitempty"`
Images []image.Summary `json:"Images,omitzero"`
Containers []container.Summary `json:"Containers,omitzero"`
Volumes []volume.Volume `json:"Volumes,omitzero"`
BuildCache []build.CacheRecord `json:"BuildCache,omitzero"`
}
func diskUsageResultFromLegacyAPI(du *legacyDiskUsage) DiskUsageResult {
return DiskUsageResult{
Images: imageDiskUsageFromLegacyAPI(du),
Containers: containerDiskUsageFromLegacyAPI(du),
BuildCache: buildCacheDiskUsageFromLegacyAPI(du),
Volumes: volumeDiskUsageFromLegacyAPI(du),
}
}
func imageDiskUsageFromLegacyAPI(du *legacyDiskUsage) ImagesDiskUsage {
idu := ImagesDiskUsage{
TotalSize: du.LayersSize,
TotalCount: int64(len(du.Images)),
Items: du.Images,
}
var used int64
for _, i := range idu.Items {
if i.Containers > 0 {
idu.ActiveCount++
if i.Size == -1 || i.SharedSize == -1 {
continue
}
used += (i.Size - i.SharedSize)
}
}
if idu.TotalCount > 0 {
idu.Reclaimable = idu.TotalSize - used
}
return idu
}
func containerDiskUsageFromLegacyAPI(du *legacyDiskUsage) ContainersDiskUsage {
cdu := ContainersDiskUsage{
TotalCount: int64(len(du.Containers)),
Items: du.Containers,
}
var used int64
for _, c := range cdu.Items {
cdu.TotalSize += c.SizeRw
switch c.State {
case container.StateRunning, container.StatePaused, container.StateRestarting:
cdu.ActiveCount++
used += c.SizeRw
}
}
cdu.Reclaimable = cdu.TotalSize - used
return cdu
}
func buildCacheDiskUsageFromLegacyAPI(du *legacyDiskUsage) BuildCacheDiskUsage {
bdu := BuildCacheDiskUsage{
TotalCount: int64(len(du.BuildCache)),
Items: du.BuildCache,
}
var used int64
for _, b := range du.BuildCache {
if !b.Shared {
bdu.TotalSize += b.Size
}
if b.InUse {
bdu.ActiveCount++
if !b.Shared {
used += b.Size
}
}
}
bdu.Reclaimable = bdu.TotalSize - used
return bdu
}
func volumeDiskUsageFromLegacyAPI(du *legacyDiskUsage) VolumesDiskUsage {
vdu := VolumesDiskUsage{
TotalCount: int64(len(du.Volumes)),
Items: du.Volumes,
}
var used int64
for _, v := range vdu.Items {
// Ignore volumes with no usage data
if v.UsageData != nil {
if v.UsageData.RefCount > 0 {
vdu.ActiveCount++
used += v.UsageData.Size
}
if v.UsageData.Size > 0 {
vdu.TotalSize += v.UsageData.Size
}
}
}
vdu.Reclaimable = vdu.TotalSize - used
return vdu
}