From 32319028e5f4cfd551a8122f8fca4146928392ad Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 7 Nov 2025 13:51:41 +0100 Subject: [PATCH 1/7] daemon/server/router/system: slightly rewrite logic for legacy Rewrite the logic to have a better separation between producing legacy fields, and verbose. We need to preserve / include all items in the response _either_ if a API >= v1.52 client requested "verbose" _or_ if we're about to produce legacy fields. Also switch to using the `httputils.BoolValue` utility; while we lose the error for invalid values (which we probably should have as a utility in `httputils`), it aligns with values accepted for other boolean values. Signed-off-by: Sebastiaan van Stijn --- daemon/server/router/system/system_routes.go | 27 ++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/daemon/server/router/system/system_routes.go b/daemon/server/router/system/system_routes.go index b40d6c84f5..dfb0f5eac9 100644 --- a/daemon/server/router/system/system_routes.go +++ b/daemon/server/router/system/system_routes.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "strconv" "time" "github.com/containerd/log" @@ -165,19 +164,21 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, } } - // To maintain backwards compatibility with older clients, when communicating with API versions prior to 1.52, - // verbose mode is always enabled. For API 1.52 and onwards, if the "verbose" query parameter is not set, - // assume legacy fields should be included. var verbose, legacyFields bool - if v := r.Form.Get("verbose"); versions.GreaterThanOrEqualTo(version, "1.52") && v != "" { - var err error - verbose, err = strconv.ParseBool(v) - if err != nil { - return invalidRequestError{Err: fmt.Errorf("invalid value for verbose: %s", v)} - } + if versions.LessThan(version, "1.52") { + legacyFields = true } else { - // In versions prior to 1.52, legacy fields were always included. - legacyFields, verbose = true, true + verbose = httputils.BoolValue(r, "verbose") + + // For API 1.52, we include both legacy and current fields, as some + // integrations (such as "docker-py") currently use "latest", non-versioned + // API version. + // + // However, if the "verbose" query parameter is set, we can assume + // the client is "API 1.52 aware", and we'll omit the legacy fields. + // + // FIXME(thaJeztah): remove legacy fields entirely for API 1.53 + legacyFields = !verbose } eg, ctx := errgroup.WithContext(ctx) @@ -190,7 +191,7 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, Containers: getContainers, Images: getImages, Volumes: getVolumes, - Verbose: verbose, + Verbose: verbose || legacyFields, }) return err }) From 04de58453103b2f3b641d835d1aca6f754558f91 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 7 Nov 2025 14:05:27 +0100 Subject: [PATCH 2/7] daemon/server/router/system: use shorter names and comments Signed-off-by: Sebastiaan van Stijn --- daemon/server/router/system/system_routes.go | 50 ++++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/daemon/server/router/system/system_routes.go b/daemon/server/router/system/system_routes.go index dfb0f5eac9..9aad8ca3f6 100644 --- a/daemon/server/router/system/system_routes.go +++ b/daemon/server/router/system/system_routes.go @@ -183,11 +183,11 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, eg, ctx := errgroup.WithContext(ctx) - var systemDiskUsage *backend.DiskUsage + var diskUsage *backend.DiskUsage if getContainers || getImages || getVolumes { eg.Go(func() error { var err error - systemDiskUsage, err = s.backend.SystemDiskUsage(ctx, backend.DiskUsageOptions{ + diskUsage, err = s.backend.SystemDiskUsage(ctx, backend.DiskUsageOptions{ Containers: getContainers, Images: getImages, Volumes: getVolumes, @@ -214,47 +214,47 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, } var v system.DiskUsage - if systemDiskUsage != nil && systemDiskUsage.Images != nil { + if diskUsage != nil && diskUsage.Images != nil { v.ImageUsage = &image.DiskUsage{ - ActiveCount: systemDiskUsage.Images.ActiveCount, - Reclaimable: systemDiskUsage.Images.Reclaimable, - TotalCount: systemDiskUsage.Images.TotalCount, - TotalSize: systemDiskUsage.Images.TotalSize, + ActiveCount: diskUsage.Images.ActiveCount, + Reclaimable: diskUsage.Images.Reclaimable, + TotalCount: diskUsage.Images.TotalCount, + TotalSize: diskUsage.Images.TotalSize, } if legacyFields { - v.LayersSize = systemDiskUsage.Images.TotalSize //nolint: staticcheck,SA1019: v.LayersSize is deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.TotalSize] instead. - v.Images = nonNilSlice(systemDiskUsage.Images.Items) //nolint: staticcheck,SA1019: v.Images is deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.Items] instead. + v.LayersSize = diskUsage.Images.TotalSize //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + v.Images = nonNilSlice(diskUsage.Images.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. } else if verbose { - v.ImageUsage.Items = systemDiskUsage.Images.Items + v.ImageUsage.Items = diskUsage.Images.Items } } - if systemDiskUsage != nil && systemDiskUsage.Containers != nil { + if diskUsage != nil && diskUsage.Containers != nil { v.ContainerUsage = &container.DiskUsage{ - ActiveCount: systemDiskUsage.Containers.ActiveCount, - Reclaimable: systemDiskUsage.Containers.Reclaimable, - TotalCount: systemDiskUsage.Containers.TotalCount, - TotalSize: systemDiskUsage.Containers.TotalSize, + ActiveCount: diskUsage.Containers.ActiveCount, + Reclaimable: diskUsage.Containers.Reclaimable, + TotalCount: diskUsage.Containers.TotalCount, + TotalSize: diskUsage.Containers.TotalSize, } if legacyFields { - v.Containers = nonNilSlice(systemDiskUsage.Containers.Items) //nolint: staticcheck,SA1019: v.Containers is deprecated: kept to maintain backwards compatibility with API < v1.52, use [ContainersDiskUsage.Items] instead. + v.Containers = nonNilSlice(diskUsage.Containers.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. } else if verbose { - v.ContainerUsage.Items = systemDiskUsage.Containers.Items + v.ContainerUsage.Items = diskUsage.Containers.Items } } - if systemDiskUsage != nil && systemDiskUsage.Volumes != nil { + if diskUsage != nil && diskUsage.Volumes != nil { v.VolumeUsage = &volume.DiskUsage{ - ActiveCount: systemDiskUsage.Volumes.ActiveCount, - TotalSize: systemDiskUsage.Volumes.TotalSize, - Reclaimable: systemDiskUsage.Volumes.Reclaimable, - TotalCount: systemDiskUsage.Volumes.TotalCount, + ActiveCount: diskUsage.Volumes.ActiveCount, + TotalSize: diskUsage.Volumes.TotalSize, + Reclaimable: diskUsage.Volumes.Reclaimable, + TotalCount: diskUsage.Volumes.TotalCount, } if legacyFields { - v.Volumes = nonNilSlice(systemDiskUsage.Volumes.Items) //nolint: staticcheck,SA1019: v.Volumes is deprecated: kept to maintain backwards compatibility with API < v1.52, use [VolumesDiskUsage.Items] instead. + v.Volumes = nonNilSlice(diskUsage.Volumes.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. } else if verbose { - v.VolumeUsage.Items = systemDiskUsage.Volumes.Items + v.VolumeUsage.Items = diskUsage.Volumes.Items } } if getBuildCache { @@ -283,7 +283,7 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, v.BuildCacheUsage.Reclaimable = reclaimable if legacyFields { - v.BuildCache = nonNilSlice(buildCache) //nolint: staticcheck,SA1019: v.BuildCache is deprecated: kept to maintain backwards compatibility with API < v1.52, use [BuildCacheDiskUsage.Items] instead. + v.BuildCache = nonNilSlice(buildCache) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. } else if verbose { v.BuildCacheUsage.Items = buildCache } From f1a33876334dd5164d6242c028df9d0e26e09792 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 7 Nov 2025 12:27:43 +0100 Subject: [PATCH 3/7] daemon/server/backend: align DiskUsage types with api Make the "per-object" types aliases for the API type, and remove the BuildCacheDiskUsage type, as it's not currently used. Signed-off-by: Sebastiaan van Stijn --- daemon/server/backend/disk_usage.go | 35 ++++------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/daemon/server/backend/disk_usage.go b/daemon/server/backend/disk_usage.go index 448e0a49b0..3805cdfde3 100644 --- a/daemon/server/backend/disk_usage.go +++ b/daemon/server/backend/disk_usage.go @@ -28,41 +28,14 @@ type DiskUsage struct { Images *ImageDiskUsage Containers *ContainerDiskUsage Volumes *VolumeDiskUsage - BuildCache *BuildCacheDiskUsage -} - -// BuildCacheDiskUsage contains disk usage for the build cache. -type BuildCacheDiskUsage struct { - ActiveCount int64 - TotalCount int64 - TotalSize int64 - Reclaimable int64 - Items []build.CacheRecord + BuildCache *build.DiskUsage } // ContainerDiskUsage contains disk usage for containers. -type ContainerDiskUsage struct { - ActiveCount int64 - TotalCount int64 - TotalSize int64 - Reclaimable int64 - Items []container.Summary -} +type ContainerDiskUsage = container.DiskUsage // ImageDiskUsage contains disk usage for images. -type ImageDiskUsage struct { - ActiveCount int64 - TotalCount int64 - TotalSize int64 - Reclaimable int64 - Items []image.Summary -} +type ImageDiskUsage = image.DiskUsage // VolumeDiskUsage contains disk usage for volumes. -type VolumeDiskUsage struct { - ActiveCount int64 - TotalCount int64 - TotalSize int64 - Reclaimable int64 - Items []volume.Volume -} +type VolumeDiskUsage = volume.DiskUsage From 0dcb1fe34409a903433db5d199cdd6f7716a6201 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 7 Nov 2025 13:01:33 +0100 Subject: [PATCH 4/7] daemon: align build.DiskUsage() with other disk-usages Move calculation of the data to the builder backend, to align with the other type of objects. This also allows us to skip the verbose data if it's not used. Signed-off-by: Sebastiaan van Stijn --- daemon/internal/builder-next/builder.go | 21 ++++++++--- daemon/server/buildbackend/build.go | 7 ++++ daemon/server/router/system/backend.go | 4 +-- daemon/server/router/system/system_routes.go | 38 +++++++------------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/daemon/internal/builder-next/builder.go b/daemon/internal/builder-next/builder.go index 550753faee..5a50b33513 100644 --- a/daemon/internal/builder-next/builder.go +++ b/daemon/internal/builder-next/builder.go @@ -150,15 +150,26 @@ func (b *Builder) Cancel(ctx context.Context, id string) error { } // DiskUsage returns a report about space used by build cache -func (b *Builder) DiskUsage(ctx context.Context) ([]build.CacheRecord, error) { +func (b *Builder) DiskUsage(ctx context.Context, options buildbackend.DiskUsageOptions) (*buildbackend.DiskUsage, error) { duResp, err := b.controller.DiskUsage(ctx, &controlapi.DiskUsageRequest{}) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting build cache usage: %w", err) } - var items []build.CacheRecord + var usage buildbackend.DiskUsage for _, r := range duResp.Record { - items = append(items, build.CacheRecord{ + usage.TotalCount++ + usage.TotalSize += r.Size + if r.InUse { + usage.ActiveCount++ + } + if !r.InUse && !r.Shared { + usage.Reclaimable += r.Size + } + if !options.Verbose { + continue + } + usage.Items = append(usage.Items, build.CacheRecord{ ID: r.ID, Parents: r.Parents, Type: r.RecordType, @@ -182,7 +193,7 @@ func (b *Builder) DiskUsage(ctx context.Context) ([]build.CacheRecord, error) { UsageCount: int(r.UsageCount), }) } - return items, nil + return &usage, nil } // Prune clears all reclaimable build cache. diff --git a/daemon/server/buildbackend/build.go b/daemon/server/buildbackend/build.go index bc5f9b27ee..a3545c7455 100644 --- a/daemon/server/buildbackend/build.go +++ b/daemon/server/buildbackend/build.go @@ -11,6 +11,13 @@ import ( "github.com/moby/moby/v2/daemon/internal/filters" ) +type DiskUsageOptions struct { + Verbose bool +} + +// DiskUsage contains disk usage for the build cache. +type DiskUsage = build.DiskUsage + type CachePruneOptions struct { All bool ReservedSpace int64 diff --git a/daemon/server/router/system/backend.go b/daemon/server/router/system/backend.go index 3d1c12f1f9..a9e289f01e 100644 --- a/daemon/server/router/system/backend.go +++ b/daemon/server/router/system/backend.go @@ -4,13 +4,13 @@ import ( "context" "time" - "github.com/moby/moby/api/types/build" "github.com/moby/moby/api/types/events" "github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/api/types/system" "github.com/moby/moby/v2/daemon/internal/filters" "github.com/moby/moby/v2/daemon/server/backend" + "github.com/moby/moby/v2/daemon/server/buildbackend" ) // Backend is the methods that need to be implemented to provide @@ -32,7 +32,7 @@ type ClusterBackend interface { // BuildBackend provides build specific system information. type BuildBackend interface { - DiskUsage(context.Context) ([]build.CacheRecord, error) + DiskUsage(context.Context, buildbackend.DiskUsageOptions) (*buildbackend.DiskUsage, error) } // StatusProvider provides methods to get the swarm status of the current node. diff --git a/daemon/server/router/system/system_routes.go b/daemon/server/router/system/system_routes.go index 9aad8ca3f6..1a2a3c0a55 100644 --- a/daemon/server/router/system/system_routes.go +++ b/daemon/server/router/system/system_routes.go @@ -24,6 +24,7 @@ import ( "github.com/moby/moby/v2/daemon/internal/timestamp" "github.com/moby/moby/v2/daemon/internal/versions" "github.com/moby/moby/v2/daemon/server/backend" + "github.com/moby/moby/v2/daemon/server/buildbackend" "github.com/moby/moby/v2/daemon/server/httputils" "github.com/moby/moby/v2/daemon/server/router/build" "github.com/moby/moby/v2/pkg/ioutils" @@ -197,11 +198,13 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, }) } - var buildCache []buildtypes.CacheRecord + var buildCacheUsage *buildbackend.DiskUsage if getBuildCache { eg.Go(func() error { var err error - buildCache, err = s.builder.DiskUsage(ctx) + buildCacheUsage, err = s.builder.DiskUsage(ctx, buildbackend.DiskUsageOptions{ + Verbose: verbose || legacyFields, + }) if err != nil { return errors.Wrap(err, "error getting build cache usage") } @@ -257,35 +260,18 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, v.VolumeUsage.Items = diskUsage.Volumes.Items } } - if getBuildCache { + if buildCacheUsage != nil { v.BuildCacheUsage = &buildtypes.DiskUsage{ - TotalCount: int64(len(buildCache)), + ActiveCount: buildCacheUsage.ActiveCount, + Reclaimable: buildCacheUsage.Reclaimable, + TotalCount: buildCacheUsage.TotalCount, + TotalSize: buildCacheUsage.TotalSize, } - activeCount := v.BuildCacheUsage.TotalCount - var totalSize, reclaimable int64 - - for _, b := range buildCache { - if versions.LessThan(version, "1.42") { - totalSize += b.Size - } - - if !b.InUse { - activeCount-- - } - if !b.InUse && !b.Shared { - reclaimable += b.Size - } - } - - v.BuildCacheUsage.ActiveCount = activeCount - v.BuildCacheUsage.TotalSize = totalSize - v.BuildCacheUsage.Reclaimable = reclaimable - if legacyFields { - v.BuildCache = nonNilSlice(buildCache) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + v.BuildCache = nonNilSlice(buildCacheUsage.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. } else if verbose { - v.BuildCacheUsage.Items = buildCache + v.BuildCacheUsage.Items = buildCacheUsage.Items } } return httputils.WriteJSON(w, http.StatusOK, v) From 20870f13c2992400e4716e63370bdbd1ad659ea5 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 7 Nov 2025 14:43:32 +0100 Subject: [PATCH 5/7] daemon: remove intermediate vars when collecting diskUsage Set values directly on the DiskUsage objects instead of using some intermediate vars, some of which were named slightly confusing due to them being used both for "totalSize" and "reclaimableSize". Signed-off-by: Sebastiaan van Stijn --- daemon/disk_usage.go | 52 ++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/daemon/disk_usage.go b/daemon/disk_usage.go index c1d27d1882..543b2aec82 100644 --- a/daemon/disk_usage.go +++ b/daemon/disk_usage.go @@ -26,30 +26,27 @@ func (daemon *Daemon) containerDiskUsage(ctx context.Context, verbose bool) (*ba return nil, fmt.Errorf("failed to retrieve container list: %v", err) } - // Remove image manifest descriptor from the result as it should not be included. - // https://github.com/moby/moby/pull/49407#discussion_r1954396666 - for _, c := range containers { - c.ImageManifestDescriptor = nil - } - isActive := func(ctr *container.Summary) bool { return ctr.State == container.StateRunning || ctr.State == container.StatePaused || ctr.State == container.StateRestarting } - activeCount := int64(len(containers)) - - du := &backend.ContainerDiskUsage{TotalCount: activeCount} + du := &backend.ContainerDiskUsage{ + ActiveCount: int64(len(containers)), + TotalCount: int64(len(containers)), + } for _, ctr := range containers { du.TotalSize += ctr.SizeRw if !isActive(ctr) { du.Reclaimable += ctr.SizeRw - activeCount-- + du.ActiveCount-- } - } - du.ActiveCount = activeCount + // Remove image manifest descriptor from the result as it should not be included. + // https://github.com/moby/moby/pull/49407#discussion_r1954396666 + ctr.ImageManifestDescriptor = nil + } if verbose { du.Items = sliceutil.Deref(containers) @@ -73,29 +70,29 @@ func (daemon *Daemon) imageDiskUsage(ctx context.Context, verbose bool) (*backen return nil, errors.Wrap(err, "failed to retrieve image list") } - reclaimable, _, err := daemon.usageLayer.Do(ctx, struct{}{}, func(ctx context.Context) (int64, error) { + totalSize, _, err := daemon.usageLayer.Do(ctx, struct{}{}, func(ctx context.Context) (int64, error) { return daemon.imageService.ImageDiskUsage(ctx) }) if err != nil { return nil, errors.Wrap(err, "failed to calculate image disk usage") } - activeCount := int64(len(images)) - - du := &backend.ImageDiskUsage{TotalCount: activeCount, TotalSize: reclaimable} + du := &backend.ImageDiskUsage{ + ActiveCount: int64(len(images)), + Reclaimable: totalSize, + TotalCount: int64(len(images)), + TotalSize: totalSize, + } for _, i := range images { if i.Containers == 0 { - activeCount-- + du.ActiveCount-- if i.Size == -1 || i.SharedSize == -1 { continue } - reclaimable -= i.Size - i.SharedSize + du.Reclaimable -= i.Size - i.SharedSize } } - du.Reclaimable = reclaimable - du.ActiveCount = activeCount - if verbose { du.Items = sliceutil.Deref(images) } @@ -115,21 +112,20 @@ func (daemon *Daemon) localVolumesSize(ctx context.Context, verbose bool) (*back return nil, err } - activeCount := int64(len(volumes)) - - du := &backend.VolumeDiskUsage{TotalCount: activeCount} + du := &backend.VolumeDiskUsage{ + ActiveCount: int64(len(volumes)), + TotalCount: int64(len(volumes)), + } for _, v := range volumes { if v.UsageData.Size != -1 { + du.TotalSize += v.UsageData.Size if v.UsageData.RefCount == 0 { du.Reclaimable += v.UsageData.Size - activeCount-- + du.ActiveCount-- } - du.TotalSize += v.UsageData.Size } } - du.ActiveCount = activeCount - if verbose { du.Items = sliceutil.Deref(volumes) } From f5e319c95003fe73832c2da1d97f424a882ba0b6 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 6 Nov 2025 23:02:24 +0100 Subject: [PATCH 6/7] daemon/server/router/system: use early return for disk-usage Use early return for legacy response. When using API < v1.52, we'd never return the new fields, so we can return early, and produce the legacy-fields only. Signed-off-by: Sebastiaan van Stijn --- daemon/server/router/system/system_routes.go | 47 ++++++++++++-------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/daemon/server/router/system/system_routes.go b/daemon/server/router/system/system_routes.go index 1a2a3c0a55..03753b8e11 100644 --- a/daemon/server/router/system/system_routes.go +++ b/daemon/server/router/system/system_routes.go @@ -216,7 +216,31 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, return err } - var v system.DiskUsage + var legacy system.LegacyDiskUsage + if legacyFields { + if diskUsage != nil { + if diskUsage.Images != nil { + legacy.LayersSize = diskUsage.Images.TotalSize //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + legacy.Images = nonNilSlice(diskUsage.Images.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + } + if diskUsage.Containers != nil { + legacy.Containers = nonNilSlice(diskUsage.Containers.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + } + if diskUsage.Volumes != nil { + legacy.Volumes = nonNilSlice(diskUsage.Volumes.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + } + } + if buildCacheUsage != nil && buildCacheUsage.Items != nil { + legacy.BuildCache = nonNilSlice(buildCacheUsage.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + } + } + if versions.LessThan(version, "1.52") { + return httputils.WriteJSON(w, http.StatusOK, legacy) + } + + v := system.DiskUsage{ + LegacyDiskUsage: legacy, + } if diskUsage != nil && diskUsage.Images != nil { v.ImageUsage = &image.DiskUsage{ ActiveCount: diskUsage.Images.ActiveCount, @@ -224,11 +248,7 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, TotalCount: diskUsage.Images.TotalCount, TotalSize: diskUsage.Images.TotalSize, } - - if legacyFields { - v.LayersSize = diskUsage.Images.TotalSize //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - v.Images = nonNilSlice(diskUsage.Images.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - } else if verbose { + if verbose { v.ImageUsage.Items = diskUsage.Images.Items } } @@ -239,10 +259,7 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, TotalCount: diskUsage.Containers.TotalCount, TotalSize: diskUsage.Containers.TotalSize, } - - if legacyFields { - v.Containers = nonNilSlice(diskUsage.Containers.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - } else if verbose { + if verbose { v.ContainerUsage.Items = diskUsage.Containers.Items } } @@ -253,10 +270,7 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, Reclaimable: diskUsage.Volumes.Reclaimable, TotalCount: diskUsage.Volumes.TotalCount, } - - if legacyFields { - v.Volumes = nonNilSlice(diskUsage.Volumes.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - } else if verbose { + if verbose { v.VolumeUsage.Items = diskUsage.Volumes.Items } } @@ -267,10 +281,7 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, TotalCount: buildCacheUsage.TotalCount, TotalSize: buildCacheUsage.TotalSize, } - - if legacyFields { - v.BuildCache = nonNilSlice(buildCacheUsage.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - } else if verbose { + if verbose { v.BuildCacheUsage.Items = buildCacheUsage.Items } } From 71bcd22d6df1929741051110a34fc3a9f9e8bb31 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 7 Nov 2025 14:46:13 +0100 Subject: [PATCH 7/7] daemon/server/router/system: simplify constructing response Now that we separated the legacy response from non-legacy responses, we can consume the data produced by the backend as-is; the backend takes care of omitting "verbose" data (leaving the `Items` slices empty), and with an early return for the legacy responses, we won't end up with returning _both_ responses on API < v1.52, but (TBD) still return both responses for API v1.52. Signed-off-by: Sebastiaan van Stijn --- daemon/server/router/system/system_routes.go | 93 ++++++-------------- 1 file changed, 25 insertions(+), 68 deletions(-) diff --git a/daemon/server/router/system/system_routes.go b/daemon/server/router/system/system_routes.go index 03753b8e11..b0945c7243 100644 --- a/daemon/server/router/system/system_routes.go +++ b/daemon/server/router/system/system_routes.go @@ -11,14 +11,10 @@ import ( "github.com/golang/gddo/httputil" "github.com/moby/moby/api/pkg/authconfig" "github.com/moby/moby/api/types" - buildtypes "github.com/moby/moby/api/types/build" - "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/events" - "github.com/moby/moby/api/types/image" "github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/api/types/system" - "github.com/moby/moby/api/types/volume" "github.com/moby/moby/v2/daemon/internal/compat" "github.com/moby/moby/v2/daemon/internal/filters" "github.com/moby/moby/v2/daemon/internal/timestamp" @@ -184,17 +180,20 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, eg, ctx := errgroup.WithContext(ctx) - var diskUsage *backend.DiskUsage + diskUsage := &backend.DiskUsage{} if getContainers || getImages || getVolumes { eg.Go(func() error { - var err error - diskUsage, err = s.backend.SystemDiskUsage(ctx, backend.DiskUsageOptions{ + du, err := s.backend.SystemDiskUsage(ctx, backend.DiskUsageOptions{ Containers: getContainers, Images: getImages, Volumes: getVolumes, Verbose: verbose || legacyFields, }) - return err + if err != nil { + return err + } + diskUsage = du + return nil }) } @@ -215,77 +214,35 @@ func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, if err := eg.Wait(); err != nil { return err } + diskUsage.BuildCache = buildCacheUsage var legacy system.LegacyDiskUsage if legacyFields { - if diskUsage != nil { - if diskUsage.Images != nil { - legacy.LayersSize = diskUsage.Images.TotalSize //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - legacy.Images = nonNilSlice(diskUsage.Images.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - } - if diskUsage.Containers != nil { - legacy.Containers = nonNilSlice(diskUsage.Containers.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - } - if diskUsage.Volumes != nil { - legacy.Volumes = nonNilSlice(diskUsage.Volumes.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. - } + if diskUsage.Images != nil { + legacy.LayersSize = diskUsage.Images.TotalSize //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + legacy.Images = nonNilSlice(diskUsage.Images.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. } - if buildCacheUsage != nil && buildCacheUsage.Items != nil { - legacy.BuildCache = nonNilSlice(buildCacheUsage.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + if diskUsage.Containers != nil { + legacy.Containers = nonNilSlice(diskUsage.Containers.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + } + if diskUsage.Volumes != nil { + legacy.Volumes = nonNilSlice(diskUsage.Volumes.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. + } + if diskUsage.BuildCache != nil { + legacy.BuildCache = nonNilSlice(diskUsage.BuildCache.Items) //nolint: staticcheck,SA1019: kept to maintain backwards compatibility with API < v1.52. } } if versions.LessThan(version, "1.52") { return httputils.WriteJSON(w, http.StatusOK, legacy) } - v := system.DiskUsage{ + return httputils.WriteJSON(w, http.StatusOK, &system.DiskUsage{ LegacyDiskUsage: legacy, - } - if diskUsage != nil && diskUsage.Images != nil { - v.ImageUsage = &image.DiskUsage{ - ActiveCount: diskUsage.Images.ActiveCount, - Reclaimable: diskUsage.Images.Reclaimable, - TotalCount: diskUsage.Images.TotalCount, - TotalSize: diskUsage.Images.TotalSize, - } - if verbose { - v.ImageUsage.Items = diskUsage.Images.Items - } - } - if diskUsage != nil && diskUsage.Containers != nil { - v.ContainerUsage = &container.DiskUsage{ - ActiveCount: diskUsage.Containers.ActiveCount, - Reclaimable: diskUsage.Containers.Reclaimable, - TotalCount: diskUsage.Containers.TotalCount, - TotalSize: diskUsage.Containers.TotalSize, - } - if verbose { - v.ContainerUsage.Items = diskUsage.Containers.Items - } - } - if diskUsage != nil && diskUsage.Volumes != nil { - v.VolumeUsage = &volume.DiskUsage{ - ActiveCount: diskUsage.Volumes.ActiveCount, - TotalSize: diskUsage.Volumes.TotalSize, - Reclaimable: diskUsage.Volumes.Reclaimable, - TotalCount: diskUsage.Volumes.TotalCount, - } - if verbose { - v.VolumeUsage.Items = diskUsage.Volumes.Items - } - } - if buildCacheUsage != nil { - v.BuildCacheUsage = &buildtypes.DiskUsage{ - ActiveCount: buildCacheUsage.ActiveCount, - Reclaimable: buildCacheUsage.Reclaimable, - TotalCount: buildCacheUsage.TotalCount, - TotalSize: buildCacheUsage.TotalSize, - } - if verbose { - v.BuildCacheUsage.Items = buildCacheUsage.Items - } - } - return httputils.WriteJSON(w, http.StatusOK, v) + ImageUsage: diskUsage.Images, + ContainerUsage: diskUsage.Containers, + VolumeUsage: diskUsage.Volumes, + BuildCacheUsage: diskUsage.BuildCache, + }) } // nonNilSlice is used for the legacy fields, which are either omitted