Files
moby/client/system_disk_usage.go
Sebastiaan van Stijn e254a9f0f1 client: containerDiskUsageFromLegacyAPI: make switch exhaustive
client/system_disk_usage.go:275:3: missing cases in switch of type container.ContainerState: container.StateCreated, container.StateRemoving, container.StateExited, container.StateDead (exhaustive)
            switch c.State {
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-12-18 17:11:23 +01:00

335 lines
8.1 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
case container.StateCreated, container.StateRemoving, container.StateExited, container.StateDead:
// not active
}
}
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
}